[FL-2245] Introduce Mifare Classic Emulation (#1242)
* digital signal: introduce digital signal * nfca: add nfca signal encoder * nfc: add mifare classic emulation scene * nfca: add classic emulation support to lib and hal * mifare classic: support basic read commands * nfc: add mifare classic menu scene * mifare classic: start parsing commands in emulation * mifare classic: add nested auth * nfc: fix errors * mifare classic: add encrypt function * nfc: fix mifare classic save * lib hex: add hex uint64_t ASCII parser * flipper format: add uint64 hex format support * nfc: add mifare classic key map * nfc: hide mifare classic keys on emulation * mifare classic: add NACK responce * nfc: add partial bytes support in transparent mode * nfc: mifare classic add shadow file support * digital signal: move arr buffer from BSS to heap * mifare classic: process access bits more careful * nfca: fix memory leack * nfc: format sources * mifare classic: cleun up Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							
								
								
									
										173
									
								
								lib/digital_signal/digital_signal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								lib/digital_signal/digital_signal.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| #include "digital_signal.h" | ||||
|  | ||||
| #include <furi.h> | ||||
| #include <stm32wbxx_ll_dma.h> | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <math.h> | ||||
|  | ||||
| #define F_TIM (64000000.0) | ||||
| #define T_TIM (1.0 / F_TIM) | ||||
|  | ||||
| DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { | ||||
|     DigitalSignal* signal = malloc(sizeof(DigitalSignal)); | ||||
|     signal->start_level = true; | ||||
|     signal->edges_max_cnt = max_edges_cnt; | ||||
|     signal->edge_timings = malloc(max_edges_cnt * sizeof(float)); | ||||
|     signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); | ||||
|     signal->edge_cnt = 0; | ||||
|  | ||||
|     return signal; | ||||
| } | ||||
|  | ||||
| void digital_signal_free(DigitalSignal* signal) { | ||||
|     furi_assert(signal); | ||||
|  | ||||
|     free(signal->edge_timings); | ||||
|     free(signal->reload_reg_buff); | ||||
|     free(signal); | ||||
| } | ||||
|  | ||||
| bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { | ||||
|     furi_assert(signal_a); | ||||
|     furi_assert(signal_b); | ||||
|  | ||||
|     if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool end_level = signal_a->start_level; | ||||
|     if(signal_a->edge_cnt) { | ||||
|         end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); | ||||
|     } | ||||
|     uint8_t start_copy = 0; | ||||
|     if(end_level == signal_b->start_level) { | ||||
|         if(signal_a->edge_cnt) { | ||||
|             signal_a->edge_timings[signal_a->edge_cnt - 1] += signal_b->edge_timings[0]; | ||||
|             start_copy += 1; | ||||
|         } else { | ||||
|             signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0]; | ||||
|         } | ||||
|     } | ||||
|     memcpy( | ||||
|         &signal_a->edge_timings[signal_a->edge_cnt], | ||||
|         &signal_b->edge_timings[start_copy], | ||||
|         (signal_b->edge_cnt - start_copy) * sizeof(float)); | ||||
|     signal_a->edge_cnt += signal_b->edge_cnt - start_copy; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool digital_signal_get_start_level(DigitalSignal* signal) { | ||||
|     furi_assert(signal); | ||||
|  | ||||
|     return signal->start_level; | ||||
| } | ||||
|  | ||||
| uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { | ||||
|     furi_assert(signal); | ||||
|  | ||||
|     return signal->edge_cnt; | ||||
| } | ||||
|  | ||||
| float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { | ||||
|     furi_assert(signal); | ||||
|     furi_assert(edge_num < signal->edge_cnt); | ||||
|  | ||||
|     return signal->edge_timings[edge_num]; | ||||
| } | ||||
|  | ||||
| static void digital_signal_prepare_arr(DigitalSignal* signal) { | ||||
|     float t_signal = 0; | ||||
|     float t_current = 0; | ||||
|     float r = 0; | ||||
|     float r_int = 0; | ||||
|     float r_dec = 0; | ||||
|  | ||||
|     for(size_t i = 0; i < signal->edge_cnt - 1; i++) { | ||||
|         t_signal += signal->edge_timings[i]; | ||||
|         r = (t_signal - t_current) / T_TIM; | ||||
|         r_dec = modff(r, &r_int); | ||||
|         if(r_dec < 0.5f) { | ||||
|             signal->reload_reg_buff[i] = (uint32_t)r_int - 1; | ||||
|         } else { | ||||
|             signal->reload_reg_buff[i] = (uint32_t)r_int; | ||||
|         } | ||||
|         t_current += (signal->reload_reg_buff[i] + 1) * T_TIM; | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { | ||||
|     furi_assert(signal); | ||||
|     furi_assert(gpio); | ||||
|  | ||||
|     // Configure gpio as output | ||||
|     furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|  | ||||
|     // Init gpio buffer and DMA channel | ||||
|     uint16_t gpio_reg = gpio->port->ODR; | ||||
|     uint16_t gpio_buff[2]; | ||||
|     if(signal->start_level) { | ||||
|         gpio_buff[0] = gpio_reg | gpio->pin; | ||||
|         gpio_buff[1] = gpio_reg & ~(gpio->pin); | ||||
|     } else { | ||||
|         gpio_buff[0] = gpio_reg & ~(gpio->pin); | ||||
|         gpio_buff[1] = gpio_reg | gpio->pin; | ||||
|     } | ||||
|     LL_DMA_InitTypeDef dma_config = {}; | ||||
|     dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; | ||||
|     dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); | ||||
|     dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||
|     dma_config.Mode = LL_DMA_MODE_CIRCULAR; | ||||
|     dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||
|     dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||
|     dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; | ||||
|     dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; | ||||
|     dma_config.NbData = 2; | ||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||
|     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; | ||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); | ||||
|     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); | ||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||
|  | ||||
|     // Init timer arr register buffer and DMA channel | ||||
|     digital_signal_prepare_arr(signal); | ||||
|     dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; | ||||
|     dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); | ||||
|     dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||
|     dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||
|     dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||
|     dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||
|     dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; | ||||
|     dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; | ||||
|     dma_config.NbData = signal->edge_cnt - 2; | ||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||
|     dma_config.Priority = LL_DMA_PRIORITY_HIGH; | ||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); | ||||
|     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); | ||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||
|  | ||||
|     // Set up timer | ||||
|     LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); | ||||
|     LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); | ||||
|     LL_TIM_SetPrescaler(TIM2, 0); | ||||
|     LL_TIM_SetAutoReload(TIM2, 10); | ||||
|     LL_TIM_SetCounter(TIM2, 0); | ||||
|     LL_TIM_EnableUpdateEvent(TIM2); | ||||
|     LL_TIM_EnableDMAReq_UPDATE(TIM2); | ||||
|  | ||||
|     // Start transactions | ||||
|     LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? | ||||
|     LL_TIM_EnableCounter(TIM2); | ||||
|  | ||||
|     while(!LL_DMA_IsActiveFlag_TC2(DMA1)) | ||||
|         ; | ||||
|  | ||||
|     LL_DMA_ClearFlag_TC1(DMA1); | ||||
|     LL_DMA_ClearFlag_TC2(DMA1); | ||||
|     LL_TIM_DisableCounter(TIM2); | ||||
|     LL_TIM_SetCounter(TIM2, 0); | ||||
|     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||
|     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
							
								
								
									
										29
									
								
								lib/digital_signal/digital_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/digital_signal/digital_signal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include <furi_hal_gpio.h> | ||||
|  | ||||
| typedef struct { | ||||
|     bool start_level; | ||||
|     uint32_t edge_cnt; | ||||
|     uint32_t edges_max_cnt; | ||||
|     float* edge_timings; | ||||
|     uint32_t* reload_reg_buff; | ||||
| } DigitalSignal; | ||||
|  | ||||
| DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); | ||||
|  | ||||
| void digital_signal_free(DigitalSignal* signal); | ||||
|  | ||||
| bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); | ||||
|  | ||||
| bool digital_signal_get_start_level(DigitalSignal* signal); | ||||
|  | ||||
| uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); | ||||
|  | ||||
| float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); | ||||
|  | ||||
| bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); | ||||
		Reference in New Issue
	
	Block a user