Просмотр исходного кода

[FL-1059] T5577 write (#463)

* Api-hal-gpio: extend init functions
* App Lfrfid: separate protocol layer
* App Lfrfid: write EM key scene
* App Lfrfid: syntax fix
SG 4 лет назад
Родитель
Сommit
618ddfcd04
28 измененных файлов с 956 добавлено и 420 удалено
  1. 4 104
      applications/lf-rfid/helpers/decoder-emmarine.cpp
  2. 2 1
      applications/lf-rfid/helpers/decoder-emmarine.h
  3. 4 91
      applications/lf-rfid/helpers/decoder-hid26.cpp
  4. 2 4
      applications/lf-rfid/helpers/decoder-hid26.h
  5. 3 37
      applications/lf-rfid/helpers/encoder-emmarine.cpp
  6. 0 1
      applications/lf-rfid/helpers/encoder-emmarine.h
  7. 3 66
      applications/lf-rfid/helpers/encoder-hid-h10301.cpp
  8. 3 89
      applications/lf-rfid/helpers/encoder-indala-40134.cpp
  9. 0 2
      applications/lf-rfid/helpers/encoder-indala-40134.h
  10. 1 0
      applications/lf-rfid/helpers/key-info.h
  11. 150 0
      applications/lf-rfid/helpers/protocols/protocol-emmarin.cpp
  12. 22 0
      applications/lf-rfid/helpers/protocols/protocol-emmarin.h
  13. 60 0
      applications/lf-rfid/helpers/protocols/protocol-generic.h
  14. 238 0
      applications/lf-rfid/helpers/protocols/protocol-hid-h10301.cpp
  15. 22 0
      applications/lf-rfid/helpers/protocols/protocol-hid-h10301.h
  16. 131 0
      applications/lf-rfid/helpers/protocols/protocol-indala-40134.cpp
  17. 22 0
      applications/lf-rfid/helpers/protocols/protocol-indala-40134.h
  18. 118 0
      applications/lf-rfid/helpers/rfid-writer.cpp
  19. 17 0
      applications/lf-rfid/helpers/rfid-writer.h
  20. 4 0
      applications/lf-rfid/lf-rfid-app.cpp
  21. 5 0
      applications/lf-rfid/lf-rfid-app.h
  22. 9 0
      applications/lf-rfid/scene/lf-rfid-scene-read-normal.cpp
  23. 5 0
      applications/lf-rfid/scene/lf-rfid-scene-start.cpp
  24. 70 0
      applications/lf-rfid/scene/lf-rfid-scene-write.cpp
  25. 14 0
      applications/lf-rfid/scene/lf-rfid-scene-write.h
  26. 27 18
      firmware/targets/f5/api-hal/api-hal-gpio.c
  27. 12 3
      firmware/targets/f5/api-hal/api-hal-gpio.h
  28. 8 4
      firmware/targets/f5/api-hal/api-hal-rfid.c

+ 4 - 104
applications/lf-rfid/helpers/decoder-emmarine.cpp

@@ -20,84 +20,13 @@ void DecoderEMMarine::reset_state() {
         manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
 }
 
-void printEM_raw(uint64_t data) {
-    // header
-    for(uint8_t i = 0; i < 9; i++) {
-        printf("%u ", data & (1LLU << 63) ? 1 : 0);
-        data = data << 1;
-    }
-    printf("\r\n");
-
-    // nibbles
-    for(uint8_t r = 0; r < 11; r++) {
-        printf("        ");
-        uint8_t value = 0;
-        for(uint8_t i = 0; i < 5; i++) {
-            printf("%u ", data & (1LLU << 63) ? 1 : 0);
-            if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0);
-            data = data << 1;
-        }
-        printf("0x%X", value);
-        printf("\r\n");
-    }
-}
-
-void printEM_data(uint64_t data) {
-    printf("EM ");
-
-    // header
-    for(uint8_t i = 0; i < 9; i++) {
-        data = data << 1;
-    }
-
-    // nibbles
-    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
-        uint8_t value = 0;
-        for(uint8_t i = 0; i < 5; i++) {
-            if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0);
-            data = data << 1;
-        }
-        printf("%X", value);
-        if(r % 2) printf(" ");
-    }
-    printf("\r\n");
-}
-
-void copyEM_data(uint64_t data, uint8_t* result, uint8_t result_size) {
-    furi_assert(result_size >= 5);
-    uint8_t result_index = 0;
-
-    // clean result
-    memset(result, 0, result_size);
-
-    // header
-    for(uint8_t i = 0; i < 9; i++) {
-        data = data << 1;
-    }
-
-    // nibbles
-    uint8_t value = 0;
-    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
-        uint8_t nibble = 0;
-        for(uint8_t i = 0; i < 5; i++) {
-            if(i < 4) nibble = (nibble << 1) | (data & (1LLU << 63) ? 1 : 0);
-            data = data << 1;
-        }
-        value = (value << 4) | nibble;
-        if(r % 2) {
-            result[result_index] |= value;
-            result_index++;
-            value = 0;
-        }
-    }
-}
-
 bool DecoderEMMarine::read(uint8_t* data, uint8_t data_size) {
     bool result = false;
 
     if(ready) {
         result = true;
-        copyEM_data(readed_data, data, data_size);
+        em_marine.decode(
+            reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t), data, data_size);
         ready = false;
     }
 
@@ -132,37 +61,8 @@ void DecoderEMMarine::process_front(bool polarity, uint32_t time) {
         if(data_ok) {
             readed_data = (readed_data << 1) | data;
 
-            // header and stop bit
-            if((readed_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return;
-
-            // row parity
-            for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
-                uint8_t parity_sum = 0;
-
-                for(uint8_t j = 0; j < 5; j++) {
-                    parity_sum += (readed_data >> (EM_FIRST_ROW_POS - i * 5 + j)) & 1;
-                }
-
-                if((parity_sum % 2)) {
-                    return;
-                }
-            }
-
-            // columns parity
-            for(uint8_t i = 0; i < 4; i++) {
-                uint8_t parity_sum = 0;
-
-                for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
-                    parity_sum += (readed_data >> (EM_COLUMN_POS - i + j * 5)) & 1;
-                }
-
-                if((parity_sum % 2)) {
-                    return;
-                }
-            }
-
-            // checks ok
-            ready = true;
+            ready = em_marine.can_be_decoded(
+                reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t));
         }
     }
 }

+ 2 - 1
applications/lf-rfid/helpers/decoder-emmarine.h

@@ -2,7 +2,7 @@
 #include <stdint.h>
 #include <atomic>
 #include "manchester-decoder.h"
-
+#include "protocols/protocol-emmarin.h"
 class DecoderEMMarine {
 public:
     bool read(uint8_t* data, uint8_t data_size);
@@ -17,4 +17,5 @@ private:
     std::atomic<bool> ready;
 
     ManchesterState manchester_saved_state;
+    ProtocolEMMarin em_marine;
 };

