347 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <furi_hal_rfid.h>
 | |
| #include <toolbox/stream/file_stream.h>
 | |
| #include <toolbox/buffer_stream.h>
 | |
| #include <toolbox/varint.h>
 | |
| #include "lfrfid_raw_worker.h"
 | |
| #include "lfrfid_raw_file.h"
 | |
| #include "tools/varint_pair.h"
 | |
| 
 | |
| #define EMULATE_BUFFER_SIZE 1024
 | |
| #define RFID_DATA_BUFFER_SIZE 2048
 | |
| #define READ_DATA_BUFFER_COUNT 4
 | |
| 
 | |
| #define TAG_EMULATE "RAW EMULATE"
 | |
| 
 | |
| // emulate mode
 | |
| typedef struct {
 | |
|     size_t overrun_count;
 | |
|     FuriStreamBuffer* stream;
 | |
| } RfidEmulateCtx;
 | |
| 
 | |
| typedef struct {
 | |
|     uint32_t emulate_buffer_arr[EMULATE_BUFFER_SIZE];
 | |
|     uint32_t emulate_buffer_ccr[EMULATE_BUFFER_SIZE];
 | |
|     RfidEmulateCtx ctx;
 | |
| } LFRFIDRawWorkerEmulateData;
 | |
| 
 | |
| typedef enum {
 | |
|     HalfTransfer,
 | |
|     TransferComplete,
 | |
| } LFRFIDRawEmulateDMAEvent;
 | |
| 
 | |
| // read mode
 | |
| #define READ_TEMP_DATA_SIZE 10
 | |
| 
 | |
| typedef struct {
 | |
|     BufferStream* stream;
 | |
|     VarintPair* pair;
 | |
| } LFRFIDRawWorkerReadData;
 | |
| 
 | |
| // main worker
 | |
| struct LFRFIDRawWorker {
 | |
|     FuriString* file_path;
 | |
|     FuriThread* thread;
 | |
|     FuriEventFlag* events;
 | |
| 
 | |
|     LFRFIDWorkerEmulateRawCallback emulate_callback;
 | |
|     LFRFIDWorkerReadRawCallback read_callback;
 | |
|     void* context;
 | |
| 
 | |
|     float frequency;
 | |
|     float duty_cycle;
 | |
| };
 | |
| 
 | |
| typedef enum {
 | |
|     LFRFIDRawWorkerEventStop,
 | |
| } LFRFIDRawWorkerEvent;
 | |
| 
 | |
| static int32_t lfrfid_raw_read_worker_thread(void* thread_context);
 | |
| static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context);
 | |
| 
 | |
| LFRFIDRawWorker* lfrfid_raw_worker_alloc() {
 | |
|     LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker));
 | |
| 
 | |
|     worker->thread = furi_thread_alloc_ex("LfrfidRawWorker", 2048, NULL, worker);
 | |
| 
 | |
|     worker->events = furi_event_flag_alloc(NULL);
 | |
| 
 | |
|     worker->file_path = furi_string_alloc();
 | |
|     return worker;
 | |
| }
 | |
| 
 | |
| void lfrfid_raw_worker_free(LFRFIDRawWorker* worker) {
 | |
|     furi_thread_free(worker->thread);
 | |
|     furi_event_flag_free(worker->events);
 | |
|     furi_string_free(worker->file_path);
 | |
|     free(worker);
 | |
| }
 | |
| 
 | |
| void lfrfid_raw_worker_start_read(
 | |
|     LFRFIDRawWorker* worker,
 | |
|     const char* file_path,
 | |
|     float freq,
 | |
|     float duty_cycle,
 | |
|     LFRFIDWorkerReadRawCallback callback,
 | |
|     void* context) {
 | |
|     furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);
 | |
| 
 | |
|     furi_string_set(worker->file_path, file_path);
 | |
| 
 | |
|     worker->frequency = freq;
 | |
|     worker->duty_cycle = duty_cycle;
 | |
|     worker->read_callback = callback;
 | |
|     worker->context = context;
 | |
| 
 | |
|     furi_thread_set_callback(worker->thread, lfrfid_raw_read_worker_thread);
 | |
| 
 | |
|     furi_thread_start(worker->thread);
 | |
| }
 | |
| 
 | |
| void lfrfid_raw_worker_start_emulate(
 | |
|     LFRFIDRawWorker* worker,
 | |
|     const char* file_path,
 | |
|     LFRFIDWorkerEmulateRawCallback callback,
 | |
|     void* context) {
 | |
|     furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped);
 | |
|     furi_string_set(worker->file_path, file_path);
 | |
|     worker->emulate_callback = callback;
 | |
|     worker->context = context;
 | |
|     furi_thread_set_callback(worker->thread, lfrfid_raw_emulate_worker_thread);
 | |
