From c692e0ea1173eb7722ba346a5383f23d7e624f11 Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Fri, 28 Jan 2022 01:09:51 +0100 Subject: [PATCH 1/5] ISO15693 sniff codec --- Firmware/Chameleon-Mini/AntennaLevel.h | 1 + .../Chameleon-Mini/Application/Application.h | 1 + .../Chameleon-Mini/Application/Sniff14443A.c | 1 - .../Chameleon-Mini/Application/Sniff15693.c | 38 + .../Chameleon-Mini/Application/Sniff15693.h | 20 + Firmware/Chameleon-Mini/Codec/Codec.c | 6 +- Firmware/Chameleon-Mini/Codec/Codec.h | 19 +- Firmware/Chameleon-Mini/Codec/ISO14443-2A.c | 3 +- Firmware/Chameleon-Mini/Codec/ISO15693.c | 2 +- .../Chameleon-Mini/Codec/Reader14443-2A.c | 10 +- .../Chameleon-Mini/Codec/SniffISO14443-2A.c | 4 +- Firmware/Chameleon-Mini/Codec/SniffISO15693.c | 766 ++++++++++++++++++ Firmware/Chameleon-Mini/Codec/SniffISO15693.h | 41 + Firmware/Chameleon-Mini/Configuration.c | 16 +- Firmware/Chameleon-Mini/ISRSharing.S | 18 +- Firmware/Chameleon-Mini/Makefile | 14 +- 16 files changed, 933 insertions(+), 27 deletions(-) create mode 100644 Firmware/Chameleon-Mini/Application/Sniff15693.c create mode 100644 Firmware/Chameleon-Mini/Application/Sniff15693.h create mode 100644 Firmware/Chameleon-Mini/Codec/SniffISO15693.c create mode 100644 Firmware/Chameleon-Mini/Codec/SniffISO15693.h diff --git a/Firmware/Chameleon-Mini/AntennaLevel.h b/Firmware/Chameleon-Mini/AntennaLevel.h index 3b7d1413..6d7dc200 100644 --- a/Firmware/Chameleon-Mini/AntennaLevel.h +++ b/Firmware/Chameleon-Mini/AntennaLevel.h @@ -24,6 +24,7 @@ static inline void AntennaLevelInit(void) { + ADCA.CAL = (PRODSIGNATURES_ADCACAL1 << 8) | PRODSIGNATURES_ADCACAL0; /* Load calibration data, source: https://www.avrfreaks.net/comment/2080211#comment-2080211 */ ADCA.CTRLA = ADC_ENABLE_bm; ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc; ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | ADC_BANDGAP_bm; diff --git a/Firmware/Chameleon-Mini/Application/Application.h b/Firmware/Chameleon-Mini/Application/Application.h index 5f6fbf33..53caa39b 100644 --- a/Firmware/Chameleon-Mini/Application/Application.h +++ b/Firmware/Chameleon-Mini/Application/Application.h @@ -22,6 +22,7 @@ #include "Sniff14443A.h" #include "EM4233.h" #include "NTAG215.h" +#include "Sniff15693.h" /* Function wrappers */ INLINE void ApplicationInit(void) { diff --git a/Firmware/Chameleon-Mini/Application/Sniff14443A.c b/Firmware/Chameleon-Mini/Application/Sniff14443A.c index c85a1776..5d8b559a 100644 --- a/Firmware/Chameleon-Mini/Application/Sniff14443A.c +++ b/Firmware/Chameleon-Mini/Application/Sniff14443A.c @@ -11,7 +11,6 @@ extern bool checkParityBits(uint8_t *Buffer, uint16_t BitCount); Sniff14443Command Sniff14443CurrentCommand = Sniff14443_Do_Nothing; -//bool selected = false; static enum { STATE_IDLE, STATE_REQA, diff --git a/Firmware/Chameleon-Mini/Application/Sniff15693.c b/Firmware/Chameleon-Mini/Application/Sniff15693.c new file mode 100644 index 00000000..67da470e --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/Sniff15693.c @@ -0,0 +1,38 @@ +/* + * SniffISO15693.h + * + * Created on: 05.11.2019 + * Author: ceres-c + */ + +#ifdef CONFIG_ISO15693_SNIFF_SUPPORT + +#include "../Codec/SniffISO15693.h" +#include "Sniff15693.h" + +void SniffISO15693AppInit(void) +{ +} + +void SniffISO15693AppReset(void) +{ +} + + +void SniffISO15693AppTask(void) +{ + +} + +void SniffISO15693AppTick(void) +{ + + +} + +uint16_t SniffISO15693AppProcess(uint8_t* FrameBuf, uint16_t FrameBytes) +{ + return 0; +} + +#endif /* CONFIG_ISO15693_SNIFF_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/Sniff15693.h b/Firmware/Chameleon-Mini/Application/Sniff15693.h new file mode 100644 index 00000000..46920d9c --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/Sniff15693.h @@ -0,0 +1,20 @@ +/* + * SniffISO15693.h + * + * Created on: 05.11.2019 + * Author: ceres-c + */ + +#ifndef SNIFF_15693_H_ +#define SNIFF_15693_H_ + +#include "Application.h" +#include "ISO15693-A.h" + +void SniffISO15693AppInit(void); +void SniffISO15693AppReset(void); +void SniffISO15693AppTask(void); +void SniffISO15693AppTick(void); +uint16_t SniffISO15693AppProcess(uint8_t* FrameBuf, uint16_t FrameBytes); + +#endif /* SNIFF_15693_H_ */ diff --git a/Firmware/Chameleon-Mini/Codec/Codec.c b/Firmware/Chameleon-Mini/Codec/Codec.c index aaf143c5..1523cb10 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.c +++ b/Firmware/Chameleon-Mini/Codec/Codec.c @@ -26,9 +26,13 @@ static volatile struct { uint8_t CodecBuffer[CODEC_BUFFER_SIZE]; uint8_t CodecBuffer2[CODEC_BUFFER_SIZE]; -void (* volatile isr_func_CODEC_TIMER_LOADMOD_CCB_VECT)(void) = NULL; void (* volatile isr_func_TCD0_CCC_vect)(void) = NULL; void (* volatile isr_func_CODEC_DEMOD_IN_INT0_VECT)(void) = NULL; +void (* volatile isr_func_ACA_AC0_vect)(void); +void (* volatile isr_func_CODEC_TIMER_LOADMOD_OVF_VECT)(void) = NULL; +void (* volatile isr_func_CODEC_TIMER_LOADMOD_CCA_VECT)(void) = NULL; +void (* volatile isr_func_CODEC_TIMER_LOADMOD_CCB_VECT)(void) = NULL; +void (* volatile isr_func_CODEC_TIMER_TIMESTAMPS_CCA_VECT)(void) = NULL; // the following three functions prevent sending data directly after turning on the reader field void CodecReaderFieldStart(void) { // DO NOT CALL THIS FUNCTION INSIDE APPLICATION! diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index df14ceb2..9a5ad38b 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -38,6 +38,7 @@ #define CODEC_SUBCARRIER_CCEN_PSK TC1_CCAEN_bm #define CODEC_SUBCARRIER_CCEN_OOK TC1_CCBEN_bm #define CODEC_TIMER_SAMPLING TCD0 +#define CODEC_TIMER_SAMPLING_OVF_VECT TCD0_OVF_vect #define CODEC_TIMER_SAMPLING_CCA_VECT TCD0_CCA_vect #define CODEC_TIMER_SAMPLING_CCB_VECT TCD0_CCB_vect #define CODEC_TIMER_SAMPLING_CCC_VECT TCD0_CCC_vect @@ -64,6 +65,7 @@ #define CODEC_THRESHOLD_CALIBRATE_MAX 2048 #define CODEC_THRESHOLD_CALIBRATE_STEPS 16 #define CODEC_TIMER_TIMESTAMPS TCD1 +#define CODEC_TIMER_TIMESTAMPS_OVF_VECT TCD1_OVF_vect #define CODEC_TIMER_TIMESTAMPS_CCA_VECT TCD1_CCA_vect #define CODEC_TIMER_TIMESTAMPS_CCB_VECT TCD1_CCB_vect @@ -80,6 +82,7 @@ #include "Reader14443-2A.h" #include "SniffISO14443-2A.h" #include "ISO15693.h" +#include "SniffISO15693.h" /* Timing definitions for ISO14443A */ #define ISO14443A_SUBCARRIER_DIVIDER 16 @@ -123,9 +126,22 @@ void isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void); extern void (* volatile isr_func_CODEC_DEMOD_IN_INT0_VECT)(void); void isr_ISO14443_2A_TCD0_CCC_vect(void); void isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT(void); +extern void (* volatile isr_func_CODEC_TIMER_LOADMOD_OVF_VECT)(void); +void isr_ISO14443_2A_CODEC_TIMER_LOADMOD_OVF_VECT(void); +void isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_OVF_VECT(void); +extern void (* volatile isr_func_CODEC_TIMER_LOADMOD_CCA_VECT)(void); +void isr_Reader14443_2A_CODEC_TIMER_LOADMOD_CCA_VECT(void); +void isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_CCA_VECT(void); extern void (* volatile isr_func_CODEC_TIMER_LOADMOD_CCB_VECT)(void); void isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT(void); void isr_SniffISO14443_2A_CODEC_TIMER_LOADMOD_CCB_VECT(void); +void isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_CCA_VECT(void); +extern void (* volatile isr_func_CODEC_TIMER_TIMESTAMPS_CCA_VECT)(void); +void isr_Reader14443_2A_CODEC_TIMER_TIMESTAMPS_CCA_VECT(void); +void isr_SNIFF_ISO15693_CODEC_TIMER_TIMESTAMPS_CCA_VECT(void); +extern void (* volatile isr_func_ACA_AC0_vect)(void); +void isr_SniffISO14443_2A_ACA_AC0_VECT(void); +void isr_SNIFF_ISO15693_ACA_AC0_VECT(void); INLINE void CodecInit(void) { ActiveConfiguration.CodecInitFunc(); @@ -166,9 +182,6 @@ INLINE void CodecInitCommon(void) { EVSYS.CH0MUX = CODEC_DEMOD_IN_EVMUX0; EVSYS.CH1MUX = CODEC_DEMOD_IN_EVMUX1; - EVSYS.CH2MUX = CODEC_DEMOD_IN_EVMUX0; - - /* Configure loadmod pin configuration and use a virtual port configuration * for single instruction cycle access */ CODEC_LOADMOD_PORT.DIRSET = CODEC_LOADMOD_MASK; diff --git a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c index 507347e1..62267525 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c @@ -223,7 +223,7 @@ ISR(CODEC_TIMER_SAMPLING_CCA_VECT) { } // Enumulate as a card to send card responds -ISR(CODEC_TIMER_LOADMOD_OVF_VECT) { +ISR_SHARED isr_ISO14443_2A_CODEC_TIMER_LOADMOD_OVF_VECT(void) { /* Bit rate timer. Output a half bit on the output. */ static void *JumpTable[] = { @@ -386,6 +386,7 @@ void ISO14443ACodecInit(void) { isr_func_TCD0_CCC_vect = &isr_Reader14443_2A_TCD0_CCC_vect; isr_func_CODEC_DEMOD_IN_INT0_VECT = &isr_ISO14443_2A_TCD0_CCC_vect; + isr_func_CODEC_TIMER_LOADMOD_OVF_VECT = &isr_ISO14443_2A_CODEC_TIMER_LOADMOD_OVF_VECT; CodecInitCommon(); StartDemod(); } diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.c b/Firmware/Chameleon-Mini/Codec/ISO15693.c index 02214847..db2222d9 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.c @@ -628,7 +628,7 @@ void ISO15693CodecTask(void) { if (DemodByteCount > 0) { LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, DemodByteCount); - LEDHook(LED_CODEC_RX, LED_PULSE); + //LEDHook(LED_CODEC_RX, LED_PULSE); if (CodecBuffer[0] & REQ_SUBCARRIER_DUAL) { bDualSubcarrier = true; diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c index 5eadf5a2..4f929ebb 100644 --- a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c @@ -48,9 +48,10 @@ void Reader14443ACodecInit(void) { /* Initialize common peripherals and start listening * for incoming data. */ CodecInitCommon(); - - // 中断回调 + /* Register shared interrupt handlers */ isr_func_TCD0_CCC_vect = &isr_Reader14443_2A_TCD0_CCC_vect; + isr_func_CODEC_TIMER_LOADMOD_CCA_VECT = &isr_Reader14443_2A_CODEC_TIMER_LOADMOD_CCA_VECT; + isr_func_CODEC_TIMER_TIMESTAMPS_CCA_VECT = &isr_Reader14443_2A_CODEC_TIMER_TIMESTAMPS_CCA_VECT; CodecSetDemodPower(true); CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES - 1; @@ -215,7 +216,7 @@ void Reader14443AMillerEOC(void) { } // EOC of Card->Reader found -ISR(CODEC_TIMER_TIMESTAMPS_CCA_VECT) { // EOC found +ISR_SHARED isr_Reader14443_2A_CODEC_TIMER_TIMESTAMPS_CCA_VECT(void) { // EOC found Reader14443A_EOC(); } @@ -231,7 +232,8 @@ ISR(ACA_AC1_vect) { // this interrupt either finds the SOC or gets triggered bef // according to the pause and modulated period // if the half bit duration is modulated, then add 1 to buffer // if the half bit duration is not modulated, then add 0 to buffer -ISR(CODEC_TIMER_LOADMOD_CCA_VECT) { // pause found +// ISR(CODEC_TIMER_LOADMOD_CCA_VECT) { // pause found +ISR_SHARED isr_Reader14443_2A_CODEC_TIMER_LOADMOD_CCA_VECT(void) { // pause found uint8_t tmp = CODEC_TIMER_TIMESTAMPS.CNTL; CODEC_TIMER_TIMESTAMPS.CNT = 0; diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c index 0fb11287..b6fa1c17 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c @@ -347,8 +347,7 @@ INLINE void Insert1(void) { } // This interrupt find Card -> Reader SOC -ISR(ACA_AC0_vect) { // this interrupt either finds the SOC or gets triggered before - +ISR_SHARED isr_SniffISO14443_2A_ACA_AC0_VECT (void) { // this interrupt either finds the SOC or gets triggered before ACA.AC0CTRL &= ~AC_INTLVL_HI_gc; // disable this interrupt // enable the pause-finding timer CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | TC_EVSEL_CH2_gc; @@ -480,6 +479,7 @@ void Sniff14443ACodecInit(void) { #endif // Common Codec Register settings CodecInitCommon(); + isr_func_ACA_AC0_vect = &isr_SniffISO14443_2A_ACA_AC0_VECT; isr_func_CODEC_TIMER_LOADMOD_CCB_VECT = &isr_SniffISO14443_2A_CODEC_TIMER_LOADMOD_CCB_VECT; // Enable demodulator power CodecSetDemodPower(true); diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c new file mode 100644 index 00000000..b079c112 --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c @@ -0,0 +1,766 @@ +/* + * SniffISO15693.c + * + * Created on: 25.01.2017 + * Author: ceres-c + */ + +#include "SniffISO15693.h" +#include "Codec.h" +#include "../System.h" +#include "../Application/Application.h" +#include "LEDHook.h" +#include "AntennaLevel.h" + +#define VCD_SOC_1_OF_4_CODE 0x7B +#define VCD_SOC_1_OF_256_CODE 0x7E +#define VCD_EOC_CODE 0xDF +#define VICC_SOC_CODE 0b00011101 /* = 0x1D 3 unmodulated periods, 3 modulated periods, 1 unmodulated, 1 modulated */ +#define VICC_EOC_CODE 0b10111000 /* = 0xB8 1 modulated period, 1 unmodulated period, 3 modulated, 3 unmodulated */ + +#define ISO15693_READER_SAMPLE_CLK TC_CLKSEL_DIV2_gc // 13.56MHz +#define ISO15693_READER_SAMPLE_PERIOD 128 // 9.4us + +// These registers provide quick access but are limited +// so global vars will be necessary +#define DataRegister Codec8Reg0 +#define StateRegister Codec8Reg1 +#define ModulationPauseCount Codec8Reg2 +#define BitSampleCount Codec8Reg3 /* Store the amount of received half-bits */ +#define SampleRegister CodecCount16Register1 +#define SampleRegisterH GPIOR4 /* CodecCount16Register1 is the composition of GPIOR4 and GPIOR5, divide them for easier read access */ +#define SampleRegisterL GPIOR5 +#define DemodFloorNoiseLevel CodecCount16Register2 +#define CodecBufferPtr CodecPtrRegister1 + +static volatile struct { + volatile bool ReaderDemodFinished; + volatile bool CardDemodFinished; +} Flags = { 0 }; + +typedef enum { + DEMOD_VCD_SOC_STATE, + DEMOD_VCD_1_OUT_OF_4_STATE, + DEMOD_VCD_1_OUT_OF_256_STATE, + DEMOD_VICC_SOC_STATE, + DEMOD_VICC_DATA, +} DemodSniffStateType; + +static volatile uint8_t ShiftRegister; +static volatile uint8_t ByteCount; +static volatile uint8_t ReaderByteCount; +static volatile uint8_t CardByteCount; +static volatile uint16_t DemodByteCount; +static volatile uint16_t SampleDataCount; + +///////////////////////////////////////////////// +// VCD->VICC +// (Code adapted from ISO15693 codec) +///////////////////////////////////////////////// + +/* This functions resets all global variables used in the codec and enables interrupts to wait for reader data */ +void ReaderSniffInit(void) { + /* Reset global variables to default values */ + CodecBufferPtr = CodecBuffer; + Flags.ReaderDemodFinished = 0; + Flags.CardDemodFinished = 0; + StateRegister = DEMOD_VCD_SOC_STATE; + DataRegister = 0; + SampleRegister = 0; + BitSampleCount = 0; + SampleDataCount = 0; + ModulationPauseCount = 0; + ByteCount = 0; + ReaderByteCount = 0; /* Clear direction-specific counters */ + CardByteCount = 0; + ShiftRegister = 0; + + /* Activate Power for demodulator */ + CodecSetDemodPower(true); + + /* Configure sampling-timer free running and sync to first modulation-pause */ + /* Set clock source for TCD0 to ISO15693_SAMPLE_CLK = TC_CLKSEL_DIV2_gc = System Clock / 2. + * Since the Chameleon is clocked at 13.56*2 MHz (see Makefile), this counter will hit at the same frequency of reader field. + * From 14.12.1 [8331F–AVR–04/2013] + */ + CODEC_TIMER_SAMPLING.CTRLA = ISO15693_READER_SAMPLE_CLK; + /* Set up TCD0 action/source: + * - Event Action: Restart timer (reset PER register) + * - Event Source Select: trigger on CODEC_TIMER_MODSTART_EVSEL = TC_EVSEL_CH0_gc = Event Channel 0 + * From 14.12.4 [8331F–AVR–04/2013] + */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; + /* Resets the sampling counter (TCD0) to 0 - From 14.12.12 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.CNT = 0; + /* Set the period for TCD0 to ISO15693_SAMPLE_PERIOD - 1 - From 14.12.14 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.PER = ISO15693_READER_SAMPLE_PERIOD - 1; + /* Set Compare Channel C (CCC) period to half bit period - 1. (- 14 to compensate ISR timing overhead) - From 14.12.16 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.CCC = ISO15693_READER_SAMPLE_PERIOD / 2 - 14 - 1; + /** + * Temporarily disable Compare Channel C (CCC) interrupts. They'll be enabled later + * in isr_SNIFF_ISO15693_CODEC_DEMOD_READER_IN_INT0_VECT once we sensed the reader + * is sending data and we're in sync with the pulses. + * From 14.12.7 [8331F–AVR–04/2013] + */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + /* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + + + /* Start looking out for modulation pause via interrupt. */ + /* Clear PORTB interrupt flags - From 13.13.13 [8331F–AVR–04/2013] */ + CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0IF_bm; + /* Use PIN1 as a source for modulation pauses. Will trigger PORTB Interrput 0 - From 13.13.11 [8331F–AVR–04/2013] */ + CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; +} + + +/* This function implements CODEC_DEMOD_IN_INT0_VECT interrupt vector. + * It is called when a pulse is detected in CODEC_DEMOD_IN_PORT (PORTB). + * The relevatn interrupt vector is registered to CODEC_DEMOD_IN_MASK0 (PIN1) via: + * CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; + * and unregistered writing the INT0MASK to 0 + */ +ISR_SHARED isr_SNIFF_ISO15693_CODEC_DEMOD_READER_IN_INT0_VECT(void) { + /* Start sample timer CODEC_TIMER_SAMPLING (TCD0). + * Set Counter Channel C (CCC) with relevant bitmask (TC0_CCCIF_bm), + * the period for clock sampling is specified in ReaderSniffInit. + */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + /* Sets register INTCTRLB to TC_CCCINTLVL_HI_gc = (0x03<<4) to enable compare/capture for high level interrupts on Channel C (CCC) */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_HI_gc; + + /* Disable this interrupt as we've already sensed the relevant pulse and will use our internal clock from now on */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; +} + +/* This function is registered to CODEC_TIMER_SAMPLING (TCD0)'s Counter Channel C (CCC). + * When the timer is enabled, this is called on counter's overflow + * + * It demodulates bits received from the reader and saves them in CodecBuffer. + * + * It disables its own interrupt when receives an EOF (calling ISO15693_EOC) or when it receives garbage + */ +ISR_SHARED SNIFF_ISO15693_READER_CODEC_TIMER_SAMPLING_CCC_VECT(void) { + /* Shift demod data */ + SampleRegister = (SampleRegister << 1) | (!(CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK) ? 0x01 : 0x00); + + if (++BitSampleCount == 8) { + BitSampleCount = 0; + switch (StateRegister) { + case DEMOD_VCD_SOC_STATE: + if (SampleRegister == VCD_SOC_1_OF_4_CODE) { + StateRegister = DEMOD_VCD_1_OUT_OF_4_STATE; + SampleDataCount = 0; + ModulationPauseCount = 0; + } else if (SampleRegister == VCD_SOC_1_OF_256_CODE) { + StateRegister = DEMOD_VCD_1_OUT_OF_256_STATE; + SampleDataCount = 0; + } else { // No SOC. Restart and try again, we probably received garbage. + Flags.ReaderDemodFinished = 1; + Flags.CardDemodFinished = 1; /* Mark Card Demod as finished as well to restart clean in CodecTagk (no bytes received -> no log) */ + /* Sets timer off for CODEC_TIMER_SAMPLING (TCD0) disabling clock source */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ + CODEC_TIMER_SAMPLING.INTCTRLB = 0; + } + break; + + case DEMOD_VCD_1_OUT_OF_4_STATE: + if (SampleRegister == VCD_EOC_CODE) { + SNIFF_ISO15693_READER_EOC_VCD(); + } else { + uint8_t SampleData = ~SampleRegister; + if (SampleData == (0x01 << 6)) { + /* ^_^^^^^^ -> 00 */ + ModulationPauseCount++; + DataRegister >>= 2; + + } else if (SampleData == (0x01 << 4)) { + /* ^^^_^^^^ -> 01 */ + ModulationPauseCount++; + DataRegister >>= 2; + DataRegister |= 0b01 << 6; + + } else if (SampleData == (0x01 << 2)) { + /* ^^^^^_^^ -> 10 */ + ModulationPauseCount++; + DataRegister >>= 2; + DataRegister |= 0b10 << 6; + + } else if (SampleData == (0x01 << 0)) { + /* ^^^^^^^_ -> 11 */ + ModulationPauseCount++; + DataRegister >>= 2; + DataRegister |= 0b11 << 6; + } + + if (ModulationPauseCount == 4) { + ModulationPauseCount = 0; + *CodecBufferPtr = DataRegister; + ++CodecBufferPtr; + ++ByteCount; + } + } + break; + + case DEMOD_VCD_1_OUT_OF_256_STATE: + if (SampleRegister == VCD_EOC_CODE) { + SNIFF_ISO15693_READER_EOC_VCD(); + } else { + uint8_t Position = ((SampleDataCount / 2) % 256) - 1; + uint8_t SampleData = ~SampleRegister; + + if (SampleData == (0x01 << 6)) { + /* ^_^^^^^^ -> N-3 */ + DataRegister = Position - 3; + ModulationPauseCount++; + + } else if (SampleData == (0x01 << 4)) { + /* ^^^_^^^^ -> N-2 */ + DataRegister = Position - 2; + ModulationPauseCount++; + + } else if (SampleData == (0x01 << 2)) { + /* ^^^^^_^^ -> N-1 */ + DataRegister = Position - 1; + ModulationPauseCount++; + + } else if (SampleData == (0x01 << 0)) { + /* ^^^^^^^_ -> N-0 */ + DataRegister = Position - 0; + ModulationPauseCount++; + } + + if (ModulationPauseCount == 1) { + ModulationPauseCount = 0; + *CodecBufferPtr = DataRegister; + ++CodecBufferPtr; + ++ByteCount; + } + } + break; + } + SampleRegister = 0; + } + SampleDataCount++; +} + +/* This function is called from isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT + * when we have 8 bits in SampleRegister and they represent an end of frame. + */ +INLINE void SNIFF_ISO15693_READER_EOC_VCD(void) { + /* Mark reader data as received */ + Flags.ReaderDemodFinished = 1; + ReaderByteCount = ByteCount; /* Copy to direction-specific variable */ + + /* Sets timer off for TCD0, disabling clock source. We're done receiving data from reader and don't need to probe the antenna anymore - From 14.12.1 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Disable event action for CODEC_TIMER_SAMPLING (TCD0) - From 14.12.4 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + /* Disable compare/capture interrupts on Channel C - From 14.12.7 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + /* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + + /* And initialize VICC->VCD sniffer */ + CardSniffInit(); +} + +///////////////////////////////////////////////// +// VICC->VCD +///////////////////////////////////////////////// + +/* Initialize card sniffing options +Note: Currently implemented only single subcarrier SOC detection + */ +INLINE void CardSniffInit(void) { + /* Reinit state variables */ + CodecBufferPtr = CodecBuffer2; + ByteCount = 0; + + if (CodecBuffer[0] & ISO15693_REQ_SUBCARRIER_DUAL) { + /** + * No support for dual subcarrier, 0xBAAAAAAD in log to mark unsupported behavior and stop here + */ + *(CodecBufferPtr++) = 0xBA; + *(CodecBufferPtr++) = 0xAA; + *(CodecBufferPtr++) = 0xAA; + *(CodecBufferPtr++) = 0xAD; + ByteCount += 4; + CardByteCount = ByteCount; /* Copy to direction-specific variable */ + + Flags.CardDemodFinished = 1; + return; + } + + /** + * Prepare ADC for antenna field sampling + * Use conversion channel 1 (0 is already used for reader field RSSI sensing), attached to + * CPU pin 42 (DEMOD-READER/2.3C). + * Use conversion channel 2, attached to CPU pin 3 (DEMOD/2.3C). + * + * Values from channel 1 (DEMOD-READER/2.3C) will be used to determine pulses levels. This channel is used + * because the signal shape from DEMOD-READER is easier to analyze with our limited resources and will + * more likely yield useful values. + * Values from channel 2 (DEMOD/2.3C) will be used to identify the first pulse threshold: ADC channel 2 + * is sampling on the same channel where analog comparator is comparing values, thus the value from + * channel 2 is the only useful threshold to identify the first pulse. + */ + ADCA.PRESCALER = ADC_PRESCALER_DIV4_gc; /* Increase ADC clock speed from default setting in AntennaLevel.h */ + ADCA.CTRLB |= ADC_FREERUN_bm; /* Set ADC as free running */ + ADCA.EVCTRL = ADC_SWEEP_012_gc; /* Enable free running sweep on channel 0, 1 and 2 */ + ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc; /* Sample PORTA Pin 2 (DEMOD-READER/2.3C) in channel 1 (same pin the analog comparator is comparing to) */ + ADCA.CH1.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; /* Single-ended input, no gain */ + ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc; /* Sample PORTA Pin 7 (DEMOD/2.3C) in channel 2 */ + ADCA.CH2.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; /* Single-ended input, no gain */ + + /** + * CODEC_READER_TIMER will now be used to timeout VICC sniffing if no data is received before period overflow. + * The overflow interrupt has to be manually deactivated once meaningful data is received, no events will clear it. + * + * PER = maximum card wait time (1500 us). + */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV256_gc; /* Clocked at 1/256 CPU clock */ + CODEC_TIMER_SAMPLING.PER = 160; /* ~1500 us card response timeout */ + CODEC_TIMER_SAMPLING.INTCTRLA = TC_OVFINTLVL_HI_gc; /* Enable overflow (SOF timeout) interrupt */ + CODEC_TIMER_SAMPLING.CTRLFSET = TC_CMD_RESTART_gc; /* Reset timer once it has been configured */ + + /** + * CODEC_TIMER_TIMESTAMPS (TCD1) will be used to count the peaks identified by ACA while sniffing VICC data + * + * CCA = 3 pulses, to update the threshold to a more suitable value for upcoming pulses + */ + CODEC_TIMER_TIMESTAMPS.CTRLA = TC_CLKSEL_EVCH2_gc; /* Using Event channel 2 as an input */ + CODEC_TIMER_TIMESTAMPS.CCA = 3; + CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_HI_gc; /* Enable CCA interrupt */ + CODEC_TIMER_TIMESTAMPS.CTRLFSET = TC_CMD_RESTART_gc; /* Reset counter once it has been configured */ + + /** + * CODEC_TIMER_LOADMOD (TCE0) has multiple usages: + * - Period overflow samples second half-bit (called 37,76 us after bit start) - while decoding data + * - Compare channel A: + * - filters erroneous VICC->VCD SOC identification + * - prepares the codec for data demodulation when a SOF is found + * - Compare channel C samples the first half-bit (called 18,88 us after bit start) - while decoding data + * + * It is restarted on every event on event channel 2 until the first 24 pulses of the SOC have been received, then + * it restarts on period overflow (after one full logic bit). + * + * Interrupt on compare channel A will be called every 64 clock cycles, unless this timer is reset by a new + * pulse. This means that interrupt on CCA will be called after some pulses, be it one or more. + * When CCA is called and we have received too few pulses, it was just noise on the field, so we have to start + * searching again for a SOF. On the other hand, if we received multiple pulses, we can finally start demodulation. + * Also, compare channel A can then be disabled. + * + * Interrupt on compare channel C will be called after the first half-bit and will save it in the samples register. + * + * Overflow interrupt will be called after the second half-bit and will save it in the samples register. + * Every 16 half-bits, this will also convert the samples to a byte and store it in the codec buffer. + * It will also check for EOF to stop card decoding. + * + * PER = one logic bit duration (27,76 us) + * CCA = duration of a single pulse (2,36 us: half hi + half low) + * CCC = half-bit duration (18,88 us) + */ + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_DIV1_gc; /* Clocked at 27.12 MHz */ + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | TC_EVSEL_CH2_gc; /* Restart this timer on every event on channel 2 (pulse detected by AC) */ + CODEC_TIMER_LOADMOD.PER = 1024 - 8; /* One full logic bit duration, slightly shorter on first run to make room for SOC detection algoritm */ + CODEC_TIMER_LOADMOD.PERBUF = 1024 - 1; /* One full logic bit duration - 1 (accommodate for small clock drift) */ + CODEC_TIMER_LOADMOD.CCA = 64; /* Single pulse width */ + CODEC_TIMER_LOADMOD.CCC = 512 - 8; /* Same as PER, use a slightly shorter CCC period on first run */ + CODEC_TIMER_LOADMOD.CCCBUF = 512; /* After first CCC, use actual CCC period (will be written when UPDATE condition is met, thus after first period hit) */ + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; /* Keep overflow interrupt (second half-bit) disabled */ + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCAINTLVL_OFF_gc | TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_OFF_gc; /* Keep all compare interrupt disabled, they will be enabled later on */ + CODEC_TIMER_LOADMOD.CTRLFSET = TC_CMD_RESTART_gc; /* Reset timer */ + + /** + * Get current signal amplitude and record it as "silence" + * + * This can't be moved closer to ADC configuration since the ADC has to be populated with valid + * values and it takes 7*4 clock cycles to do so (7 ADC stages * 4 ADC clock prescaler) + */ + uint32_t accumulator; + accumulator = ADCA.CH2RES - ANTENNA_LEVEL_OFFSET; /* PORTA Pin 7 (DEMOD/2.3C) - CPU pin 7 */ + for (uint8_t i = 0; i < 127; i++) { /* Add 127 more values */ + accumulator += ADCA.CH2RES - ANTENNA_LEVEL_OFFSET; + } + DemodFloorNoiseLevel = (uint16_t)(accumulator >> 7); /* Divide by 2^7=128 */ + /** + * Typical values with my CR95HF reader: + * ReaderFloorNoiseLevel: ~900 (measured on ADCA.CH1RES) + * DemodFloorNoiseLevel: ~1500 + */ + + /* Reconfigure ADC to sample only the first 2 channels from now on (no need for CH2 once the noise level has been found) */ + ADCA.EVCTRL = ADC_SWEEP_01_gc; + + /* Write threshold to the DAC channel 0 (connected to Analog Comparator positive input) */ + DACB.CH0DATA = DemodFloorNoiseLevel + (DemodFloorNoiseLevel >> 3); /* Slightly increase DAC output to ease triggering */ + DACB.CH0DATA |= -( (DACB.CH0DATA & 0xFFF) < DemodFloorNoiseLevel); /* Branchfree saturating addition */ + + /** + * Finally, now that we have the DAC set up, configure analog comparator A (the only one in this MCU) + * channel 0 (the only one that can be connecte to the DAC) to recognize carrier pulses modulated by + * the VICC against the correct threshold coming from the DAC. + */ + ACA.AC0MUXCTRL = AC_MUXPOS_DAC_gc | AC_MUXNEG_PIN7_gc; /* Tigger when DAC signal is above PORTA Pin 7 (DEMOD/2.3C) */ + /* enable AC | high speed mode | large hysteresis | sample on rising edge | high level interrupts */ + /* Hysteresis is not actually needed, but appeared to be working and sounds like it might be more robust */ + ACA.AC0CTRL = AC_ENABLE_bm | AC_HSMODE_bm | AC_HYSMODE_LARGE_gc | AC_INTMODE_RISING_gc | AC_INTLVL_HI_gc; + + /* This function ends ~100 us after last VCD pulse */ +} + +/** + * This function restores all the configured peripherals (ADC/AC/timers) to a clean state + * + * Not inlined, since once we need to call this, there won't be any strict timing constraint + */ +void CardSniffDeinit(void) { + /* Restore ADC settings as per AntennaLevel.h */ + ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc; /* Reduce clock */ + ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc; /* Stop free running ADC */ + ADCA.EVCTRL = 0; /* Stop channel sweep or any ADC event */ + /* Ignore channel 1 and 2 mux settings (no "off" state) */ + + /* Disable all timers interrupts */ + CODEC_TIMER_SAMPLING.INTCTRLA = TC_OVFINTLVL_OFF_gc; + CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCAINTLVL_OFF_gc | TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_OFF_gc; + + /* Stop timer/counters disabling clock sources */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_TIMESTAMPS.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + + /* Reset ACA AC0 to default setting */ + ACA.AC0MUXCTRL = AC_MUXPOS_DAC_gc | AC_MUXNEG_PIN7_gc; /* This actually was unchanged */ + ACA.AC0CTRL = CODEC_AC_DEMOD_SETTINGS; /* Disable interrupt as well */ +} + + +/** + * This interrupt is called on every rising edge sensed by the analog comparator + * and it enables the spurious pulse filtering interrupt (CODEC_TIMER_LOADMOD CCA). + * If we did not receive a SOC but, indeed, noise, this interrupt will be enabled again. + */ +ISR_SHARED isr_SNIFF_ISO15693_ACA_AC0_VECT(void) { + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCAINTLVL_HI_gc; /* Enable level 0 CCA interrupt to filter spurious pulses and find SOC */ + + ACA.AC0CTRL = AC_ENABLE_bm | AC_HSMODE_bm | AC_HYSMODE_LARGE_gc | AC_INTMODE_RISING_gc | AC_INTLVL_OFF_gc; /* Disable this interrupt */ + + DACB.CH0DATA = DemodFloorNoiseLevel + (DemodFloorNoiseLevel >> 2); /* Blindly increase threshold after 1 pulse */ + DACB.CH0DATA |= -( (DACB.CH0DATA & 0xFFF) < DemodFloorNoiseLevel); /* Branchfree saturating addition */ + /* Note: by the time the DAC has changed its output, we're already after the 2nd pulse */ +} + +/** + * This interrupt is called after 3 subcarrier pulses and increases the threshold + */ +ISR_SHARED isr_SNIFF_ISO15693_CODEC_TIMER_TIMESTAMPS_CCA_VECT(void) { + + uint16_t TempRead = ADCA.CH1RES; /* Can't possibly be greater than 0xFFF since the dac has 12 bit res */ + DACB.CH0DATA = TempRead - ANTENNA_LEVEL_OFFSET; /* Further increase DAC output after 3 pulses with value from PORTA Pin 2 (DEMOD-READER/2.3) */ + DACB.CH0DATA &= -(DACB.CH0DATA <= TempRead); /* Branchfree saturating subtraction */ + + CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_OFF_gc; /* Disable this interrupt */ +} + +/** + * This interrupt is called every 64 clock cycles (= once per pulse) unless the timer is reset. + * This means it will be invoked only if, after a pulse, we don't receive another one. + * + * This will either find a noise or the end of the SOC. If we've received noise, most likely, + * we received few pulses. The soc is made of 24 pulses, so if VICC modulation actually started, + * we should have a relevant number of pulses + */ +ISR_SHARED isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_CCA_VECT(void) { + if (CODEC_TIMER_TIMESTAMPS.CNT < 15) { + /* We most likely received garbage */ + + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCAINTLVL_OFF_gc; /* Disable all compare interrupts, including this one */ + + DACB.CH0DATA = DemodFloorNoiseLevel + (DemodFloorNoiseLevel >> 3); /* Restore DAC output (AC negative comparation threshold) to pre-AC0 interrupt update value */ + DACB.CH0DATA |= -( (DACB.CH0DATA & 0xFFF) < DemodFloorNoiseLevel); /* Branchfree saturating addition */ + ACA.AC0CTRL |= AC_INTLVL_HI_gc; /* Re-enable analog comparator interrupt to search for another pulse */ + + CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_HI_gc; /* Re enable CCA interrupt in case it was triggered and then is now disabled */ + } else { + /** + * Received a lot of pulses, we can assume it was the SOF, prepare interrupts for data demodulation. + * Data demodulation interrupt will take care of checking if we receive the two remaining half-bits + * of the sof and will then convalidate that these pulses were actually part of the SOF + */ + + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCCINTLVL_HI_gc; /* Enable CCC (first half-bit decoding), disable CCA (spurious pulse timeout) */ + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_HI_gc; /* Enable overflow (second half-bit decoding) interrupt */ + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; /* Disable timer resetting on every AC0 event */ + + /** + * Prepare registers for data demodulation + * This is a bit like cheating. We assumed this is a real SOC, so we can pretend we've already received + * the first 3 unmodulated and 3 modulated periods (binary: 0b000111) which compose the SOC. + * The two upcoming half-bits (0b01) will be shifted in during data decoding. + */ + SampleRegister = 0b000111; /* = 0x7 */ + BitSampleCount = 4 + 3; /* Pretend we've already received 8 empty half-bits and the above 6 half-bits */ + StateRegister = DEMOD_VICC_SOC_STATE; + } + + CODEC_TIMER_TIMESTAMPS.CNT = 0; /* Clear pulses counter nonetheless */ +} + +/** + * This interrupt is called on the first half-bit + */ +ISR(CODEC_TIMER_LOADMOD_CCC_VECT) { + /** + * This interrupt is called on every odd half-bit, thus we don't need to do any check, + * just append to the sample register. + */ + SampleRegister = (SampleRegister << 1) | (CODEC_TIMER_TIMESTAMPS.CNT > 4); /* Using 4 as a discriminating factor to allow for slight errors in pulses counting. */ + /* Don't increase BitSampleCount, since this is only the first half of a bit */ + + CODEC_TIMER_TIMESTAMPS.CNT = 0; /* Clear count register for next half-bit */ +} + +/** + * This interrupt handles the VICC SOF timeout when the card does not answer + * and restarts reader sniffing + */ +ISR(CODEC_TIMER_SAMPLING_OVF_VECT) { + Flags.CardDemodFinished = 1; + + /* Call cleanup function */ + CardSniffDeinit(); +} + +/** + * This interrupt is called on the second bit-half to decode it. + * It replaces isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_OVF_VECT_timeout once the SOF is received + */ +ISR_SHARED isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_OVF_VECT(void) { + /** + * This interrupt is called on every even half-bit, we then need to check the content of the register + */ + SampleRegister = (SampleRegister << 1) | (CODEC_TIMER_TIMESTAMPS.CNT > 4); /* Using 4 as a discriminating factor to allow for slight errors in pulses counting. */ + BitSampleCount++; + CODEC_TIMER_TIMESTAMPS.CNT = 0; /* Clear count register for next half-bit */ + + // char tmpBuf[10]; + // snprintf(tmpBuf, 10, "BSC: %d\n", BitSampleCount); + // TerminalSendString(tmpBuf); + + if (BitSampleCount == 8) { /* We have 16 half-bits in SampleRegister at this point */ + BitSampleCount = 0; + + switch (StateRegister) { + case DEMOD_VICC_SOC_STATE: + if (SampleRegister == VICC_SOC_CODE) { + /* We've actually received a SOF after the previous (mostly unknown) train of pulses */ + CODEC_TIMER_SAMPLING.INTCTRLA = TC_OVFINTLVL_OFF_gc; /* Disable VICC SOF timeout handler */ + StateRegister = DEMOD_VICC_DATA; + } else { + /* No SOC. The train of pulses was actually garbage. */ + + /* Restore DAC output to intial value */ + DACB.CH0DATA = DemodFloorNoiseLevel + (DemodFloorNoiseLevel >> 3); + DACB.CH0DATA |= -( (DACB.CH0DATA & 0xFFF) < DemodFloorNoiseLevel); /* Branchfree saturating addition */ + + /* Re enable AC interrupt */ + ACA.AC0CTRL = AC_ENABLE_bm | AC_HSMODE_bm | AC_HYSMODE_LARGE_gc | AC_INTMODE_RISING_gc | AC_INTLVL_HI_gc; + + /* Restore other interrupts as per original setup (same as CardSniffInit) */ + CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_HI_gc; /* Re enable 3rd pulse thresh update */ + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; /* Disable overflow interrupt (second half-bit) */ + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCAINTLVL_OFF_gc | TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_OFF_gc; /* Disable all compare channel interrupts */ + + /* Reset timers */ + CODEC_TIMER_TIMESTAMPS.CTRLFSET = TC_CMD_RESTART_gc; /* Restart peak counter */ + CODEC_TIMER_LOADMOD.CTRLFSET = TC_CMD_RESTART_gc; /* Reset pulse train timer */ + + /* Reset periods which were possibly modified */ + CODEC_TIMER_LOADMOD.PER = 1024 - 8; /* One full logic bit duration, slightly shorter on first run to make room for SOC detection algoritm */ + CODEC_TIMER_LOADMOD.PERBUF = 1024 - 1; /* One full logic bit duration - 1 (accommodate for small clock drift) */ + CODEC_TIMER_LOADMOD.CCC = 512 - 8; /* Same as PER, use a slightly shorter CCC period on first run */ + CODEC_TIMER_LOADMOD.CCCBUF = 512; /* After first CCC, use actual CCC period (will be written when UPDATE condition is met, thus after first period hit) */ + + /* No need to (dis|en)able CODEC_TIMER_SAMPLING OVF interrupt (we're still waiting for a valid SOF and that interrupt wasn't disabled) */ + } + break; + + case DEMOD_VICC_DATA: + if (SampleRegisterL == VICC_EOC_CODE) { + Flags.CardDemodFinished = 1; + + CardByteCount = ByteCount; /* Copy to direction-specific variable */ + + /* Call cleanup function */ + CardSniffDeinit(); + } else if ( + (SampleRegisterL & 0b00000011) == 0b00000011 || /* Ugly, I know */ + (SampleRegisterL & 0b00000011) == 0b00000000 || + (SampleRegisterL & 0b00001100) == 0b00001100 || + (SampleRegisterL & 0b00001100) == 0b00000000 || + (SampleRegisterL & 0b00110000) == 0b00110000 || + (SampleRegisterL & 0b00110000) == 0b00000000 || + (SampleRegisterL & 0b11000000) == 0b11000000 || + (SampleRegisterL & 0b11000000) == 0b00000000 || + (SampleRegisterH & 0b00000011) == 0b00000011 || + (SampleRegisterH & 0b00000011) == 0b00000000 || + (SampleRegisterH & 0b00001100) == 0b00001100 || + (SampleRegisterH & 0b00001100) == 0b00000000 || + (SampleRegisterH & 0b00110000) == 0b00110000 || + (SampleRegisterH & 0b00110000) == 0b00000000 || + (SampleRegisterH & 0b11000000) == 0b11000000 || + (SampleRegisterH & 0b11000000) == 0b00000000 + ) { + /** + * Check for invalid data coding (11 or 00 bit-halfs). + * Write 0x0BADDA7A in log to mark broken frame and stop here: we have + * no knowledge about where the EOF could be + */ + + *(CodecBufferPtr++) = 0x0B; + *(CodecBufferPtr++) = 0xAD; + *(CodecBufferPtr++) = 0xDA; + *(CodecBufferPtr++) = 0x7A; + ByteCount += 4; + + Flags.CardDemodFinished = 1; + + CardByteCount = ByteCount; /* Copy to direction-specific variable */ + + /* Call cleanup function */ + CardSniffDeinit(); + + } else { + DataRegister = ( (SampleRegisterL & 0b00000011) == 0b00000001) << 3; + DataRegister |= ( (SampleRegisterL & 0b00001100) == 0b00000100) << 2; + DataRegister |= ( (SampleRegisterL & 0b00110000) == 0b00010000) << 1; + DataRegister |= ( (SampleRegisterL & 0b11000000) == 0b01000000); /* Bottom 2 bits */ + DataRegister |= ( (SampleRegisterH & 0b00000011) == 0b00000001) << 7; + DataRegister |= ( (SampleRegisterH & 0b00001100) == 0b00000100) << 6; + DataRegister |= ( (SampleRegisterH & 0b00110000) == 0b00010000) << 5; + DataRegister |= ( (SampleRegisterH & 0b11000000) == 0b01000000) << 4; + + *CodecBufferPtr = DataRegister; + CodecBufferPtr++; + ByteCount++; + if (ByteCount == 256) { + /* Somehow missed the EOF and running out of buffer */ + Flags.CardDemodFinished = 1; + + /* Call cleanup function */ + CardSniffDeinit(); + }; + } + break; + } + } + +} + + +///////////////////////////////////////////////// +// External interface +///////////////////////////////////////////////// + +void SniffISO15693CodecInit(void) { + CodecInitCommon(); + + /** + * Register function handlers to shared ISR + */ + /* CODEC_TIMER_SAMPLING (TCD0)'s Counter Channel C (CCC) - Reader demod */ + isr_func_TCD0_CCC_vect = &SNIFF_ISO15693_READER_CODEC_TIMER_SAMPLING_CCC_VECT; + /* CODEC_DEMOD_IN_PORT (PORTB) interrupt 0 - Reader demod */ + isr_func_CODEC_DEMOD_IN_INT0_VECT = &isr_SNIFF_ISO15693_CODEC_DEMOD_READER_IN_INT0_VECT; + /* CODEC_TIMER_TIMESTAMPS (TCD1)'s Counter Channel C (CCC) - Card demod */ + isr_func_CODEC_TIMER_TIMESTAMPS_CCA_VECT = &isr_SNIFF_ISO15693_CODEC_TIMER_TIMESTAMPS_CCA_VECT; /* Updated threshold after 3 pulses */ + /* CODEC_TIMER_LOADMOD (TCE0)'s Counter Channel A (CCA) and overflow (OVF) - Card demod */ + isr_func_CODEC_TIMER_LOADMOD_CCA_VECT = &isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_CCA_VECT; /* Spurious pulses and SOC detection */ + /* CCC (first half-bit decoder) ISR is not shared, thus doesn't need to be assigned here */ + isr_func_CODEC_TIMER_LOADMOD_OVF_VECT = &isr_SNIFF_ISO15693_CODEC_TIMER_LOADMOD_OVF_VECT; /* Second half-bit decoder */ + /* Analog comparator A (ACA) channel 0 interrupt handler - Card demod */ + isr_func_ACA_AC0_vect = &isr_SNIFF_ISO15693_ACA_AC0_VECT; /* Detect first card pulse and enable demod interrupts */ + + /** + * Route events from the analog comparator (which will be configured later) on the event system + * using channel 2 (channel 0 and 1 are used by the antenna). + * These events will be counted by CODEC_TIMER_TIMESTAMPS and will (sometimes) + * reset CODEC_TIMER_LOADMOD. + */ + EVSYS.CH2MUX = EVSYS_CHMUX_ACA_CH0_gc; /* Route analog comparator channel 0 events on event system channel 2 */ + + /* Change DAC reference source to Internal 1V (same as ADC source) */ + DACB.CTRLC = DAC_REFSEL_INT1V_gc; + + ReaderSniffInit(); +} + +void SniffISO15693CodecDeInit(void) { + /* Gracefully shutdown codec */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; + + /* Reset global variables to default values */ + CodecBufferPtr = CodecBuffer; + Flags.ReaderDemodFinished = 0; + Flags.CardDemodFinished = 0; + StateRegister = DEMOD_VCD_SOC_STATE; + DataRegister = 0; + SampleRegister = 0; + BitSampleCount = 0; + SampleDataCount = 0; + ModulationPauseCount = 0; + ByteCount = 0; + ShiftRegister = 0; + + /* Disable sample timer */ + /* Sets timer off for CODEC_TIMER_SAMPLING (TCD0) disabling clock source */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Disable event action for CODEC_TIMER_SAMPLING (TCD0) */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + /* Sets register INTCTRLB to TC_CCCINTLVL_OFF_gc = (0x00<<4) to disable compare/capture C interrupts */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + /* Restore Counter Channel C (CCC) interrupt mask (TC0_CCCIF_bm) */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + + CodecSetDemodPower(false); + + /* Stop AC0 events routing on channel 2 */ + EVSYS.CH2MUX = EVSYS_CHMUX_OFF_gc; + + /* Restore default DAC reference to Codec.h setting */ + DACB.CTRLC = DAC_REFSEL_AVCC_gc; +} + +void SniffISO15693CodecTask(void) { + if (Flags.ReaderDemodFinished) { + Flags.ReaderDemodFinished = 0; + + DemodByteCount = ReaderByteCount; + + if (DemodByteCount > 0) { + LogEntry(LOG_INFO_CODEC_SNI_READER_DATA, CodecBuffer, DemodByteCount); + LEDHook(LED_CODEC_RX, LED_PULSE); + } + + } + + if(Flags.CardDemodFinished) { + Flags.CardDemodFinished = 0; + + DemodByteCount = CardByteCount; + + if (DemodByteCount > 0) { + LogEntry(LOG_INFO_CODEC_SNI_CARD_DATA, CodecBuffer2, DemodByteCount); + LEDHook(LED_CODEC_RX, LED_PULSE); + + } + + ReaderSniffInit(); + } +} diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO15693.h b/Firmware/Chameleon-Mini/Codec/SniffISO15693.h new file mode 100644 index 00000000..ef7d1d2c --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/SniffISO15693.h @@ -0,0 +1,41 @@ +/* + * SniffISO15693.h + * + * Created on: 05.11.2019 + * Author: ceres-c + */ + +#ifndef SNIFF_ISO15693_H_ +#define SNIFF_ISO15693_H_ + +#include "Terminal/CommandLine.h" + +#define ISO15693_APP_NO_RESPONSE 0x0000 + +#define SUBCARRIER_1 32 +#define SUBCARRIER_2 28 +#define SUBCARRIER_OFF 0 +#define SOF_PATTERN 0x1D +#define EOF_PATTERN 0xB8 + +//Used when checking the request sent from the reader +#define ISO15693_REQ_SUBCARRIER_SINGLE 0x00 +#define ISO15693_REQ_SUBCARRIER_DUAL 0x01 +#define ISO15693_REQ_DATARATE_LOW 0x00 +#define ISO15693_REQ_DATARATE_HIGH 0x02 + +/* Codec Interface */ +void SniffISO15693CodecInit(void); +void SniffISO15693CodecDeInit(void); +void SniffISO15693CodecTask(void); + +/* Application Interface */ +void SniffISO15693CodecStart(void); +void SniffISO15693CodecReset(void); + +/* Internal functions */ +INLINE void SNIFF_ISO15693_READER_EOC_VCD(void); +INLINE void CardSniffInit(void); + + +#endif /* SNIFF_ISO15693_H_ */ diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index 74f89dab..e8a3ff83 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -411,14 +411,14 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { #endif #ifdef CONFIG_ISO15693_SNIFF_SUPPORT [CONFIG_ISO15693_SNIFF] = { - .CodecInitFunc = ISO15693CodecInit, - .CodecDeInitFunc = ISO15693CodecDeInit, - .CodecTaskFunc = ISO15693CodecTask, - .ApplicationInitFunc = ApplicationInitDummy, - .ApplicationResetFunc = ApplicationResetDummy, - .ApplicationTaskFunc = ApplicationTaskDummy, - .ApplicationTickFunc = ApplicationTickDummy, - .ApplicationProcessFunc = ApplicationProcessDummy, + .CodecInitFunc = SniffISO15693CodecInit, + .CodecDeInitFunc = SniffISO15693CodecDeInit, + .CodecTaskFunc = SniffISO15693CodecTask, + .ApplicationInitFunc = SniffISO15693AppInit, + .ApplicationResetFunc = SniffISO15693AppReset, + .ApplicationTaskFunc = SniffISO15693AppTask, + .ApplicationTickFunc = SniffISO15693AppTick, + .ApplicationProcessFunc = SniffISO15693AppProcess, .ApplicationGetUidFunc = ApplicationGetUidDummy, .ApplicationSetUidFunc = ApplicationSetUidDummy, .UidSize = 0, diff --git a/Firmware/Chameleon-Mini/ISRSharing.S b/Firmware/Chameleon-Mini/ISRSharing.S index 7bf3dfc3..a1d197cb 100644 --- a/Firmware/Chameleon-Mini/ISRSharing.S +++ b/Firmware/Chameleon-Mini/ISRSharing.S @@ -44,6 +44,22 @@ CODEC_DEMOD_IN_INT0_VECT: CODEC_TIMER_SAMPLING_CCC_VECT: call_isr isr_func_TCD0_CCC_vect +.global CODEC_TIMER_LOADMOD_OVF_VECT +CODEC_TIMER_LOADMOD_OVF_VECT: + call_isr isr_func_CODEC_TIMER_LOADMOD_OVF_VECT + +.global CODEC_TIMER_LOADMOD_CCA_VECT +CODEC_TIMER_LOADMOD_CCA_VECT: + call_isr isr_func_CODEC_TIMER_LOADMOD_CCA_VECT + .global CODEC_TIMER_LOADMOD_CCB_VECT CODEC_TIMER_LOADMOD_CCB_VECT: - call_isr isr_func_CODEC_TIMER_LOADMOD_CCB_VECT \ No newline at end of file + call_isr isr_func_CODEC_TIMER_LOADMOD_CCB_VECT + +.global CODEC_TIMER_TIMESTAMPS_CCA_VECT +CODEC_TIMER_TIMESTAMPS_CCA_VECT: + call_isr isr_func_CODEC_TIMER_TIMESTAMPS_CCA_VECT + +.global ACA_AC0_vect +ACA_AC0_vect: + call_isr isr_func_ACA_AC0_vect diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index cc7184eb..7325d984 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -21,7 +21,7 @@ SETTINGS += -DCONFIG_NTAG215_SUPPORT SETTINGS += -DCONFIG_VICINITY_SUPPORT SETTINGS += -DCONFIG_SL2S2002_SUPPORT SETTINGS += -DCONFIG_TITAGITSTANDARD_SUPPORT -# SETTINGS += -DCONFIG_ISO15693_SNIFF_SUPPORT +SETTINGS += -DCONFIG_ISO15693_SNIFF_SUPPORT SETTINGS += -DCONFIG_EM4233_SUPPORT #Support magic mode on mifare classic configuration @@ -112,9 +112,9 @@ OPTIMIZATION = s SRC += Chameleon-Mini.c LUFADescriptors.c System.c ISRSharing.S Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c Uart.c uartcmd.c SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/SniffISO14443-2A.c Codec/Reader14443-ISR.S -SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c Application/CryptoTDEA.S -SRC += Codec/ISO15693.c -SRC += Application/Vicinity.c Application/Sl2s2002.c Application/TITagitstandard.c Application/ISO15693-A.c Application/EM4233.c Application/NTAG215.c +SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c Application/CryptoTDEA.S Application/NTAG215.c +SRC += Codec/ISO15693.c Codec/SniffISO15693.c +SRC += Application/Vicinity.c Application/Sl2s2002.c Application/TITagitstandard.c Application/ISO15693-A.c Application/EM4233.c Application/Sniff15693.c SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) LUFA_PATH = ../LUFA CC_FLAGS = -flto -DUSE_LUFA_CONFIG_HEADER -DFLASH_DATA_ADDR=$(FLASH_DATA_ADDR) -DFLASH_DATA_SIZE=$(FLASH_DATA_SIZE) -DSPM_HELPER_ADDR=$(SPM_HELPER_ADDR) -DBUILD_DATE=$(BUILD_DATE) -DCOMMIT_ID=\"$(COMMIT_ID)\" $(SETTINGS) @@ -135,7 +135,7 @@ AVRDUDE_FLAGS = -p $(AVRDUDE_MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) AVRDUDE_WRITE_APP_LATEST = -U application:w:Latest/Chameleon-RevG.hex AVRDUDE_WRITE_EEPROM_LATEST = -U eeprom:w:Latest/Chameleon-RevG.eep -.PHONY: program program-latest dfu-flip dfu-prog check_size style +.PHONY: program program-latest dfu-flip dfu-prog dfu-reset check_size style # Default target all: @@ -188,6 +188,10 @@ dfu-prog: $(TARGET).hex $(TARGET).eep check_size dfu-programmer $(MCU) flash $(TARGET).hex dfu-programmer $(MCU) reset +# Reset the device after firmware upgrade +dfu-reset: + dfu-programmer $(MCU) reset + check_size: @{ \ set -e; \ From 89dde5c6094248a004ce8a0aaae37f47b25c7828 Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Fri, 28 Jan 2022 01:10:27 +0100 Subject: [PATCH 2/5] Printing unknown log entries hex code --- Software/Chameleon/Log.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Software/Chameleon/Log.py b/Software/Chameleon/Log.py index 524ecab4..f26d404f 100644 --- a/Software/Chameleon/Log.py +++ b/Software/Chameleon/Log.py @@ -71,7 +71,8 @@ def binaryParityDecoder(data): 0x45: { 'name': 'CODEC RX SNI READER W/PARITY', 'decoder': binaryParityDecoder }, 0x46: { 'name': 'CODEC RX SNI CARD', 'decoder': binaryDecoder }, 0x47: { 'name': 'CODEC RX SNI CARD W/PARITY', 'decoder': binaryParityDecoder }, - + 0x48: { 'name': 'CODEC RX SNI READER FIELD DETECTED', 'decoder': noDecoder }, + 0x80: { 'name': 'APP READ', 'decoder': binaryDecoder }, 0x81: { 'name': 'APP WRITE', 'decoder': binaryDecoder }, @@ -95,7 +96,7 @@ def binaryParityDecoder(data): } TIMESTAMP_MAX = 65536 -eventTypes = { i : ({'name': 'UNKNOWN', 'decoder': binaryDecoder} if i not in eventTypes.keys() else eventTypes[i]) for i in range(256) } +eventTypes = { i : ({'name': f'UNKNOWN {hex(i)}', 'decoder': binaryDecoder} if i not in eventTypes.keys() else eventTypes[i]) for i in range(256) } def parseBinary(binaryStream, decoder=None): log = [] @@ -130,11 +131,11 @@ def parseBinary(binaryStream, decoder=None): logData = eventTypes[event]['decoder'](logData) # Calculate delta timestamp respecting 16 bit overflow - deltaTimestamp = timestamp - lastTimestamp; + deltaTimestamp = timestamp - lastTimestamp lastTimestamp = timestamp if (deltaTimestamp < 0): - deltaTimestamp += TIMESTAMP_MAX; + deltaTimestamp += TIMESTAMP_MAX note = "" # If we need to decode the data and paritybit check success From 3b53376b560f165682de7e76dbe8af731d2354da Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Fri, 28 Jan 2022 12:16:17 +0100 Subject: [PATCH 3/5] Ported fptrs fixes --- Firmware/Chameleon-Mini/Codec/ISO15693.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.c b/Firmware/Chameleon-Mini/Codec/ISO15693.c index db2222d9..02214847 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.c @@ -628,7 +628,7 @@ void ISO15693CodecTask(void) { if (DemodByteCount > 0) { LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, DemodByteCount); - //LEDHook(LED_CODEC_RX, LED_PULSE); + LEDHook(LED_CODEC_RX, LED_PULSE); if (CodecBuffer[0] & REQ_SUBCARRIER_DUAL) { bDualSubcarrier = true; From d3299fee436263f012bf4e76aa14a2600ca114aa Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Mon, 31 Jan 2022 12:54:03 +0100 Subject: [PATCH 4/5] Calling AppProcess as suggested by @cacke-r --- .../Chameleon-Mini/Application/Sniff14443A.c | 14 +++++++------- Firmware/Chameleon-Mini/Codec/Codec.c | 2 ++ Firmware/Chameleon-Mini/Codec/Codec.h | 2 ++ .../Chameleon-Mini/Codec/SniffISO14443-2A.c | 6 ++---- .../Chameleon-Mini/Codec/SniffISO14443-2A.h | 1 - Firmware/Chameleon-Mini/Codec/SniffISO15693.c | 19 ++++++++++--------- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/Sniff14443A.c b/Firmware/Chameleon-Mini/Application/Sniff14443A.c index 5d8b559a..b03aaf9a 100644 --- a/Firmware/Chameleon-Mini/Application/Sniff14443A.c +++ b/Firmware/Chameleon-Mini/Application/Sniff14443A.c @@ -67,7 +67,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { case STATE_REQA: LED_PORT.OUTCLR = LED_RED; // If received Reader REQA or WUPA - if (TrafficSource == TRAFFIC_READER && + if (SniffTrafficSource == TRAFFIC_READER && (Buffer[0] == 0x26 || Buffer[0] == 0x52)) { SniffState = STATE_ATQA; } else { @@ -76,7 +76,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { break; case STATE_ATQA: // ATQA: P RRRR XXXX P XXRX XXXX - if (TrafficSource == TRAFFIC_CARD && + if (SniffTrafficSource == TRAFFIC_CARD && BitCount == 2 * 9 && (Buffer[0] & 0x20) == 0x00 && // Bit6 RFU shall be 0 (Buffer[1] & 0xE0) == 0x00 && // bit13-16 RFU shall be 0 @@ -87,7 +87,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { } else { // If not ATQA, but REQA, then stay on this state, // Reset to REQA, save the counter and reset the counter - if (TrafficSource == TRAFFIC_READER && + if (SniffTrafficSource == TRAFFIC_READER && (Buffer[0] == 0x26 || Buffer[0] == 0x52)) { } else { // If not ATQA and not REQA then reset to REQA @@ -97,7 +97,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { break; case STATE_ANTICOLLI: // SEL: 93/95/97 - if (TrafficSource == TRAFFIC_READER && + if (SniffTrafficSource == TRAFFIC_READER && BitCount == 2 * 8 && (Buffer[0] & 0xf0) == 0x90 && (Buffer[0] & 0x09) == 0x01) { @@ -108,7 +108,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { } break; case STATE_UID: - if (TrafficSource == TRAFFIC_CARD && + if (SniffTrafficSource == TRAFFIC_CARD && BitCount == 5 * 9 && checkParityBits(Buffer, BitCount)) { SniffState = STATE_SELECT; @@ -119,7 +119,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { case STATE_SELECT: // SELECT: 9 bytes, SEL = 93/95/97, NVB=70 - if (TrafficSource == TRAFFIC_READER && + if (SniffTrafficSource == TRAFFIC_READER && BitCount == 9 * 8 && (Buffer[0] & 0xf0) == 0x90 && (Buffer[0] & 0x09) == 0x01 && @@ -132,7 +132,7 @@ uint16_t Sniff14443AAppProcess(uint8_t *Buffer, uint16_t BitCount) { break; case STATE_SAK: // SAK: 1Byte SAK + CRC - if (TrafficSource == TRAFFIC_CARD && + if (SniffTrafficSource == TRAFFIC_CARD && BitCount == 3 * 9 && checkParityBits(Buffer, BitCount)) { if ((Buffer[0] & 0x04) == 0x00) { diff --git a/Firmware/Chameleon-Mini/Codec/Codec.c b/Firmware/Chameleon-Mini/Codec/Codec.c index 1523cb10..194d90d4 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.c +++ b/Firmware/Chameleon-Mini/Codec/Codec.c @@ -26,6 +26,8 @@ static volatile struct { uint8_t CodecBuffer[CODEC_BUFFER_SIZE]; uint8_t CodecBuffer2[CODEC_BUFFER_SIZE]; +enum RCTraffic SniffTrafficSource; + void (* volatile isr_func_TCD0_CCC_vect)(void) = NULL; void (* volatile isr_func_CODEC_DEMOD_IN_INT0_VECT)(void) = NULL; void (* volatile isr_func_ACA_AC0_vect)(void); diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index 9a5ad38b..0005d8cf 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -119,6 +119,8 @@ typedef enum { extern uint8_t CodecBuffer[CODEC_BUFFER_SIZE]; extern uint8_t CodecBuffer2[CODEC_BUFFER_SIZE]; +extern enum RCTraffic {TRAFFIC_READER, TRAFFIC_CARD} SniffTrafficSource; + /* Shared ISR pointers and handlers */ extern void (* volatile isr_func_TCD0_CCC_vect)(void); void isr_Reader14443_2A_TCD0_CCC_vect(void); diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c index b6fa1c17..2a3d683b 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c @@ -56,8 +56,6 @@ static volatile uint16_t ReaderBitCount; static volatile uint16_t CardBitCount; static volatile uint16_t rawBitCount; -enum RCTraffic TrafficSource; - INLINE void CardSniffInit(void); INLINE void CardSniffDeinit(void); @@ -510,7 +508,7 @@ void Sniff14443ACodecTask(void) { // Let the Application layer know where this data comes from LEDHook(LED_CODEC_RX, LED_PULSE); - TrafficSource = TRAFFIC_READER; + SniffTrafficSource = TRAFFIC_READER; ApplicationProcess(CodecBuffer, ReaderBitCount); } @@ -523,7 +521,7 @@ void Sniff14443ACodecTask(void) { LEDHook(LED_CODEC_RX, LED_PULSE); // Let the Application layer know where this data comes from - TrafficSource = TRAFFIC_CARD; + SniffTrafficSource = TRAFFIC_CARD; ApplicationProcess(CodecBuffer2, CardBitCount); } diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.h b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.h index b8616b0c..8e9080a2 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.h +++ b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.h @@ -9,7 +9,6 @@ #include "Codec.h" #include "Terminal/CommandLine.h" -extern enum RCTraffic {TRAFFIC_READER, TRAFFIC_CARD} TrafficSource; /* Codec Interface */ void Sniff14443ACodecInit(void); void Sniff14443ACodecDeInit(void); diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c index b079c112..2962f4e5 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c @@ -50,7 +50,6 @@ static volatile uint8_t ShiftRegister; static volatile uint8_t ByteCount; static volatile uint8_t ReaderByteCount; static volatile uint8_t CardByteCount; -static volatile uint16_t DemodByteCount; static volatile uint16_t SampleDataCount; ///////////////////////////////////////////////// @@ -741,11 +740,12 @@ void SniffISO15693CodecTask(void) { if (Flags.ReaderDemodFinished) { Flags.ReaderDemodFinished = 0; - DemodByteCount = ReaderByteCount; - - if (DemodByteCount > 0) { - LogEntry(LOG_INFO_CODEC_SNI_READER_DATA, CodecBuffer, DemodByteCount); + if (ReaderByteCount > 0) { + LogEntry(LOG_INFO_CODEC_SNI_READER_DATA, CodecBuffer, ReaderByteCount); LEDHook(LED_CODEC_RX, LED_PULSE); + + SniffTrafficSource = TRAFFIC_READER; + ApplicationProcess(CodecBuffer, ReaderByteCount); } } @@ -753,12 +753,13 @@ void SniffISO15693CodecTask(void) { if(Flags.CardDemodFinished) { Flags.CardDemodFinished = 0; - DemodByteCount = CardByteCount; - - if (DemodByteCount > 0) { - LogEntry(LOG_INFO_CODEC_SNI_CARD_DATA, CodecBuffer2, DemodByteCount); + if (CardByteCount > 0) { + LogEntry(LOG_INFO_CODEC_SNI_CARD_DATA, CodecBuffer2, CardByteCount); LEDHook(LED_CODEC_RX, LED_PULSE); + SniffTrafficSource = TRAFFIC_CARD; + ApplicationProcess(CodecBuffer2, CardByteCount); + /* Note: the application might want to know if the frame is broken, need to extern bBrokenFrame */ } ReaderSniffInit(); From 59f68ac4c9137c418cbe0c8b8b6db743e97c1406 Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Tue, 1 Feb 2022 18:07:20 +0100 Subject: [PATCH 5/5] Fixed check-size in makefile and updated readme --- Firmware/Chameleon-Mini/Makefile | 10 +++++----- README.md | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index 7325d984..91903503 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -18,11 +18,11 @@ SETTINGS += -DCONFIG_MF_ULTRALIGHT_SUPPORT SETTINGS += -DCONFIG_ISO14443A_SNIFF_SUPPORT SETTINGS += -DCONFIG_ISO14443A_READER_SUPPORT SETTINGS += -DCONFIG_NTAG215_SUPPORT -SETTINGS += -DCONFIG_VICINITY_SUPPORT -SETTINGS += -DCONFIG_SL2S2002_SUPPORT -SETTINGS += -DCONFIG_TITAGITSTANDARD_SUPPORT +# SETTINGS += -DCONFIG_VICINITY_SUPPORT +# SETTINGS += -DCONFIG_SL2S2002_SUPPORT +# SETTINGS += -DCONFIG_TITAGITSTANDARD_SUPPORT +# SETTINGS += -DCONFIG_EM4233_SUPPORT SETTINGS += -DCONFIG_ISO15693_SNIFF_SUPPORT -SETTINGS += -DCONFIG_EM4233_SUPPORT #Support magic mode on mifare classic configuration SETTINGS += -DSUPPORT_MF_CLASSIC_MAGIC_MODE @@ -198,7 +198,7 @@ check_size: if [ ! -f $(TARGET).elf ]; then \ exit 0; \ fi; \ - PROGMEM_SIZE=$$(avr-size --mcu=atxmega128a4u --format=avr $(TARGET).elf | grep -oP "\d+" | sed -n 3p); \ + PROGMEM_SIZE=$$(avr-size --mcu=atxmega128a4u --format=avr $(TARGET).elf | grep -oP "Program:\W+(\d+)" | grep -oP "\d+"); \ MAX_PRGMEM_SIZE=$$(printf "%d\n" $(FLASH_DATA_ADDR)); \ if [ $$PROGMEM_SIZE -gt $$MAX_PRGMEM_SIZE ]; then \ echo "make: *** $(TARGET).elf Application Section size $$PROGMEM_SIZE excedes maximum allowed $$MAX_PRGMEM_SIZE. Please disable some features in Makefile"; \ diff --git a/README.md b/README.md index 78a744d0..f3acdd9f 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ MF_DETECTION_1K|Detecting reader to obtain keys|MFKEY32V2|List results directly| MF_DETECTION_4K|Detecting reader to obtain keys|MFKEY32V2|List results directly| ISO14443A_READER|Reader Mode|-|Display UID| ISO14443A_SNIFF|Sniffing|-|Not supported| -ISO15693_SNIFF|Sniffing|-|Not supported| +ISO15693_SNIFF|Sniffing|-|Supported| **4. Button Custom Function Introduction** @@ -296,7 +296,8 @@ Sniff Mode NTAG|ISO14443A 106 kbit/s|Support|Support| No LEGIC prime|LEGICprime/ ISO14443A/ ISO15693|Possible but not supported|Possible but not supported|No| HID iCLASS|125kHz/ISO15693/ISO14443B|Possible but not supported|Possible but not supported|No| Epass|ISO14443A/B|Supported / Supported|Low rate only / not supported|No| -ISO15693|ISO15693|Support|Support|No| +TiTagIT Standard|ISO15693|Support|Support|Support| +EM4233|ISO15693|Support|Support|Support| (2)Sniff Mode Support Type @@ -305,7 +306,8 @@ ISO15693|ISO15693|Support|Support|No| |Encoding type|Whether the hardware supports|Does the software support| Whether the application layer supports|Note| | ------------------- |:-------------------:| -------------------:| ------------------- |-------------------:| Non-13.56MHz|Not Supported|Not Supported|Not Supported| -ISO 14443 A 106 kbit/s|Reader -> card Direction sniffing|Maybe support the other direction|Currently only supported Reader -> card Direction sniffing |Support| +ISO 14443 A 106 kbit/s|Reader -> card Direction sniffing|Maybe support the other direction|Currently only supported Reader -> card Direction sniffing || +ISO 15693|Support|Support|Support|Single subcarrier only| (3) Card Type Supported via Reading