+ 4 - 91
applications/lf-rfid/helpers/decoder-hid26.cpp

@@ -17,11 +17,8 @@ bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
 
     if(ready) {
         result = true;
-        data[0] = facility;
-        data[1] = (uint8_t)(number >> 8);
-        data[2] = (uint8_t)number;
-
-        //printf("HID %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
+        hid.decode(
+            reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
         ready = false;
     }
 
@@ -87,94 +84,10 @@ void DecoderHID26::store_data(bool data) {
     stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
     stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
     stored_data[2] = (stored_data[2] << 1) | data;
-    validate_stored_data();
-}
-
-void DecoderHID26::validate_stored_data() {
-    // packet preamble
-    // raw data
-    if(*(reinterpret_cast<uint8_t*>(stored_data) + 3) != 0x1D) {
-        return;
-    }
-
-    // encoded company/oem
-    // coded with 01 = 0, 10 = 1 transitions
-    // stored in word 0
-    if((*stored_data >> 10 & 0x3FFF) != 0x1556) {
-        return;
-    }
-
-    // encoded format/length
-    // coded with 01 = 0, 10 = 1 transitions
-    // stored in word 0 and word 1
-    if((((*stored_data & 0x3FF) << 12) | ((*(stored_data + 1) >> 20) & 0xFFF)) != 0x155556) {
-        return;
-    }
-
-    // data decoding
-    uint32_t result = 0;
-
-    // decode from word 1
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 9; i >= 0; i--) {
-        switch((*(stored_data + 1) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            return;
-            break;
-        }
-    }
-
-    // decode from word 2
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 15; i >= 0; i--) {
-        switch((*(stored_data + 2) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            return;
-            break;
-        }
-    }
-
-    // store decoded data
-    facility = result >> 17;
-    number = result >> 1;
-
-    // trailing parity (odd) test
-    uint8_t parity_sum = 0;
-    for(int8_t i = 0; i < 13; i++) {
-        if(((result >> i) & 1) == 1) {
-            parity_sum++;
-        }
-    }
 
-    if((parity_sum % 2) != 1) {
-        return;
+    if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
+        ready = true;
     }
-
-    // leading parity (even) test
-    parity_sum = 0;
-    for(int8_t i = 13; i < 26; i++) {
-        if(((result >> i) & 1) == 1) {
-            parity_sum++;
-        }
-    }
-
-    if((parity_sum % 2) == 1) {
-        return;
-    }
-
-    ready = true;
 }
 
 void DecoderHID26::reset_state() {

+ 2 - 4
applications/lf-rfid/helpers/decoder-hid26.h

@@ -1,6 +1,7 @@
 #pragma once
 #include <stdint.h>
 #include <atomic>
+#include "protocols/protocol-hid-h10301.h"
 
 class DecoderHID26 {
 public:
@@ -15,12 +16,9 @@ private:
 
     uint32_t stored_data[3] = {0, 0, 0};
     void store_data(bool data);
-    void validate_stored_data();
-
-    uint8_t facility = 0;
-    uint16_t number = 0;
 
     std::atomic<bool> ready;
 
     void reset_state();
+    ProtocolHID10301 hid;
 };

+ 3 - 37
applications/lf-rfid/helpers/encoder-emmarine.cpp

@@ -1,48 +1,14 @@
 #include "encoder-emmarine.h"
+#include "protocols/protocol-emmarin.h"
 #include <furi.h>
 
 void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
-    furi_check(data_size == 5);
+    ProtocolEMMarin em_marin;
+    em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(uint64_t));
 
-    // header
-    card_data = 0b111111111;
-
-    // data
-    for(uint8_t i = 0; i < 5; i++) {
-        write_nibble(false, data[i]);
-        write_nibble(true, data[i]);
-    }
-
-    // column parity and stop bit
-    uint8_t parity_sum;
-
-    for(uint8_t c = 0; c < 4; c++) {
-        parity_sum = 0;
-        for(uint8_t i = 1; i <= 10; i++) {
-            uint8_t parity_bit = (card_data >> (i * 5 - 1)) & 1;
-            parity_sum += parity_bit;
-        }
-        card_data = (card_data << 1) | ((parity_sum % 2) & 1);
-    }
-
-    // stop bit
-    card_data = (card_data << 1) | 0;
     card_data_index = 0;
 }
 
-void EncoderEM::write_nibble(bool low_nibble, uint8_t data) {
-    uint8_t parity_sum = 0;
-    uint8_t start = 0;
-    if(!low_nibble) start = 4;
-
-    for(int8_t i = (start + 3); i >= start; i--) {
-        parity_sum += (data >> i) & 1;
-        card_data = (card_data << 1) | ((data >> i) & 1);
-    }
-
-    card_data = (card_data << 1) | ((parity_sum % 2) & 1);
-}
-
 // data transmitted as manchester encoding
 // 0 - high2low
 // 1 - low2high

+ 0 - 1
applications/lf-rfid/helpers/encoder-emmarine.h

@@ -19,5 +19,4 @@ private:
 
     uint64_t card_data;
     uint8_t card_data_index;
-    void write_nibble(bool low_nibble, uint8_t data);
 };

+ 3 - 66
applications/lf-rfid/helpers/encoder-hid-h10301.cpp

@@ -1,73 +1,10 @@
 #include "encoder-hid-h10301.h"
+#include "protocols/protocol-hid-h10301.h"
 #include <furi.h>
 
 void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
-    furi_check(data_size == 3);
-
-    card_data[0] = 0;
-    card_data[1] = 0;
-    card_data[2] = 0;
-
-    uint32_t fc_cn = (data[0] << 16) | (data[1] << 8) | data[2];
-
-    // even parity sum calculation (high 12 bits of data)
-    uint8_t even_parity_sum = 0;
-    for(int8_t i = 12; i < 24; i++) {
-        if(((fc_cn >> i) & 1) == 1) {
-            even_parity_sum++;
-        }
-    }
-
-    // odd parity sum calculation (low 12 bits of data)
-    uint8_t odd_parity_sum = 1;
-    for(int8_t i = 0; i < 12; i++) {
-        if(((fc_cn >> i) & 1) == 1) {
-            odd_parity_sum++;
-        }
-    }
-
-    // 0x1D preamble
-    write_raw_bit(0, 0);
-    write_raw_bit(0, 1);
-    write_raw_bit(0, 2);
-    write_raw_bit(1, 3);
-    write_raw_bit(1, 4);
-    write_raw_bit(1, 5);
-    write_raw_bit(0, 6);
-    write_raw_bit(1, 7);
-
-    // company / OEM code 1
-    write_bit(0, 8);
-    write_bit(0, 10);
-    write_bit(0, 12);
-    write_bit(0, 14);
-    write_bit(0, 16);
-    write_bit(0, 18);
-    write_bit(1, 20);
-
-    // card format / length 1
-    write_bit(0, 22);
-    write_bit(0, 24);
-    write_bit(0, 26);
-    write_bit(0, 28);
-    write_bit(0, 30);
-    write_bit(0, 32);
-    write_bit(0, 34);
-    write_bit(0, 36);
-    write_bit(0, 38);
-    write_bit(0, 40);
-    write_bit(1, 42);
-
-    // even parity bit
-    write_bit((even_parity_sum % 2), 44);
-
-    // data
-    for(uint8_t i = 0; i < 24; i++) {
-        write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2));
-    }
-
-    // odd parity bit
-    write_bit((odd_parity_sum % 2), 94);
+    ProtocolHID10301 hid;
+    hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
 
     card_data_index = 0;
     bit_index = 0;