|     furi_thread_start(worker->thread);
 | |
| }
 | |
| 
 | |
| void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) {
 | |
|     worker->emulate_callback = NULL;
 | |
|     worker->context = NULL;
 | |
|     worker->read_callback = NULL;
 | |
|     furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop);
 | |
|     furi_thread_join(worker->thread);
 | |
| }
 | |
| 
 | |
| static void lfrfid_raw_worker_capture(bool level, uint32_t duration, void* context) {
 | |
|     LFRFIDRawWorkerReadData* ctx = context;
 | |
| 
 | |
|     bool need_to_send = varint_pair_pack(ctx->pair, level, duration);
 | |
| 
 | |
|     if(need_to_send) {
 | |
|         buffer_stream_send_from_isr(
 | |
|             ctx->stream, varint_pair_get_data(ctx->pair), varint_pair_get_size(ctx->pair));
 | |
|         varint_pair_reset(ctx->pair);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int32_t lfrfid_raw_read_worker_thread(void* thread_context) {
 | |
|     LFRFIDRawWorker* worker = (LFRFIDRawWorker*)thread_context;
 | |
| 
 | |
|     Storage* storage = furi_record_open(RECORD_STORAGE);
 | |
|     LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
 | |
|     const char* filename = furi_string_get_cstr(worker->file_path);
 | |
|     bool file_valid = lfrfid_raw_file_open_write(file, filename);
 | |
| 
 | |
|     LFRFIDRawWorkerReadData* data = malloc(sizeof(LFRFIDRawWorkerReadData));
 | |
| 
 | |
|     data->stream = buffer_stream_alloc(RFID_DATA_BUFFER_SIZE, READ_DATA_BUFFER_COUNT);
 | |
|     data->pair = varint_pair_alloc();
 | |
| 
 | |
|     if(file_valid) {
 | |
|         // write header
 | |
|         file_valid = lfrfid_raw_file_write_header(
 | |
|             file, worker->frequency, worker->duty_cycle, RFID_DATA_BUFFER_SIZE);
 | |
|     }
 | |
| 
 | |
|     if(file_valid) {
 | |
|         // setup carrier
 | |
|         furi_hal_rfid_pins_read();
 | |
|         furi_hal_rfid_tim_read(worker->frequency, worker->duty_cycle);
 | |
|         furi_hal_rfid_tim_read_start();
 | |
| 
 | |
|         // stabilize detector
 | |
|         furi_delay_ms(1500);
 | |
| 
 | |
|         // start capture
 | |
|         furi_hal_rfid_tim_read_capture_start(lfrfid_raw_worker_capture, data);
 | |
| 
 | |
|         while(1) {
 | |
|             Buffer* buffer = buffer_stream_receive(data->stream, 100);
 | |
| 
 | |
|             if(buffer != NULL) {
 | |
|                 file_valid = lfrfid_raw_file_write_buffer(
 | |
|                     file, buffer_get_data(buffer), buffer_get_size(buffer));
 | |
|                 buffer_reset(buffer);
 | |
|             }
 | |
| 
 | |
|             if(!file_valid) {
 | |
|                 if(worker->read_callback != NULL) {
 | |
|                     // message file_error to worker
 | |
|                     worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if(buffer_stream_get_overrun_count(data->stream) > 0 &&
 | |
|                worker->read_callback != NULL) {
 | |
|                 // message overrun to worker
 | |
|                 worker->read_callback(LFRFIDWorkerReadRawOverrun, worker->context);
 | |
|             }
 | |
| 
 | |
|             uint32_t flags = furi_event_flag_get(worker->events);
 | |
|             if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         furi_hal_rfid_tim_read_capture_stop();
 | |
|         furi_hal_rfid_tim_read_stop();
 | |
|     } else {
 | |
|         if(worker->read_callback != NULL) {
 | |
|             // message file_error to worker
 | |
|             worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(!file_valid) {
 | |
|         const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);
 | |
|         while(true) {
 | |
|             uint32_t flags = furi_event_flag_wait(
 | |
|                 worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);
 | |
| 
 | |
|             if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     varint_pair_free(data->pair);
 | |
|     buffer_stream_free(data->stream);
 | |
|     lfrfid_raw_file_free(file);
 | |
|     furi_record_close(RECORD_STORAGE);
 | |
|     free(data);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void rfid_emulate_dma_isr(bool half, void* context) {
 | |
|     RfidEmulateCtx* ctx = context;
 | |
| 
 | |
|     uint32_t flag = half ? HalfTransfer : TransferComplete;
 | |
|     size_t len = furi_stream_buffer_send(ctx->stream, &flag, sizeof(uint32_t), 0);
 | |
|     if(len != sizeof(uint32_t)) {
 | |
|         ctx->overrun_count++;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) {
 | |
|     LFRFIDRawWorker* worker = thread_context;
 | |
| 
 | |
|     bool file_valid = true;
 | |
| 
 | |
|     LFRFIDRawWorkerEmulateData* data = malloc(sizeof(LFRFIDRawWorkerEmulateData));
 | |
| 
 | |
|     Storage* storage = furi_record_open(RECORD_STORAGE);
 | |
|     data->ctx.overrun_count = 0;
 | |
|     data->ctx.stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t));
 | |
| 
 | |
|     LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
 | |
| 
 | |
|     do {
 | |
|         file_valid = lfrfid_raw_file_open_read(file, furi_string_get_cstr(worker->file_path));
 | |
|         if(!file_valid) break;
 | |
|         file_valid = lfrfid_raw_file_read_header(file, &worker->frequency, &worker->duty_cycle);
 | |
|         if(!file_valid) break;
 | |
| 
 | |
|         for(size_t i = 0; i < EMULATE_BUFFER_SIZE; i++) {
 | |
|             file_valid = lfrfid_raw_file_read_pair(
 | |
|                 file, &data->emulate_buffer_arr[i], &data->emulate_buffer_ccr[i], NULL);
 | |
|             if(!file_valid) break;
 | |
|             data->emulate_buffer_arr[i] /= 8;
 | |
|             data->emulate_buffer_arr[i] -= 1;
 | |
|             data->emulate_buffer_ccr[i] /= 8;
 | |
|         }
 | |
|     } while(false);
 | |
| 
 | |
|     furi_hal_rfid_tim_emulate_dma_start(
 | |
|         data->emulate_buffer_arr,
 | |
|         data->emulate_buffer_ccr,
 | |
|         EMULATE_BUFFER_SIZE,
 | |
|         rfid_emulate_dma_isr,
 | |
|         &data->ctx);
 | |
| 
 | |
|     if(!file_valid && worker->emulate_callback != NULL) {
 | |
|         // message file_error to worker
 | |
|         worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);
 | |
|     }
 | |
| 
 | |
|     if(file_valid) {
 | |
|         uint32_t flag = 0;
 | |
| 
 | |
|         while(true) {
 | |
|             size_t size =
 | |
|                 furi_stream_buffer_receive(data->ctx.stream, &flag, sizeof(uint32_t), 100);
 | |
| 
 | |
|             if(size == sizeof(uint32_t)) {
 | |
|                 size_t start = 0;
 | |
|                 if(flag == TransferComplete) {
 | |
|                     start = (EMULATE_BUFFER_SIZE / 2);
 | |
|                 }
 | |
| 
 | |
|                 for(size_t i = 0; i < (EMULATE_BUFFER_SIZE / 2); i++) {
 | |
|                     file_valid = lfrfid_raw_file_read_pair(
 | |
|                         file,
 | |
|                         &data->emulate_buffer_arr[start + i],
 | |
|                         &data->emulate_buffer_ccr[start + i],
 | |
|                         NULL);
 | |
|                     if(!file_valid) break;
 | |
|                     data->emulate_buffer_arr[i] /= 8;
 | |
|                     data->emulate_buffer_arr[i] -= 1;
 | |
|                     data->emulate_buffer_ccr[i] /= 8;
 | |
|                 }
 | |
|             } else if(size != 0) {
 | |
|                 data->ctx.overrun_count++;
 | |
|             }
 | |
| 
 | |
|             if(!file_valid) {
 | |
|                 if(worker->emulate_callback != NULL) {
 | |
|                     // message file_error to worker
 | |
|                     worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if(data->ctx.overrun_count > 0 && worker->emulate_callback != NULL) {
 | |
|                 // message overrun to worker
 | |
|                 worker->emulate_callback(LFRFIDWorkerEmulateRawOverrun, worker->context);
 | |
|             }
 | |
| 
 | |
|             uint32_t flags = furi_event_flag_get(worker->events);
 | |
|             if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     furi_hal_rfid_tim_emulate_dma_stop();
 | |
| 
 | |
|     if(!file_valid) {
 | |
|         const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop);
 | |
|         while(true) {
 | |
|             uint32_t flags = furi_event_flag_wait(
 | |
|                 worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever);
 | |
| 
 | |
|             if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) {
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(data->ctx.overrun_count) {
 | |
|         FURI_LOG_E(TAG_EMULATE, "overruns: %zu", data->ctx.overrun_count);
 | |
|     }
 | |
| 
 | |
|     furi_stream_buffer_free(data->ctx.stream);
 | |
|     lfrfid_raw_file_free(file);
 | |
|     furi_record_close(RECORD_STORAGE);
 | |
|     free(data);
 | |
| 
 | |
|     return 0;
 | |
| }
 | 
