From b1f0da0c331c74a5c2321c15e6dc4ead4ebd5531 Mon Sep 17 00:00:00 2001 From: georg Date: Thu, 31 Aug 2017 14:00:59 +0200 Subject: [PATCH] added ISO15693 codec --- .../Chameleon-Mini/Application/Application.h | 2 + .../Chameleon-Mini/Application/ISO15693-A.c | 52 ++ .../Chameleon-Mini/Application/ISO15693-A.h | 104 ++++ .../Chameleon-Mini/Application/Vicinity.c | 132 ++++ .../Chameleon-Mini/Application/Vicinity.h | 27 + Firmware/Chameleon-Mini/Codec/Codec.h | 12 +- Firmware/Chameleon-Mini/Codec/ISO14443-2A.c | 6 +- Firmware/Chameleon-Mini/Codec/ISO15693.c | 575 ++++++++++++++++++ Firmware/Chameleon-Mini/Codec/ISO15693.h | 47 ++ Firmware/Chameleon-Mini/Configuration.c | 20 + Firmware/Chameleon-Mini/Configuration.h | 3 + Firmware/Chameleon-Mini/Makefile | 5 +- 12 files changed, 978 insertions(+), 7 deletions(-) create mode 100644 Firmware/Chameleon-Mini/Application/ISO15693-A.c create mode 100644 Firmware/Chameleon-Mini/Application/ISO15693-A.h create mode 100644 Firmware/Chameleon-Mini/Application/Vicinity.c create mode 100644 Firmware/Chameleon-Mini/Application/Vicinity.h create mode 100644 Firmware/Chameleon-Mini/Codec/ISO15693.c create mode 100644 Firmware/Chameleon-Mini/Codec/ISO15693.h diff --git a/Firmware/Chameleon-Mini/Application/Application.h b/Firmware/Chameleon-Mini/Application/Application.h index 0b94960..b732796 100644 --- a/Firmware/Chameleon-Mini/Application/Application.h +++ b/Firmware/Chameleon-Mini/Application/Application.h @@ -16,6 +16,8 @@ #include "MifareUltralight.h" #include "MifareClassic.h" #include "Reader14443A.h" +#include "Vicinity.h" + /* Function wrappers */ INLINE void ApplicationInit(void) { diff --git a/Firmware/Chameleon-Mini/Application/ISO15693-A.c b/Firmware/Chameleon-Mini/Application/ISO15693-A.c new file mode 100644 index 0000000..0f7f3d3 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/ISO15693-A.c @@ -0,0 +1,52 @@ +#include "ISO15693-A.h" +#include "../Common.h" +#include + +//Refer to ISO/IEC 15693-3:2001 page 41 +uint16_t calculateCRC(void* FrameBuf, uint16_t FrameBufSize) +{ + uint16_t reg = ISO15693_CRC16_PRESET; + uint8_t i, j; + + uint8_t *DataPtr = (uint8_t *)FrameBuf; + + for(i = 0; i < FrameBufSize; i++) { + reg = reg ^ *DataPtr++; + for (j = 0; j < 8; j++) { + if (reg & 0x0001) { + reg = (reg >> 1) ^ ISO15693_CRC16_POLYNORMAL; + } else { + reg = (reg >> 1); + } + } + } + + return ~reg; +} + +void ISO15693AppendCRC(uint8_t* FrameBuf, uint16_t FrameBufSize) +{ + uint16_t crc; + + crc = calculateCRC(FrameBuf, FrameBufSize); + + uint8_t crcLb = crc & 0xFF; + uint8_t crcHb = crc >> 8; + + + FrameBuf[FrameBufSize] = crcLb; + FrameBuf[FrameBufSize + 1] = crcHb; +} + +bool ISO15693CheckCRC(void* FrameBuf, uint16_t FrameBufSize) +{ + uint16_t crc; + uint8_t *DataPtr = (uint8_t *)FrameBuf; + + crc = calculateCRC(DataPtr, FrameBufSize); + + uint8_t crcLb = crc & 0xFF; + uint8_t crcHb = crc >> 8; + + return (DataPtr[FrameBufSize] == crcLb && DataPtr[FrameBufSize + 1] == crcHb); +} diff --git a/Firmware/Chameleon-Mini/Application/ISO15693-A.h b/Firmware/Chameleon-Mini/Application/ISO15693-A.h new file mode 100644 index 0000000..2a40482 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/ISO15693-A.h @@ -0,0 +1,104 @@ +/* + * ISO15693-3.h + * + * Created on: 24.08.2013 + * Author: skuser + */ + +#ifndef ISO15693_3_H_ +#define ISO15693_3_H_ + +#include "../Common.h" + +#define ISO15693_CMD_INVENTORY 0x01 +#define ISO15693_CMD_STAY_QUIET 0x02 +#define ISO15693_CMD_READ_SINGLE 0x20 +#define ISO15693_CMD_WRITE_SINGLE 0x21 +#define ISO15693_CMD_LOCK_BLOCK 0x22 +#define ISO15693_CMD_READ_MULTIPLE 0x23 +#define ISO15693_CMD_WRITE_MULTIPLE 0x24 +#define ISO15693_CMD_SELECT 0x25 +#define ISO15693_CMD_RESET_TO_READY 0x26 +#define ISO15693_CMD_WRITE_AFI 0x27 +#define ISO15693_CMD_LOCK_AFI 0x28 +#define ISO15693_CMD_WRITE_DSFID 0x29 +#define ISO15693_CMD_LOCK_DSFID 0x2A +#define ISO15693_CMD_GET_SYS_INFO 0x2B +#define ISO15693_CMD_GET_BLOCK_SEC 0x2C + +#define ISO15693_REQ_FLAG_SUBCARRIER 0x01 +#define ISO15693_REQ_FLAG_DATARATE 0x02 +#define ISO15693_REQ_FLAG_INVENTORY 0x04 +#define ISO15693_REQ_FLAG_PROT_EXT 0x08 +#define ISO15693_REQ_FLAG_OPTION 0x40 +#define ISO15693_REQ_FLAG_RFU 0x80 +/* When INVENTORY flag is not set: */ +#define ISO15693_REQ_FLAG_SELECT 0x10 +#define ISO15693_REQ_FLAG_ADDRESS 0x20 +/* When INVENTORY flag is set: */ +#define ISO15693_REQ_FLAG_AFI 0x10 +#define ISO15693_REQ_FLAG_NB_SLOTS 0x20 + +#define ISO15693_RES_FLAG_ERROR 0x01 +#define ISO15693_RES_FLAG_PROT_EXT 0x08 + + +#define ISO15693_MIN_FRAME_SIZE 5 + +#define ISO15693_CRC16_SIZE 2 /* Bytes */ +#define ISO15693_CRC16_POLYNORMAL 0x8408 +#define ISO15693_CRC16_PRESET 0xFFFF + +typedef uint8_t ISO15693UidType[8]; + +void ISO15693AppendCRC(uint8_t* FrameBuf, uint16_t FrameBufSize); +bool ISO15693CheckCRC(void* FrameBuf, uint16_t FrameBufSize); + +INLINE +bool ISO15693CompareUid(uint8_t* Uid1, uint8_t* Uid2) +{ + if ( (Uid1[0] == Uid2[0]) + && (Uid1[1] == Uid2[1]) + && (Uid1[2] == Uid2[2]) + && (Uid1[3] == Uid2[3]) + && (Uid1[4] == Uid2[4]) + && (Uid1[5] == Uid2[5]) + && (Uid1[6] == Uid2[6]) + && (Uid1[7] == Uid2[7]) ) { + return true; + } else { + return false; + } +} + +INLINE +void ISO15693CopyUid(uint8_t* DstUid, uint8_t* SrcUid) +{ + DstUid[0] = SrcUid[0]; + DstUid[1] = SrcUid[1]; + DstUid[2] = SrcUid[2]; + DstUid[3] = SrcUid[3]; + DstUid[4] = SrcUid[4]; + DstUid[5] = SrcUid[5]; + DstUid[6] = SrcUid[6]; + DstUid[7] = SrcUid[7]; +} + +INLINE +bool ISO15693Addressed(uint8_t* Buffer, uint8_t* MyUid) { + if (Buffer[0] & ISO15693_REQ_FLAG_ADDRESS) { + /* Addressed mode */ + if ( ISO15693CompareUid(&Buffer[2], MyUid) ) { + /* Our UID addressed */ + return true; + } else { + /* Our UID not addressed */ + return false; + } + } else { + /* Non-Addressed mode */ + return true; + } +} + +#endif /* ISO15693_3_H_ */ diff --git a/Firmware/Chameleon-Mini/Application/Vicinity.c b/Firmware/Chameleon-Mini/Application/Vicinity.c new file mode 100644 index 0000000..7a3a961 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/Vicinity.c @@ -0,0 +1,132 @@ +/* + * ISO15693.c + * + * Created on: 01-03-2017 + * Author: Phillip Nash + */ + + +#include "Vicinity.h" +#include "../Codec/ISO15693.h" +#include "../Memory.h" +#include "Crypto1.h" +#include "../Random.h" +#include "ISO15693-A.h" + + +static enum { + STATE_READY, + STATE_SELECTED, + STATE_QUIET +} State; + +ISO15693UidType MyUid = {0x60, 0x0B, 0x88, 0x77, 0x06, 0x44, 0x02, 0xE1}; + + + +void VicinityAppInit(void) +{ + State = STATE_READY; +} + +void VicinityAppReset(void) +{ + State = STATE_READY; +} + + +void VicinityAppTask(void) +{ + +} + +void VicinityAppTick(void) +{ + + +} + +void sendIntToTermina(uint8_t val) +{ + char buf[16]; + snprintf(buf,16, "%d,",val); + TerminalSendString(buf); +} + +uint16_t VicinityAppProcess(uint8_t* FrameBuf, uint16_t FrameBytes) +{ + if (FrameBytes >= ISO15693_MIN_FRAME_SIZE) { + if(ISO15693CheckCRC(FrameBuf, FrameBytes - ISO15693_CRC16_SIZE)) { + // At this point, we have a valid ISO15693 frame + uint8_t Command = FrameBuf[1]; + uint16_t ResponseByteCount = ISO15693_APP_NO_RESPONSE; + + + switch(State) { + case STATE_READY: + if (Command == ISO15693_CMD_INVENTORY) { + FrameBuf[0] = 0x00; /* Flags */ + FrameBuf[1] = 0x00; /* DSFID */ + ISO15693CopyUid(&FrameBuf[2], MyUid); + ResponseByteCount = 10; + } else if (Command == ISO15693_CMD_STAY_QUIET) { + if (ISO15693Addressed(FrameBuf, MyUid)) { + State = STATE_QUIET; + } + } else if (Command == ISO15693_CMD_GET_SYS_INFO) { + if (ISO15693Addressed(FrameBuf, MyUid)) { + FrameBuf[0] = 0; /* Flags */ + FrameBuf[1] = 0; /* InfoFlags */ + ISO15693CopyUid(&FrameBuf[2], MyUid); + ResponseByteCount = 10; + } + } + break; + + case STATE_SELECTED: + + break; + + case STATE_QUIET: + if (Command == ISO15693_CMD_RESET_TO_READY) { + if (ISO15693Addressed(FrameBuf, MyUid)) { + FrameBuf[0] = 0; + ResponseByteCount = 1; + State = STATE_READY; + } + } + break; + + default: + break; + } + + if (ResponseByteCount > 0) { + /* There is data to be sent. Append CRC */ + ISO15693AppendCRC(FrameBuf, ResponseByteCount); + ResponseByteCount += ISO15693_CRC16_SIZE; + } + + return ResponseByteCount; + + } else { // Invalid CRC + return ISO15693_APP_NO_RESPONSE; + } + } else { // Min frame size not met + return ISO15693_APP_NO_RESPONSE; + } + +} + +void VicinityGetUid(ConfigurationUidType Uid) +{ + memcpy(Uid, MyUid, sizeof(ConfigurationUidType)); +} + +void VicinitySetUid(ConfigurationUidType Uid) +{ + memcpy(MyUid, Uid, sizeof(ConfigurationUidType)); +} + + + diff --git a/Firmware/Chameleon-Mini/Application/Vicinity.h b/Firmware/Chameleon-Mini/Application/Vicinity.h new file mode 100644 index 0000000..797ff01 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/Vicinity.h @@ -0,0 +1,27 @@ +/* + * MifareClassic.h + * + * Created on: 01.03.2017 + * Author: skuser + */ + +#ifndef VICINITY_H_ +#define VICINITY_H_ + +#include "Application.h" +#include "ISO15693-A.h" + +#define ISO15693_GENERIC_UID_SIZE 8 //ISO15693_UID_SIZE +#define ISO15693_GENERIC_MEM_SIZE 8192 //ISO15693_MAX_MEM_SIZE + +void VicinityAppInit(void); +void VicinityAppReset(void); +void VicinityAppTask(void); +void VicinityAppTick(void); +uint16_t VicinityAppProcess(uint8_t* FrameBuf, uint16_t FrameBytes); +void VicinityGetUid(ConfigurationUidType Uid); +void VicinitySetUid(ConfigurationUidType Uid); + + + +#endif /* VICINITY_H_ */ diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index cf9d67d..4324126 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -17,6 +17,7 @@ #include "ISO14443-2A.h" #include "Reader14443-2A.h" +#include "ISO15693.h" /* Timing definitions for ISO14443A */ #define ISO14443A_SUBCARRIER_DIVIDER 16 @@ -38,6 +39,7 @@ #define CODEC_DEMOD_IN_EVMUX0 EVSYS_CHMUX_PORTB_PIN1_gc #define CODEC_DEMOD_IN_EVMUX1 EVSYS_CHMUX_PORTB_PIN2_gc #define CODEC_DEMOD_IN_INT0_VECT PORTB_INT0_vect +#define CODEC_DEMOD_IN_INT1_VECT PORTB_INT1_vect #define CODEC_LOADMOD_PORT PORTC #define CODEC_LOADMOD_MASK PIN6_bm #define CODEC_CARRIER_IN_PORT PORTC @@ -56,7 +58,8 @@ #define CODEC_SUBCARRIER_CCEN_OOK TC1_CCBEN_bm #define CODEC_TIMER_SAMPLING TCD0 #define CODEC_TIMER_SAMPLING_CCA_VECT TCD0_CCA_vect -#define CODEC_TIMER_SAMPLING_CCB_VECT TCD0_CCB_vect +#define CODEC_TIMER_SAMPLING_CCB_VECT TCD0_CCB_vect +#define CODEC_TIMER_SAMPLING_CCC_VECT TCD0_CCC_vect #define CODEC_TIMER_LOADMOD TCE0 #define CODEC_TIMER_LOADMOD_OVF_VECT TCE0_OVF_vect #define CODEC_TIMER_LOADMOD_CCA_VECT TCE0_CCA_vect @@ -140,7 +143,7 @@ INLINE void CodecInitCommon(void) CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL0 = PORT_ISC_RISING_gc; CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL1 = PORT_ISC_FALLING_gc; CODEC_DEMOD_IN_PORT.INT0MASK = 0; - CODEC_DEMOD_IN_PORT.INTCTRL = PORT_INT0LVL_HI_gc; + CODEC_DEMOD_IN_PORT.INTCTRL = PORT_INT0LVL_HI_gc | PORT_INT1LVL_HI_gc; EVSYS.CH0MUX = CODEC_DEMOD_IN_EVMUX0; EVSYS.CH1MUX = CODEC_DEMOD_IN_EVMUX1; @@ -201,6 +204,11 @@ INLINE void CodecSetSubcarrier(SubcarrierModType ModType, uint16_t Divider) } } +INLINE void CodecChangeDivider(uint16_t Divider) +{ + CODEC_SUBCARRIER_TIMER.PER = Divider - 1; +} + INLINE void CodecStartSubcarrier(void) { CODEC_SUBCARRIER_TIMER.CTRLA = CODEC_TIMER_CARRIER_CLKSEL; diff --git a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c index e43ea61..ba91594 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c @@ -76,10 +76,10 @@ static void StartDemod(void) { /* Start looking out for modulation pause via interrupt. */ CODEC_DEMOD_IN_PORT.INTFLAGS = 0x03; - CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; + CODEC_DEMOD_IN_PORT.INT1MASK = CODEC_DEMOD_IN_MASK0; } -ISR(CODEC_DEMOD_IN_INT0_VECT) { +ISR(CODEC_DEMOD_IN_INT1_VECT) { /* This is the first edge of the first modulation-pause after StartDemod. * Now we have time to start * demodulating beginning from one bit-width after this edge. */ @@ -109,7 +109,7 @@ ISR(CODEC_DEMOD_IN_INT0_VECT) { CODEC_TIMER_LOADMOD.CTRLA = CODEC_TIMER_CARRIER_CLKSEL; /* Disable this interrupt */ - CODEC_DEMOD_IN_PORT.INT0MASK = 0; + CODEC_DEMOD_IN_PORT.INT1MASK = 0; } ISR(CODEC_TIMER_SAMPLING_CCA_VECT) { diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.c b/Firmware/Chameleon-Mini/Codec/ISO15693.c new file mode 100644 index 0000000..ac4d99a --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.c @@ -0,0 +1,575 @@ +/* + * ISO15693.c + * + * Created on: 25.01.2017 + * Author: Phillip Nash + */ + +#include "ISO15693.h" +#include "../System.h" +#include "../Application/Application.h" +#include "LEDHook.h" +#include "AntennaLevel.h" +#include "Terminal/Terminal.h" +#include +#include +#include + + +#define SOC_1_OF_4_CODE 0x7B +#define SOC_1_OF_256_CODE 0x7E +#define EOC_CODE 0xDF +#define ISO15693_SAMPLE_CLK TC_CLKSEL_DIV2_gc // 13.56MHz +#define ISO15693_SAMPLE_PERIOD 128 // 9.4us + +#define WRITE_GRID_CYCLES 4096 +#define SUBCARRIER_1 32 +#define SUBCARRIER_2 28 +#define SUBCARRIER_OFF 0 +#define SOF_PATTERN 0x1D +#define EOF_PATTERN 0xB8 + +// These registers provide quick access but are limited +// so global vars will be necessary +#define DataRegister Codec8Reg0 +#define StateRegister Codec8Reg1 +#define ModulationPauseCount Codec8Reg2 +#define SampleRegister Codec8Reg3 +#define BitSent CodecCount16Register1 +#define BitSampleCount CodecCount16Register2 +#define CodecBufferPtr CodecPtrRegister1 + +static volatile struct { + volatile bool DemodFinished; + volatile bool LoadmodFinished; + volatile bool DemodAborted; +} Flags = { 0 }; + +typedef enum { + DEMOD_SOC_STATE, + DEMOD_1_OUT_OF_4_STATE, + DEMOD_1_OUT_OF_256_STATE +} DemodStateType; + +static volatile enum { + LOADMOD_WAIT, + + /* Single subcarrier */ + LOADMOD_START_SINGLE, + LOADMOD_SOF_SINGLE, + LOADMOD_BIT0_SINGLE, + LOADMOD_BIT1_SINGLE, + LOADMOD_EOF_SINGLE, + + /* Dual subcarrier */ + LOADMOD_START_DUAL, + LOADMOD_SOF_DUAL, + LOADMOD_BIT0_DUAL, + LOADMOD_BIT1_DUAL, + LOADMOD_EOF_DUAL, + + LOADMOD_FINISHED +} LoadModState; + +static volatile DemodStateType DemodState; +static volatile uint8_t ShiftRegister; +static volatile uint8_t ByteCount; +static volatile uint16_t BitRate1; +static volatile uint16_t BitRate2; +static volatile uint16_t SampleDataCount; +static volatile uint16_t ReadCommandFromReader = 0; + +#ifdef CONFIG_VICINITY_SUPPORT + +// Started when a single pulse has been detected +ISR(CODEC_DEMOD_IN_INT0_VECT) { + // Start sample timer + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_HI_gc; + /* Disable this interrupt */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; +} + +INLINE void ISO15693_EOC(void) +{ + BitRate1 = 256 * 4; + if (CodecBuffer[0] & ISO15693_REQ_DATARATE_HIGH) + BitRate1 = 256; + + if (CodecBuffer[0] & ISO15693_REQ_SUBCARRIER_DUAL) + { + BitRate2 = 252 * 4; + if (CodecBuffer[0] & ISO15693_REQ_DATARATE_HIGH) + BitRate2 = 252; + } else { + BitRate2 = BitRate1; + } + + CODEC_TIMER_LOADMOD.CTRLD = 0; + CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm; + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_HI_gc; + CODEC_TIMER_LOADMOD.PERBUF = BitRate1 - 1; + + Flags.DemodFinished = 1; + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = 0; +} + +ISR(CODEC_TIMER_SAMPLING_CCC_VECT) // Reading data sent from the reader +{ + /* Shift demod data */ + SampleRegister = (SampleRegister << 1) | (!(CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK) ? 0x01 : 0x00); + + if (++BitSampleCount == 8) + { + BitSampleCount = 0; + switch (DemodState) + { + case DEMOD_SOC_STATE: + if (SampleRegister == SOC_1_OF_4_CODE) + { + DemodState = DEMOD_1_OUT_OF_4_STATE; + SampleDataCount = 0; + ModulationPauseCount = 0; + } else if (SampleRegister == SOC_1_OF_256_CODE) { + DemodState = DEMOD_1_OUT_OF_256_STATE; + SampleDataCount = 0; + } else { // No SOC. Restart and try again + Flags.DemodFinished = 1; + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = 0; + } + break; + + case DEMOD_1_OUT_OF_4_STATE: + if (SampleRegister == EOC_CODE) + { + ISO15693_EOC(); + } 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_1_OUT_OF_256_STATE: + if (SampleRegister == EOC_CODE) + { + ISO15693_EOC(); + } 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; + } + } + SampleDataCount++; +} + + +ISR(CODEC_TIMER_LOADMOD_CCB_VECT) +{ + static void* JumpTable[] = { + [LOADMOD_START_SINGLE] = &&LOADMOD_START_SINGLE_LABEL, + [LOADMOD_SOF_SINGLE] = &&LOADMOD_SOF_SINGLE_LABEL, + [LOADMOD_BIT0_SINGLE] = &&LOADMOD_BIT0_SINGLE_LABEL, + [LOADMOD_BIT1_SINGLE] = &&LOADMOD_BIT1_SINGLE_LABEL, + [LOADMOD_EOF_SINGLE] = &&LOADMOD_EOF_SINGLE_LABEL, + [LOADMOD_START_DUAL] = &&LOADMOD_START_DUAL_LABEL, + [LOADMOD_SOF_DUAL] = &&LOADMOD_SOF_DUAL_LABEL, + [LOADMOD_BIT0_DUAL] = &&LOADMOD_BIT0_DUAL_LABEL, + [LOADMOD_BIT1_DUAL] = &&LOADMOD_BIT1_DUAL_LABEL, + [LOADMOD_EOF_DUAL] = &&LOADMOD_EOF_DUAL_LABEL, + [LOADMOD_FINISHED] = &&LOADMOD_FINISHED_LABEL + }; + + if ( (StateRegister >= LOADMOD_START_SINGLE) && (StateRegister <= LOADMOD_FINISHED) ) { + goto *JumpTable[StateRegister]; + } else { + return; + } + + LOADMOD_START_SINGLE_LABEL: + CodecStartSubcarrier(); + ShiftRegister = SOF_PATTERN; + BitSent = 0; + // fallthrough + LOADMOD_SOF_SINGLE_LABEL: + if (ShiftRegister & 0x80) { + CodecSetLoadmodState(true); + } else { + CodecSetLoadmodState(false); + } + + ShiftRegister <<= 1; + BitSent++; + + if ( (BitSent % 8) == 0) { + /* Last SOF bit has been put out. Start sending out data */ + StateRegister = LOADMOD_BIT0_SINGLE; + ShiftRegister = (*CodecBufferPtr++); + } else { + StateRegister = LOADMOD_SOF_SINGLE; + } + return; + + LOADMOD_BIT0_SINGLE_LABEL: //Manchester encoding + + if (ShiftRegister & 0x01) { + /* Deactivate carrier */ + CodecSetLoadmodState(false); + } else { + /* Activate carrier */ + CodecSetLoadmodState(true); + } + + StateRegister = LOADMOD_BIT1_SINGLE; + return; + + LOADMOD_BIT1_SINGLE_LABEL: //Manchester encoding + if (ShiftRegister & 0x01) { + CodecSetLoadmodState(true); + } else { + CodecSetLoadmodState(false); + } + + ShiftRegister >>= 1; + BitSent++; + + StateRegister = LOADMOD_BIT0_SINGLE; + + if ( (BitSent % 8) == 0 ) { + /* Byte boundary */ + if (--ByteCount == 0) { + /* No more data left */ + ShiftRegister = EOF_PATTERN; + StateRegister = LOADMOD_EOF_SINGLE; + } else { + ShiftRegister = (*CodecBufferPtr++); + } + } + return; + + LOADMOD_EOF_SINGLE_LABEL: //End of Manchester encoding + + /* Output EOF */ + if (ShiftRegister & 0x80) { + CodecSetLoadmodState(true); + } else { + CodecSetLoadmodState(false); + } + + ShiftRegister <<= 1; + BitSent++; + + if ( (BitSent % 8) == 0) { + /* Last bit has been put out */ + StateRegister = LOADMOD_FINISHED; + } else { + StateRegister = LOADMOD_EOF_SINGLE; + } + return; + + + + + + + + LOADMOD_START_DUAL_LABEL: + CodecStartSubcarrier(); + ShiftRegister = SOF_PATTERN; + BitSent = 0; + CodecSetLoadmodState(true); + // fallthrough + LOADMOD_SOF_DUAL_LABEL: + if (ShiftRegister & 0x80) { + CodecChangeDivider(SUBCARRIER_1); + CODEC_TIMER_LOADMOD.PER = BitRate1 - 1; + } else { + CodecChangeDivider(SUBCARRIER_2); + CODEC_TIMER_LOADMOD.PER = BitRate2 - 1; + } + + ShiftRegister <<= 1; + BitSent++; + + if ( (BitSent % 8) == 0) { + /* Last SOF bit has been put out. Start sending out data */ + StateRegister = LOADMOD_BIT0_DUAL; + ShiftRegister = (*CodecBufferPtr++); + } else { + StateRegister = LOADMOD_BIT0_DUAL; + } + return; + + LOADMOD_BIT0_DUAL_LABEL: //Manchester encoding + + if (ShiftRegister & 0x01) { + /* fc / 32 */ + CodecChangeDivider(SUBCARRIER_1); + CODEC_TIMER_LOADMOD.PER = BitRate1 - 1; + } else { + /* fc / 28 */ + CodecChangeDivider(SUBCARRIER_2); + CODEC_TIMER_LOADMOD.PER = BitRate2 - 1; + } + + StateRegister = LOADMOD_BIT1_DUAL; + return; + + LOADMOD_BIT1_DUAL_LABEL: //Manchester encoding + if (ShiftRegister & 0x01) { + CodecChangeDivider(SUBCARRIER_2); + CODEC_TIMER_LOADMOD.PER = BitRate2 - 1; + } else { + CodecChangeDivider(SUBCARRIER_1); + CODEC_TIMER_LOADMOD.PER = BitRate1 - 1; + } + + ShiftRegister >>= 1; + BitSent++; + + StateRegister = LOADMOD_BIT0_DUAL; + + if ( (BitSent % 8) == 0 ) { + /* Byte boundary */ + if (--ByteCount == 0) { + /* No more data left */ + ShiftRegister = EOF_PATTERN; + StateRegister = LOADMOD_EOF_DUAL; + } else { + ShiftRegister = (*CodecBufferPtr++); + } + } + return; + + LOADMOD_EOF_DUAL_LABEL: //End of Manchester encoding + + /* Output EOF */ + if (ShiftRegister & 0x80) { + CodecChangeDivider(SUBCARRIER_1); + CODEC_TIMER_LOADMOD.PER = BitRate1 - 1; + } else { + CodecChangeDivider(SUBCARRIER_2); + CODEC_TIMER_LOADMOD.PER = BitRate2 - 1; + } + + ShiftRegister <<= 1; + BitSent++; + + if ( (BitSent % 8) == 0) { + /* Last bit has been put out */ + StateRegister = LOADMOD_FINISHED; + } else { + StateRegister = LOADMOD_EOF_DUAL; + } + return; + + + + + + + + LOADMOD_FINISHED_LABEL: + + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLB = 0; + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, SUBCARRIER_1); + Flags.LoadmodFinished = 1; + return; +} + +#endif /* CONFIG_VICINITY_SUPPORT */ + +void StartISO15693Demod(void) { + + CodecBufferPtr = CodecBuffer; + Flags.DemodFinished = 0; + Flags.LoadmodFinished = 0; + DemodState = DEMOD_SOC_STATE; + DataRegister = 0; + SampleRegister = 0; + BitSampleCount = 0; + SampleDataCount = 0; + ModulationPauseCount = 0; + ByteCount = 0; + ShiftRegister = 0; + + CodecSetDemodPower(true); + + /* Configure sampling-timer free running and sync to first modulation-pause. */ + CODEC_TIMER_SAMPLING.CNT = 0; + CODEC_TIMER_SAMPLING.PER = ISO15693_SAMPLE_PERIOD - 1; + CODEC_TIMER_SAMPLING.CCC = ISO15693_SAMPLE_PERIOD / 2 - 14 - 1; /* Half bit. ISR compensate*/ + CODEC_TIMER_SAMPLING.CTRLA = ISO15693_SAMPLE_CLK; + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; + CODEC_TIMER_LOADMOD.PER = 4192 + 128 + 128 - 1; + CODEC_TIMER_LOADMOD.INTCTRLA = 0; + CODEC_TIMER_LOADMOD.INTCTRLB = 0; + CODEC_TIMER_LOADMOD.CTRLA = ISO15693_SAMPLE_CLK; + + /* Start looking out for modulation pause via interrupt. */ + CODEC_DEMOD_IN_PORT.INTFLAGS = 0x03; + CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; +} + +void ISO15693CodecInit(void) +{ + CodecInitCommon(); + StartISO15693Demod(); +} + +void ISO15693CodecDeInit(void) +{ + /* Gracefully shutdown codec */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; + + CodecBufferPtr = CodecBuffer; + Flags.DemodFinished = 0; + Flags.LoadmodFinished = 0; + DemodState = DEMOD_SOC_STATE; + DataRegister = 0; + SampleRegister = 0; + BitSampleCount = 0; + SampleDataCount = 0; + ModulationPauseCount = 0; + ByteCount = 0; + ShiftRegister = 0; + + //Disable sample timer + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + + //Disable load modulation + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc; + CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm; + + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); + CodecSetDemodPower(false); + CodecSetLoadmodState(false); +} + +void ISO15693CodecTask(void) +{ + if (Flags.DemodFinished) { + Flags.DemodFinished = 0; + + uint16_t DemodByteCount = ByteCount; + uint16_t AppReceivedByteCount = 0; + bool bDualSubcarrier = false; + + if (DemodByteCount > 0) + { + if (CodecBuffer[0] & ISO15693_REQ_SUBCARRIER_DUAL) + { + bDualSubcarrier = true; + } + LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, DemodByteCount); + AppReceivedByteCount = ApplicationProcess(CodecBuffer, DemodByteCount); + + } else { + ApplicationReset(); + } + + //This is only reached when we've received a valid frame + if (AppReceivedByteCount > 0) { + LogEntry(LOG_INFO_CODEC_TX_DATA, CodecBuffer, AppReceivedByteCount); + CodecBufferPtr = CodecBuffer; + ByteCount = AppReceivedByteCount; + + CodecStartSubcarrier(); + + /* Start loadmodulating */ + if (bDualSubcarrier) { + StateRegister = LOADMOD_START_DUAL; + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_2); + } else { + StateRegister = LOADMOD_START_SINGLE; + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_1); + } + + } else { + /* No data to be processed. Disable T1 waiting and + * start listening again */ + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc; + StartISO15693Demod(); + } + } + + if (Flags.LoadmodFinished) { + Flags.LoadmodFinished = 0; + /* Load modulation has been finished. Stop it and start to listen + * for incoming data again. */ + StartISO15693Demod(); + } +} diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.h b/Firmware/Chameleon-Mini/Codec/ISO15693.h new file mode 100644 index 0000000..93162d7 --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.h @@ -0,0 +1,47 @@ +/* + * ISO15693.h + * + * Created on: 25.01.2017 + * Author: Phillip Nash + */ + +#ifndef ISO15693_H_ +#define ISO15693_H_ + +#include "Terminal/CommandLine.h" + +#define ISO15693_APP_NO_RESPONSE 0x0000 + +/* VERY OUTDATED */ +#define TIMER_SAMPLING TCC0 +#define TIMER_T1_BITRATE TCD1 +#define TIMER_SUBCARRIER TCD0 + +#define SUBCARRIER_1 32 +#define SUBCARRIER_2 28 +#define SUBCARRIER_OFF 0 +#define SOF_PATTERN 0x1D +#define EOF_PATTERN 0xB8 + +#define T1_NOMINAL_CYCLES 4352 - 14//4352 - 25 /* ISR prologue compensation */ +#define WRITE_GRID_CYCLES 4096 + +//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 ISO15693CodecInit(void); +void ISO15693CodecDeInit(void); +void ISO15693CodecTask(void); + +/* Application Interface */ +void ISO15693CodecStart(void); +void ISO15693CodecReset(void); + + + +#endif /* ISO15693_H_ */ diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index 72ffce1..0727eda 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -37,6 +37,9 @@ static const MapEntryType PROGMEM ConfigurationMap[] = { #ifdef CONFIG_ISO14443A_READER_SUPPORT { .Id = CONFIG_ISO14443A_READER, .Text = "ISO14443A_READER" }, #endif +#ifdef CONFIG_VICINITY_SUPPORT + { .Id = CONFIG_VICINITY, .Text = "VICINITY" }, +#endif }; /* Include all Codecs and Applications */ @@ -220,6 +223,23 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .ReadOnly = false }, #endif +#ifdef CONFIG_VICINITY_SUPPORT + [CONFIG_VICINITY] = { + .CodecInitFunc = ISO15693CodecInit, + .CodecDeInitFunc = ISO15693CodecDeInit, + .CodecTaskFunc = ISO15693CodecTask, + .ApplicationInitFunc = VicinityAppInit, + .ApplicationResetFunc = VicinityAppReset, + .ApplicationTaskFunc = VicinityAppTask, + .ApplicationTickFunc = VicinityAppTick, + .ApplicationProcessFunc = VicinityAppProcess, + .ApplicationGetUidFunc = VicinityGetUid, + .ApplicationSetUidFunc = VicinitySetUid, + .UidSize = ISO15693_GENERIC_UID_SIZE, + .MemorySize = ISO15693_GENERIC_MEM_SIZE, + .ReadOnly = false + }, +#endif }; ConfigurationType ActiveConfiguration; diff --git a/Firmware/Chameleon-Mini/Configuration.h b/Firmware/Chameleon-Mini/Configuration.h index 2ca4944..8eeba79 100644 --- a/Firmware/Chameleon-Mini/Configuration.h +++ b/Firmware/Chameleon-Mini/Configuration.h @@ -42,6 +42,9 @@ typedef enum { #endif #ifdef CONFIG_ISO14443A_READER_SUPPORT CONFIG_ISO14443A_READER, +#endif +#ifdef CONFIG_VICINITY_SUPPORT + CONFIG_VICINITY, #endif /* This HAS to be the last element */ CONFIG_COUNT diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index 9d3175f..13300c1 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -12,6 +12,7 @@ SETTINGS += -DCONFIG_MF_CLASSIC_4K_7B_SUPPORT SETTINGS += -DCONFIG_MF_ULTRALIGHT_SUPPORT SETTINGS += -DCONFIG_ISO14443A_SNIFF_SUPPORT SETTINGS += -DCONFIG_ISO14443A_READER_SUPPORT +SETTINGS += -DCONFIG_VICINITY_SUPPORT #Support magic mode on mifare classic configuration SETTINGS += -DSUPPORT_MF_CLASSIC_MAGIC_MODE @@ -92,8 +93,8 @@ TARGET = Chameleon-Mini OPTIMIZATION = s SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.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/Reader14443-ISR.S -SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c +SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/Reader14443-ISR.S Codec/ISO15693.c +SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Vicinity.c Application/ISO15693-A.c SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) LUFA_PATH = ../LUFA CC_FLAGS = -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)