+ 3 - 89
applications/lf-rfid/helpers/encoder-indala-40134.cpp

@@ -1,102 +1,16 @@
 #include "encoder-indala-40134.h"
+#include "protocols/protocol-indala-40134.h"
 #include <furi.h>
 
 void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) {
-    furi_check(data_size == 3);
-    uint32_t fc_and_card = (data[0] << 16) | (data[1] << 8) | data[2];
-
-    card_data = 0;
-
-    // preamble
-    set_bit(1, 0);
-    set_bit(1, 2);
-    set_bit(1, 32);
-
-    // factory code
-    set_bit(((fc_and_card >> 23) & 1), 57);
-    set_bit(((fc_and_card >> 22) & 1), 49);
-    set_bit(((fc_and_card >> 21) & 1), 44);
-    set_bit(((fc_and_card >> 20) & 1), 47);
-    set_bit(((fc_and_card >> 19) & 1), 48);
-    set_bit(((fc_and_card >> 18) & 1), 53);
-    set_bit(((fc_and_card >> 17) & 1), 39);
-    set_bit(((fc_and_card >> 16) & 1), 58);
-
-    // card number
-    set_bit(((fc_and_card >> 15) & 1), 42);
-    set_bit(((fc_and_card >> 14) & 1), 45);
-    set_bit(((fc_and_card >> 13) & 1), 43);
-    set_bit(((fc_and_card >> 12) & 1), 40);
-    set_bit(((fc_and_card >> 11) & 1), 52);
-    set_bit(((fc_and_card >> 10) & 1), 36);
-    set_bit(((fc_and_card >> 9) & 1), 35);
-    set_bit(((fc_and_card >> 8) & 1), 51);
-    set_bit(((fc_and_card >> 7) & 1), 46);
-    set_bit(((fc_and_card >> 6) & 1), 33);
-    set_bit(((fc_and_card >> 5) & 1), 37);
-    set_bit(((fc_and_card >> 4) & 1), 54);
-    set_bit(((fc_and_card >> 3) & 1), 56);
-    set_bit(((fc_and_card >> 2) & 1), 59);
-    set_bit(((fc_and_card >> 1) & 1), 50);
-    set_bit(((fc_and_card >> 0) & 1), 41);
-
-    // checksum
-    uint8_t checksum = 0;
-    checksum += ((fc_and_card >> 14) & 1);
-    checksum += ((fc_and_card >> 12) & 1);
-    checksum += ((fc_and_card >> 9) & 1);
-    checksum += ((fc_and_card >> 8) & 1);
-    checksum += ((fc_and_card >> 6) & 1);
-    checksum += ((fc_and_card >> 5) & 1);
-    checksum += ((fc_and_card >> 2) & 1);
-    checksum += ((fc_and_card >> 0) & 1);
-
-    // wiegand parity bits
-    // even parity sum calculation (high 12 bits of data)
-    uint8_t even_parity_sum = 0;
-    for(int8_t i = 12; i < 24; i++) {
-        if(((fc_and_card >> i) & 1) == 1) {
-            even_parity_sum++;
-        }
-    }
-
-    // odd parity sum calculation (low 12 bits of data)
-    uint8_t odd_parity_sum = 1;
-    for(int8_t i = 0; i < 12; i++) {
-        if(((fc_and_card >> i) & 1) == 1) {
-            odd_parity_sum++;
-        }
-    }
-
-    // even parity bit
-    set_bit((even_parity_sum % 2), 34);
-
-    // odd parity bit
-    set_bit((odd_parity_sum % 2), 38);
-
-    // checksum
-    if((checksum & 1) == 1) {
-        set_bit(0, 62);
-        set_bit(1, 63);
-    } else {
-        set_bit(1, 62);
-        set_bit(0, 63);
-    }
+    ProtocolIndala40134 indala;
+    indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data));
 
     last_bit = card_data & 1;
     card_data_index = 0;
     current_polarity = true;
 }
 
-void EncoderIndala_40134::set_bit(bool bit, uint8_t position) {
-    position = 63 - position;
-    if(bit) {
-        card_data |= 1ull << position;
-    } else {
-        card_data &= ~(1ull << position);
-    }
-}
-
 void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
     *period = 2;
     *pulse = 1;

+ 0 - 2
applications/lf-rfid/helpers/encoder-indala-40134.h

@@ -20,6 +20,4 @@ private:
     bool last_bit;
     bool current_polarity;
     static const uint8_t clock_per_bit = 16;
-
-    void set_bit(bool bit, uint8_t position);
 };

+ 1 - 0
applications/lf-rfid/helpers/key-info.h

@@ -6,4 +6,5 @@ static const uint8_t LFRFID_KEY_SIZE = 8;
 enum class LfrfidKeyType : uint8_t {
     KeyEmarine,
     KeyHID,
+    KeyIndala,
 };

+ 150 - 0
applications/lf-rfid/helpers/protocols/protocol-emmarin.cpp

@@ -0,0 +1,150 @@
+#include "protocol-emmarin.h"
+#include <furi.h>
+
+#define EM_HEADER_POS 55
+#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
+
+#define EM_FIRST_ROW_POS 50
+
+#define EM_ROW_COUNT 10
+#define EM_COLUMN_COUNT 4
+#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
+
+#define EM_COLUMN_POS 4
+#define EM_STOP_POS 0
+#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
+
+#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
+#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
+
+typedef uint64_t EMMarinCardData;
+
+void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) {
+    uint8_t parity_sum = 0;
+    uint8_t start = 0;
+    if(!low_nibble) start = 4;
+
+    for(int8_t i = (start + 3); i >= start; i--) {
+        parity_sum += (data >> i) & 1;
+        *card_data = (*card_data << 1) | ((data >> i) & 1);
+    }
+
+    *card_data = (*card_data << 1) | ((parity_sum % 2) & 1);
+}
+
+uint8_t ProtocolEMMarin::get_encoded_data_size() {
+    return sizeof(EMMarinCardData);
+}
+
+uint8_t ProtocolEMMarin::get_decoded_data_size() {
+    return 5;
+}
+
+void ProtocolEMMarin::encode(
+    const uint8_t* decoded_data,
+    const uint8_t decoded_data_size,
+    uint8_t* encoded_data,
+    const uint8_t encoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    EMMarinCardData card_data;
+
+    // header
+    card_data = 0b111111111;
+
+    // data
+    for(uint8_t i = 0; i < get_decoded_data_size(); i++) {
+        write_nibble(false, decoded_data[i], &card_data);
+        write_nibble(true, decoded_data[i], &card_data);
+    }
+
+    // column parity and stop bit
+    uint8_t parity_sum;
+
+    for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
+        parity_sum = 0;
+        for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
+            uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
+            parity_sum += parity_bit;
+        }
+        card_data = (card_data << 1) | ((parity_sum % 2) & 1);
+    }
+
+    // stop bit
+    card_data = (card_data << 1) | 0;
+
+    memcpy(encoded_data, &card_data, get_encoded_data_size());
+}
+
+void ProtocolEMMarin::decode(
+    const uint8_t* encoded_data,
+    const uint8_t encoded_data_size,
+    uint8_t* decoded_data,
+    const uint8_t decoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    uint8_t decoded_data_index = 0;
+    EMMarinCardData card_data = *(reinterpret_cast<const EMMarinCardData*>(encoded_data));
+
+    // clean result
+    memset(decoded_data, 0, decoded_data_size);
+
+    // header
+    for(uint8_t i = 0; i < 9; i++) {
+        card_data = card_data << 1;
+    }
+
+    // nibbles
+    uint8_t value = 0;
+    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
+        uint8_t nibble = 0;
+        for(uint8_t i = 0; i < 5; i++) {
+            if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
+            card_data = card_data << 1;
+        }
+        value = (value << 4) | nibble;
+        if(r % 2) {
+            decoded_data[decoded_data_index] |= value;
+            decoded_data_index++;
+            value = 0;
+        }
+    }
+}
+
+bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
+    furi_check(encoded_data_size >= get_encoded_data_size());
+    const EMMarinCardData* card_data = reinterpret_cast<const EMMarinCardData*>(encoded_data);
+
+    // check header and stop bit
+    if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
+
+    // check row parity
+    for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
+        uint8_t parity_sum = 0;
+
+        for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
+            parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
+        }
+
+        if((parity_sum % 2)) {
+            return false;
+        }
+    }
+
+    // check columns parity
+    for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
+        uint8_t parity_sum = 0;
+
+        for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
+            parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
+        }
+
+        if((parity_sum % 2)) {
+            return false;
+        }
+    }
+
+    return true;
+}

+ 22 - 0
applications/lf-rfid/helpers/protocols/protocol-emmarin.h

@@ -0,0 +1,22 @@
+#pragma once
+#include "protocol-generic.h"
+
+class ProtocolEMMarin : public ProtocolGeneric {
+public:
+    uint8_t get_encoded_data_size() final;
+    uint8_t get_decoded_data_size() final;
+
+    void encode(
+        const uint8_t* decoded_data,
+        const uint8_t decoded_data_size,
+        uint8_t* encoded_data,
+        const uint8_t encoded_data_size) final;
+
+    void decode(
+        const uint8_t* encoded_data,
+        const uint8_t encoded_data_size,
+        uint8_t* decoded_data,
+        const uint8_t decoded_data_size) final;
+
+    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
+};

+ 60 - 0
applications/lf-rfid/helpers/protocols/protocol-generic.h

@@ -0,0 +1,60 @@
+#pragma once
+#include "stdint.h"
+#include "stdbool.h"
+
+class ProtocolGeneric {
+public:
+    /**
+     * @brief Get the encoded data size
+     * 
+     * @return uint8_t size of encoded data in bytes
+     */
+    virtual uint8_t get_encoded_data_size() = 0;
+
+    /**
+     * @brief Get the decoded data size
+     * 
+     * @return uint8_t size of decoded data in bytes
+     */
+    virtual uint8_t get_decoded_data_size() = 0;
+
+    /**
+     * @brief encode decoded data
+     * 
+     * @param decoded_data 
+     * @param decoded_data_size 
+     * @param encoded_data 
+     * @param encoded_data_size 
+     */
+    virtual void encode(
+        const uint8_t* decoded_data,
+        const uint8_t decoded_data_size,
+        uint8_t* encoded_data,
+        const uint8_t encoded_data_size) = 0;
+
+    /**
+     * @brief decode encoded data
+     * 
+     * @param encoded_data 
+     * @param encoded_data_size 
+     * @param decoded_data 
+     * @param decoded_data_size 
+     */
+    virtual void decode(
+        const uint8_t* encoded_data,
+        const uint8_t encoded_data_size,
+        uint8_t* decoded_data,
+        const uint8_t decoded_data_size) = 0;
+
+    /**
+     * @brief fast check that data can be correctly decoded
+     * 
+     * @param encoded_data 
+     * @param encoded_data_size 
+     * @return true - can be correctly decoded
+     * @return false - cannot be correctly decoded
+     */
+    virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
+
+    virtual ~ProtocolGeneric(){};
+};

+ 238 - 0
applications/lf-rfid/helpers/protocols/protocol-hid-h10301.cpp

@@ -0,0 +1,238 @@
+#include "protocol-hid-h10301.h"
+#include <furi.h>
+
+typedef uint32_t HID10301CardData;
+constexpr uint8_t HID10301Count = 3;
+constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8;
+
+static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
+    if(bit) {
+        card_data[position / HID10301BitSize] |=
+            1UL << (HID10301BitSize - (position % HID10301BitSize) - 1);
+    } else {
+        card_data[position / (sizeof(HID10301CardData) * 8)] &=
+            ~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1));
+    }
+}
+
+static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
+    write_raw_bit(bit, position + 0, card_data);
+    write_raw_bit(!bit, position + 1, card_data);
+}
+
+uint8_t ProtocolHID10301::get_encoded_data_size() {
+    return sizeof(HID10301CardData) * HID10301Count;
+}
+
+uint8_t ProtocolHID10301::get_decoded_data_size() {
+    return 3;
+}
+
+void ProtocolHID10301::encode(
+    const uint8_t* decoded_data,
+    const uint8_t decoded_data_size,
+    uint8_t* encoded_data,
+    const uint8_t encoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    HID10301CardData card_data[HID10301Count] = {0, 0, 0};
+
+    uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
+
+    // even parity sum calculation (high 12 bits of data)
+    uint8_t even_parity_sum = 0;
+    for(int8_t i = 12; i < 24; i++) {
+        if(((fc_cn >> i) & 1) == 1) {
+            even_parity_sum++;
+        }
+    }
+
+    // odd parity sum calculation (low 12 bits of data)
+    uint8_t odd_parity_sum = 1;
+    for(int8_t i = 0; i < 12; i++) {
+        if(((fc_cn >> i) & 1) == 1) {
+            odd_parity_sum++;
+        }
+    }
+
+    // 0x1D preamble
+    write_raw_bit(0, 0, card_data);
+    write_raw_bit(0, 1, card_data);
+    write_raw_bit(0, 2, card_data);
+    write_raw_bit(1, 3, card_data);
+    write_raw_bit(1, 4, card_data);
+    write_raw_bit(1, 5, card_data);
+    write_raw_bit(0, 6, card_data);
+    write_raw_bit(1, 7, card_data);
+
+    // company / OEM code 1
+    write_bit(0, 8, card_data);
+    write_bit(0, 10, card_data);
+    write_bit(0, 12, card_data);
+    write_bit(0, 14, card_data);
+    write_bit(0, 16, card_data);
+    write_bit(0, 18, card_data);
+    write_bit(1, 20, card_data);
+
+    // card format / length 1
+    write_bit(0, 22, card_data);
+    write_bit(0, 24, card_data);
+    write_bit(0, 26, card_data);
+    write_bit(0, 28, card_data);
+    write_bit(0, 30, card_data);
+    write_bit(0, 32, card_data);
+    write_bit(0, 34, card_data);
+    write_bit(0, 36, card_data);
+    write_bit(0, 38, card_data);
+    write_bit(0, 40, card_data);
+    write_bit(1, 42, card_data);
+
+    // even parity bit
+    write_bit((even_parity_sum % 2), 44, card_data);
+
+    // data
+    for(uint8_t i = 0; i < 24; i++) {
+        write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
+    }
+
+    // odd parity bit
+    write_bit((odd_parity_sum % 2), 94, card_data);
+
+    memcpy(encoded_data, &card_data, get_encoded_data_size());
+}
+
+void ProtocolHID10301::decode(
+    const uint8_t* encoded_data,
+    const uint8_t encoded_data_size,
+    uint8_t* decoded_data,
+    const uint8_t decoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
+
+    // data decoding
+    uint32_t result = 0;
+
+    // decode from word 1
+    // coded with 01 = 0, 10 = 1 transitions
+    for(int8_t i = 9; i >= 0; i--) {
+        switch((*(card_data + 1) >> (2 * i)) & 0b11) {
+        case 0b01:
+            result = (result << 1) | 0;
+            break;
+        case 0b10:
+            result = (result << 1) | 1;
+            break;
+        default:
+            break;
+        }
+    }
+
+    // decode from word 2
+    // coded with 01 = 0, 10 = 1 transitions
+    for(int8_t i = 15; i >= 0; i--) {
+        switch((*(card_data + 2) >> (2 * i)) & 0b11) {
+        case 0b01:
+            result = (result << 1) | 0;
+            break;
+        case 0b10:
+            result = (result << 1) | 1;
+            break;
+        default:
+            break;
+        }
+    }
+
+    uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
+
+    memcpy(decoded_data, &data, get_decoded_data_size());
+}
+
+bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
+
+    // packet preamble
+    // raw data
+    if(*(encoded_data + 3) != 0x1D) {
+        return false;
+    }
+
+    // encoded company/oem
+    // coded with 01 = 0, 10 = 1 transitions
+    // stored in word 0
+    if((*card_data >> 10 & 0x3FFF) != 0x1556) {
+        return false;
+    }
+
+    // encoded format/length
+    // coded with 01 = 0, 10 = 1 transitions
+    // stored in word 0 and word 1
+    if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
+        return false;
+    }
+
+    // data decoding
+    uint32_t result = 0;
+
+    // decode from word 1
+    // coded with 01 = 0, 10 = 1 transitions
+    for(int8_t i = 9; i >= 0; i--) {
+        switch((*(card_data + 1) >> (2 * i)) & 0b11) {
+        case 0b01:
+            result = (result << 1) | 0;
+            break;
+        case 0b10:
+            result = (result << 1) | 1;
+            break;
+        default:
+            return false;
+            break;
+        }
+    }
+
+    // decode from word 2
+    // coded with 01 = 0, 10 = 1 transitions
+    for(int8_t i = 15; i >= 0; i--) {
+        switch((*(card_data + 2) >> (2 * i)) & 0b11) {
+        case 0b01:
+            result = (result << 1) | 0;
+            break;
+        case 0b10:
+            result = (result << 1) | 1;
+            break;
+        default:
+            return false;
+            break;
+        }
+    }
+
+    // trailing parity (odd) test
+    uint8_t parity_sum = 0;
+    for(int8_t i = 0; i < 13; i++) {
+        if(((result >> i) & 1) == 1) {
+            parity_sum++;
+        }
+    }
+
+    if((parity_sum % 2) != 1) {
+        return false;
+    }
+
+    // leading parity (even) test
+    parity_sum = 0;
+    for(int8_t i = 13; i < 26; i++) {
+        if(((result >> i) & 1) == 1) {
+            parity_sum++;
+        }
+    }
+
+    if((parity_sum % 2) == 1) {
+        return false;
+    }
+
+    return true;
+}

+ 22 - 0
applications/lf-rfid/helpers/protocols/protocol-hid-h10301.h

@@ -0,0 +1,22 @@
+#pragma once
+#include "protocol-generic.h"
+
+class ProtocolHID10301 : public ProtocolGeneric {
+public:
+    uint8_t get_encoded_data_size() final;
+    uint8_t get_decoded_data_size() final;
+
+    void encode(
+        const uint8_t* decoded_data,
+        const uint8_t decoded_data_size,
+        uint8_t* encoded_data,
+        const uint8_t encoded_data_size) final;
+
+    void decode(
+        const uint8_t* encoded_data,
+        const uint8_t encoded_data_size,
+        uint8_t* decoded_data,
+        const uint8_t decoded_data_size) final;
+
+    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
+};

+ 131 - 0
applications/lf-rfid/helpers/protocols/protocol-indala-40134.cpp

@@ -0,0 +1,131 @@
+#include "protocol-indala-40134.h"
+#include <furi.h>
+
+typedef uint64_t Indala40134CardData;
+
+static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) {
+    position = (sizeof(Indala40134CardData) * 8) - 1 - position;
+    if(bit) {
+        *card_data |= 1ull << position;
+    } else {
+        *card_data &= ~(1ull << position);
+    }
+}
+
+uint8_t ProtocolIndala40134::get_encoded_data_size() {
+    return sizeof(Indala40134CardData);
+}
+
+uint8_t ProtocolIndala40134::get_decoded_data_size() {
+    return 3;
+}
+
+void ProtocolIndala40134::encode(
+    const uint8_t* decoded_data,
+    const uint8_t decoded_data_size,
+    uint8_t* encoded_data,
+    const uint8_t encoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+
+    uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
+    Indala40134CardData card_data = 0;
+
+    // preamble
+    set_bit(1, 0, &card_data);
+    set_bit(1, 2, &card_data);
+    set_bit(1, 32, &card_data);
+
+    // factory code
+    set_bit(((fc_and_card >> 23) & 1), 57, &card_data);
+    set_bit(((fc_and_card >> 22) & 1), 49, &card_data);
+    set_bit(((fc_and_card >> 21) & 1), 44, &card_data);
+    set_bit(((fc_and_card >> 20) & 1), 47, &card_data);
+    set_bit(((fc_and_card >> 19) & 1), 48, &card_data);
+    set_bit(((fc_and_card >> 18) & 1), 53, &card_data);
+    set_bit(((fc_and_card >> 17) & 1), 39, &card_data);
+    set_bit(((fc_and_card >> 16) & 1), 58, &card_data);
+
+    // card number
+    set_bit(((fc_and_card >> 15) & 1), 42, &card_data);
+    set_bit(((fc_and_card >> 14) & 1), 45, &card_data);
+    set_bit(((fc_and_card >> 13) & 1), 43, &card_data);
+    set_bit(((fc_and_card >> 12) & 1), 40, &card_data);
+    set_bit(((fc_and_card >> 11) & 1), 52, &card_data);
+    set_bit(((fc_and_card >> 10) & 1), 36, &card_data);
+    set_bit(((fc_and_card >> 9) & 1), 35, &card_data);
+    set_bit(((fc_and_card >> 8) & 1), 51, &card_data);
+    set_bit(((fc_and_card >> 7) & 1), 46, &card_data);
+    set_bit(((fc_and_card >> 6) & 1), 33, &card_data);
+    set_bit(((fc_and_card >> 5) & 1), 37, &card_data);
+    set_bit(((fc_and_card >> 4) & 1), 54, &card_data);
+    set_bit(((fc_and_card >> 3) & 1), 56, &card_data);
+    set_bit(((fc_and_card >> 2) & 1), 59, &card_data);
+    set_bit(((fc_and_card >> 1) & 1), 50, &card_data);
+    set_bit(((fc_and_card >> 0) & 1), 41, &card_data);
+
+    // checksum
+    uint8_t checksum = 0;
+    checksum += ((fc_and_card >> 14) & 1);
+    checksum += ((fc_and_card >> 12) & 1);
+    checksum += ((fc_and_card >> 9) & 1);
+    checksum += ((fc_and_card >> 8) & 1);
+    checksum += ((fc_and_card >> 6) & 1);
+    checksum += ((fc_and_card >> 5) & 1);
+    checksum += ((fc_and_card >> 2) & 1);
+    checksum += ((fc_and_card >> 0) & 1);
+
+    // wiegand parity bits
+    // even parity sum calculation (high 12 bits of data)
+    uint8_t even_parity_sum = 0;
+    for(int8_t i = 12; i < 24; i++) {
+        if(((fc_and_card >> i) & 1) == 1) {
+            even_parity_sum++;
+        }
+    }
+
+    // odd parity sum calculation (low 12 bits of data)
+    uint8_t odd_parity_sum = 1;
+    for(int8_t i = 0; i < 12; i++) {
+        if(((fc_and_card >> i) & 1) == 1) {
+            odd_parity_sum++;
+        }
+    }
+
+    // even parity bit
+    set_bit((even_parity_sum % 2), 34, &card_data);
+
+    // odd parity bit
+    set_bit((odd_parity_sum % 2), 38, &card_data);
+
+    // checksum
+    if((checksum & 1) == 1) {
+        set_bit(0, 62, &card_data);
+        set_bit(1, 63, &card_data);
+    } else {
+        set_bit(1, 62, &card_data);
+        set_bit(0, 63, &card_data);
+    }
+
+    memcpy(encoded_data, &card_data, get_encoded_data_size());
+}
+
+void ProtocolIndala40134::decode(
+    const uint8_t* encoded_data,
+    const uint8_t encoded_data_size,
+    uint8_t* decoded_data,
+    const uint8_t decoded_data_size) {
+    furi_check(decoded_data_size >= get_decoded_data_size());
+    furi_check(encoded_data_size >= get_encoded_data_size());
+    // TODO implement decoding
+    furi_check(0);
+}
+
+bool ProtocolIndala40134::can_be_decoded(
+    const uint8_t* encoded_data,
+    const uint8_t encoded_data_size) {
+    furi_check(encoded_data_size >= get_encoded_data_size());
+    // TODO implement decoding
+    furi_check(0);
+    return false;
+}

+ 22 - 0
applications/lf-rfid/helpers/protocols/protocol-indala-40134.h

@@ -0,0 +1,22 @@
+#pragma once
+#include "protocol-generic.h"
+
+class ProtocolIndala40134 : public ProtocolGeneric {
+public:
+    uint8_t get_encoded_data_size() final;
+    uint8_t get_decoded_data_size() final;
+
+    void encode(
+        const uint8_t* decoded_data,
+        const uint8_t decoded_data_size,
+        uint8_t* encoded_data,
+        const uint8_t encoded_data_size) final;
+
+    void decode(
+        const uint8_t* encoded_data,
+        const uint8_t encoded_data_size,
+        uint8_t* decoded_data,
+        const uint8_t decoded_data_size) final;
+
+    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
+};

+ 118 - 0
applications/lf-rfid/helpers/rfid-writer.cpp

@@ -0,0 +1,118 @@
+#include "rfid-writer.h"
+#include <api-hal.h>
+#include "protocols/protocol-emmarin.h"
+
+extern COMP_HandleTypeDef hcomp1;
+
+/**
+ * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us)
+ * 
+ */
+class T55xxTiming {
+public:
+    constexpr static const uint16_t wait_time = 400;
+    constexpr static const uint8_t start_gap = 15;
+    constexpr static const uint8_t write_gap = 10;
+    constexpr static const uint8_t data_0 = 24;
+    constexpr static const uint8_t data_1 = 56;
+    constexpr static const uint16_t program = 700;
+};
+
+class T55xxCmd {
+public:
+    constexpr static const uint8_t opcode_page_0 = 0b10;
+    constexpr static const uint8_t opcode_page_1 = 0b11;
+    constexpr static const uint8_t opcode_reset = 0b00;
+};
+
+RfidWriter::RfidWriter() {
+}
+
+RfidWriter::~RfidWriter() {
+}
+
+void RfidWriter::start() {
+    api_hal_rfid_tim_read(125000, 0.5);
+    api_hal_rfid_pins_read();
+}
+
+void RfidWriter::stop() {
+    api_hal_rfid_tim_read_stop();
+    api_hal_rfid_tim_reset();
+    api_hal_rfid_pins_reset();
+}
+
+void RfidWriter::write_gap(uint32_t gap_time) {
+    api_hal_rfid_tim_read_stop();
+    delay_us(gap_time * 8);
+    api_hal_rfid_tim_read_start();
+}
+
+void RfidWriter::write_bit(bool value) {
+    if(value) {
+        delay_us(T55xxTiming::data_1 * 8);
+    } else {
+        delay_us(T55xxTiming::data_0 * 8);
+    }
+    write_gap(T55xxTiming::write_gap);
+}
+
+void RfidWriter::write_byte(uint8_t value) {
+    for(uint8_t i = 0; i < 8; i++) {
+        write_bit((value >> i) & 1);
+    }
+}
+
+void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) {
+    // wait to power card
+    api_hal_rfid_tim_read_start();
+    delay_us(T55xxTiming::wait_time * 8);
+
+    // start gap
+    write_gap(T55xxTiming::start_gap);
+
+    // opcode
+    switch(page) {
+    case 0:
+        write_bit(1);
+        write_bit(0);
+        break;
+    case 1:
+        write_bit(1);
+        write_bit(1);
+        break;
+    default:
+        furi_check(false);
+        break;
+    }
+
+    // lock bit
+    write_bit(lock_bit);
+
+    // data
+    for(uint8_t i = 0; i < 32; i++) {
+        write_bit((data >> (31 - i)) & 1);
+    }
+
+    // block address
+    write_bit((block >> 2) & 1);
+    write_bit((block >> 1) & 1);
+    write_bit((block >> 0) & 1);
+
+    delay_us(T55xxTiming::program * 8);
+
+    api_hal_rfid_tim_read_stop();
+}
+
+void RfidWriter::write_em(uint8_t em_data[5]) {
+    ProtocolEMMarin em_card;
+    uint64_t em_encoded_data;
+    em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
+    uint32_t em_config_block_data = 0b01100000000101001000000001000000;
+
+    __disable_irq();
+    write_block(0, 0, false, em_config_block_data);
+    write_block(0, 1, false, em_encoded_data);
+    write_block(0, 2, false, em_encoded_data >> 32);
+    __enable_irq();
+}

+ 17 - 0
applications/lf-rfid/helpers/rfid-writer.h

@@ -0,0 +1,17 @@
+#pragma once
+#include "stdint.h"
+
+class RfidWriter {
+public:
+    RfidWriter();
+    ~RfidWriter();
+    void start();
+    void stop();
+    void write_em(uint8_t em_data[5]);
+
+private:
+    void write_gap(uint32_t gap_time);
+    void write_bit(bool value);
+    void write_byte(uint8_t value);
+    void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
+};

+ 4 - 0
applications/lf-rfid/lf-rfid-app.cpp

@@ -143,4 +143,8 @@ RfidReader* LfrfidApp::get_reader() {
 
 RfidTimerEmulator* LfrfidApp::get_emulator() {
     return &emulator;
+}
+
+RfidWriter* LfrfidApp::get_writer() {
+    return &writer;
 }

+ 5 - 0
applications/lf-rfid/lf-rfid-app.h

@@ -10,6 +10,7 @@
 #include "scene/lf-rfid-scene-read-normal.h"
 #include "scene/lf-rfid-scene-read-indala.h"
 #include "scene/lf-rfid-scene-tune.h"
+#include "scene/lf-rfid-scene-write.h"
 
 #include "helpers/rfid-reader.h"
 #include "helpers/rfid-timer-emulator.h"
@@ -30,6 +31,7 @@ public:
         EmulateHID,
         EmulateEM,
         Tune,
+        Write,
     };
 
     LfrfidAppViewManager* get_view_manager();
@@ -49,6 +51,7 @@ public:
 
     RfidReader* get_reader();
     RfidTimerEmulator* get_emulator();
+    RfidWriter* get_writer();
 
 private:
     std::list<Scene> previous_scenes_list = {Scene::Exit};
@@ -63,6 +66,7 @@ private:
         {Scene::EmulateHID, new LfrfidSceneEmulateHID()},
         {Scene::EmulateEM, new LfrfidSceneEmulateEMMarine()},
         {Scene::Tune, new LfrfidSceneTune()},
+        {Scene::Write, new LfrfidSceneWrite()},
     };
 
     static const uint8_t text_store_size = 128;
@@ -70,4 +74,5 @@ private:
 
     RfidReader reader;
     RfidTimerEmulator emulator;
+    RfidWriter writer;
 };

+ 9 - 0
applications/lf-rfid/scene/lf-rfid-scene-read-normal.cpp

@@ -55,6 +55,15 @@ bool LfrfidSceneReadNormal::on_event(LfrfidApp* app, LfrfidEvent* event) {
                     data[2],
                     success_reads);
                 break;
+            case LfrfidKeyType::KeyIndala:
+                app->set_text_store(
+                    "[IND] %02X %02X %02X\n"
+                    "count: %u",
+                    data[0],
+                    data[1],
+                    data[2],
+                    success_reads);
+                break;
             }
             popup_set_text(
                 app->get_view_manager()->get_popup(),

+ 5 - 0
applications/lf-rfid/scene/lf-rfid-scene-start.cpp

@@ -5,6 +5,7 @@
 #include <callback-connector.h>
 
 typedef enum {
+    SubmenuIndexWrite,
     SubmenuIndexReadNormal,
     SubmenuIndexReadIndala,
     SubmenuIndexEmulateEM,
@@ -18,6 +19,7 @@ void LfrfidSceneStart::on_enter(LfrfidApp* app) {
     Submenu* submenu = view_manager->get_submenu();
     auto callback = cbc::obtain_connector(this, &LfrfidSceneStart::submenu_callback);
 
+    submenu_add_item(submenu, "Write T5577", SubmenuIndexWrite, callback, app);
     submenu_add_item(submenu, "Read Normal", SubmenuIndexReadNormal, callback, app);
     submenu_add_item(submenu, "Read Indala", SubmenuIndexReadIndala, callback, app);
     submenu_add_item(submenu, "Emulate EM", SubmenuIndexEmulateEM, callback, app);
@@ -33,6 +35,9 @@ bool LfrfidSceneStart::on_event(LfrfidApp* app, LfrfidEvent* event) {
 
     if(event->type == LfrfidEvent::Type::MenuSelected) {
         switch(event->payload.menu_index) {
+        case SubmenuIndexWrite:
+            app->switch_to_next_scene(LfrfidApp::Scene::Write);
+            break;
         case SubmenuIndexReadNormal:
             app->switch_to_next_scene(LfrfidApp::Scene::ReadNormal);
             break;

+ 70 - 0
applications/lf-rfid/scene/lf-rfid-scene-write.cpp

@@ -0,0 +1,70 @@
+#include "lf-rfid-scene-write.h"
+
+#include "../lf-rfid-app.h"
+#include "../lf-rfid-view-manager.h"
+#include "../lf-rfid-event.h"
+#include "../helpers/key-info.h"
+
+void LfrfidSceneWrite::on_enter(LfrfidApp* app) {
+    LfrfidAppViewManager* view_manager = app->get_view_manager();
+
+    Popup* popup = view_manager->get_popup();
+    popup_set_header(popup, "LF-RFID", 64, 16, AlignCenter, AlignBottom);
+    app->set_text_store("Writing...");
+    popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
+
+    view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup);
+
+    timing_index = 0;
+}
+
+bool LfrfidSceneWrite::on_event(LfrfidApp* app, LfrfidEvent* event) {
+    bool consumed = false;
+
+    // TODO move read\write logic to key worker
+
+    bool readed = false;
+    uint8_t em_data[5] = {0x1A, 0x2B, 0xC3, 0xD4, 0xE5};
+
+    if(timing_index == 0) {
+        app->get_reader()->stop();
+        app->get_writer()->start();
+        app->get_writer()->write_em(em_data);
+        app->get_writer()->stop();
+        delay(100);
+        app->get_reader()->start(RfidReader::Type::Normal);
+    } else {
+        uint8_t data[LFRFID_KEY_SIZE];
+        LfrfidKeyType type;
+
+        app->get_reader()->read(&type, data, LFRFID_KEY_SIZE);
+        if(type == LfrfidKeyType::KeyEmarine) {
+            if(memcmp(em_data, data, 5) == 0) {
+                readed = true;
+            }
+        }
+    }
+
+    if(readed) {
+        app->set_text_store("Writed!");
+        app->notify_green_blink();
+    } else {
+        app->set_text_store("Writing [1A 2B C3 D4 E5]");
+        timing_index++;
+        if(timing_index == 4) {
+            timing_index = 0;
+        }
+    }
+    popup_set_text(
+        app->get_view_manager()->get_popup(), app->get_text_store(), 64, 22, AlignCenter, AlignTop);
+
+    return consumed;
+}
+
+void LfrfidSceneWrite::on_exit(LfrfidApp* app) {
+    LfrfidAppViewManager* view_manager = app->get_view_manager();
+
+    Popup* popup = view_manager->get_popup();
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+}

+ 14 - 0
applications/lf-rfid/scene/lf-rfid-scene-write.h

@@ -0,0 +1,14 @@
+#pragma once
+#include "lf-rfid-scene-generic.h"
+#include "../helpers/key-info.h"
+#include "../helpers/rfid-writer.h"
+
+class LfrfidSceneWrite : public LfrfidScene {
+public:
+    void on_enter(LfrfidApp* app) final;
+    bool on_event(LfrfidApp* app, LfrfidEvent* event) final;
+    void on_exit(LfrfidApp* app) final;
+
+private:
+    uint8_t timing_index;
+};

+ 27 - 18
firmware/targets/f5/api-hal/api-hal-gpio.c

@@ -41,11 +41,28 @@ static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) {
     return pin_num;
 }
 
+void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) {
+    hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
+}
+
 void hal_gpio_init(
     const GpioPin* gpio,
     const GpioMode mode,
     const GpioPull pull,
     const GpioSpeed speed) {
+    // we cannot set alternate mode in this function
+    furi_assert(mode != GpioModeAltFunctionPushPull);
+    furi_assert(mode != GpioModeAltFunctionOpenDrain);
+
+    hal_gpio_init_ex(gpio, mode, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+}
+
+void hal_gpio_init_ex(
+    const GpioPin* gpio,
+    const GpioMode mode,
+    const GpioPull pull,
+    const GpioSpeed speed,
+    const GpioAltFn alt_fn) {
     uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
     uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
     uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
@@ -112,27 +129,19 @@ void hal_gpio_init(
             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
         }
     }
-    __enable_irq();
-}
 
-void hal_gpio_init_alt(
-    const GpioPin* gpio,
-    const GpioMode mode,
-    const GpioPull pull,
-    const GpioSpeed speed,
-    const GpioAltFn alt_fn) {
-    hal_gpio_init(gpio, mode, pull, speed);
+    if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) {
+        // enable alternate mode
+        LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
 
-    __disable_irq();
-    // enable alternate mode
-    LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
-
-    // set alternate function
-    if(hal_gpio_get_pin_num(gpio) < 8) {
-        LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
-    } else {
-        LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
+        // set alternate function
+        if(hal_gpio_get_pin_num(gpio) < 8) {
+            LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
+        } else {
+            LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
+        }
     }
+
     __enable_irq();
 }
 

+ 12 - 3
firmware/targets/f5/api-hal/api-hal-gpio.h

@@ -153,6 +153,8 @@ typedef enum {
     GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */
 
     GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */
+
+    GpioAltFnUnused = 16, /*!< just dummy value */
 } GpioAltFn;
 
 /**
@@ -164,7 +166,14 @@ typedef struct {
 } GpioPin;
 
 /**
- * GPIO initialization function
+ * GPIO initialization function, simple version
+ * @param gpio  GpioPin
+ * @param mode  GpioMode
+ */
+void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode);
+
+/**
+ * GPIO initialization function, normal version
  * @param gpio  GpioPin
  * @param mode  GpioMode
  * @param pull  GpioPull
@@ -177,14 +186,14 @@ void hal_gpio_init(
     const GpioSpeed speed);
 
 /**
- * GPIO initialization with alternative function
+ * GPIO initialization function, extended version
  * @param gpio  GpioPin
  * @param mode  GpioMode
  * @param pull  GpioPull
  * @param speed GpioSpeed
  * @param alt_fn GpioAltFn
  */
-void hal_gpio_init_alt(
+void hal_gpio_init_ex(
     const GpioPin* gpio,
     const GpioMode mode,
     const GpioPull pull,

+ 8 - 4
firmware/targets/f5/api-hal/api-hal-rfid.c

@@ -21,8 +21,8 @@ void api_hal_rfid_pins_emulate() {
     api_hal_ibutton_pin_low();
 
     // pull pin to timer out
-    hal_gpio_init_alt(
-        &gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM1);
+    hal_gpio_init_ex(
+        &gpio_rfid_pull, GpioModeAltFunctionPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM1);
 
     // pull rfid antenna from carrier side
     hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo);
@@ -39,8 +39,12 @@ void api_hal_rfid_pins_read() {
     hal_gpio_write(&gpio_rfid_pull, false);
 
     // carrier pin to timer out
-    hal_gpio_init_alt(
-        &gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM1);
+    hal_gpio_init_ex(
+        &gpio_rfid_carrier_out,
+        GpioModeAltFunctionPushPull,
+        GpioSpeedLow,
+        GpioPullNo,
+        GpioAltFn1TIM1);
 
     // comparator in
     hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioSpeedLow, GpioPullNo);