diff --git a/Firmware/Chameleon-Mini/.gitignore b/Firmware/Chameleon-Mini/.gitignore index ec42b485..33344402 100644 --- a/Firmware/Chameleon-Mini/.gitignore +++ b/Firmware/Chameleon-Mini/.gitignore @@ -1,11 +1,13 @@ -Chameleon-Mini.eep -Chameleon-Mini.hex -Chameleon-Mini.elf -Chameleon-Mini.map -Chameleon-Mini.bin -Chameleon-Mini.lss -Chameleon-Mini.sym +Chameleon-Mini*.eep +Chameleon-Mini*.hex +Chameleon-Mini*.elf +Chameleon-Mini*.map +Chameleon-Mini*.bin +Chameleon-Mini*.lss +Chameleon-Mini*.sym Bin/ Bin/* Latest/ Latest/* +.cache/ +.cache/** diff --git a/Firmware/Chameleon-Mini/Application/Application.h b/Firmware/Chameleon-Mini/Application/Application.h index 4b98f3b7..5c5db348 100644 --- a/Firmware/Chameleon-Mini/Application/Application.h +++ b/Firmware/Chameleon-Mini/Application/Application.h @@ -15,6 +15,7 @@ /* Applications */ #include "MifareUltralight.h" #include "MifareClassic.h" +#include "LegicPrime.h" #include "Reader14443A.h" #include "Vicinity.h" #include "Sl2s2002.h" diff --git a/Firmware/Chameleon-Mini/Application/CryptoAES128.c b/Firmware/Chameleon-Mini/Application/CryptoAES128.c index ef5abcc9..cd5c989f 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoAES128.c +++ b/Firmware/Chameleon-Mini/Application/CryptoAES128.c @@ -102,7 +102,7 @@ void aes_get_key(uint8_t *key_out) { } } -static void CryptoAESEncryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key, bool XorModeOn); +static void CryptoAESEncryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key); static void CryptoAESDecryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key); static bool aes_lastsubkey_generate(uint8_t *key, uint8_t *last_sub_key) { @@ -114,7 +114,8 @@ static bool aes_lastsubkey_generate(uint8_t *key, uint8_t *last_sub_key) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - CryptoAESEncryptBlock(dummy_data, dummy_data, key, false); + aes_configure_encrypt(AES_MANUAL, AES_XOR_OFF); + CryptoAESEncryptBlock(dummy_data, dummy_data, key); /* If not error. */ if (!aes_is_error()) { /* Store the last subkey. */ @@ -196,12 +197,7 @@ static uint16_t CryptoAESGetPaddedBufferSize(uint16_t bufSize) { return bufSize + CRYPTO_AES_BLOCK_SIZE - spareBytes; } -static void CryptoAESEncryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key, bool XorModeOn) { - aes_software_reset(); - AES.CTRL = AES_RESET_bm; - NOP(); - AES.CTRL = 0; - aes_configure_encrypt(AES_MANUAL, XorModeOn ? AES_XOR_ON : AES_XOR_OFF); +static void CryptoAESEncryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key) { aes_isr_configure(AES_INTLVL_OFF); aes_set_key(Key); for (uint8_t i = 0; i < CRYPTO_AES_BLOCK_SIZE; i++) { @@ -217,14 +213,8 @@ static void CryptoAESEncryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const } static void CryptoAESDecryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key) { - AES.CTRL = AES_RESET_bm; - NOP(); - AES.CTRL = 0; uint8_t lastSubKey[CRYPTO_AES_KEY_SIZE]; aes_lastsubkey_generate(Key, lastSubKey); - AES.CTRL = AES_RESET_bm; - NOP(); - AES.CTRL = 0; aes_configure_decrypt(AES_MANUAL, AES_XOR_OFF); aes_isr_configure(AES_INTLVL_OFF); aes_set_key(lastSubKey); @@ -242,6 +232,8 @@ static void CryptoAESDecryptBlock(uint8_t *Plaintext, uint8_t *Ciphertext, const int CryptoAESEncryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Ciphertext, uint8_t *IVIn, const uint8_t *Key) { + aes_software_reset(); + aes_configure_encrypt(AES_MANUAL, AES_XOR_ON); uint8_t *IV = IVIn; if ((Count % CRYPTO_AES_BLOCK_SIZE) != 0) { return 0xBE; @@ -273,7 +265,7 @@ int CryptoAESEncryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Cipherte CryptoMemoryXOR(&Ciphertext[(blk - 1) * CRYPTO_AES_BLOCK_SIZE], inputBlock, CRYPTO_AES_BLOCK_SIZE); } } - CryptoAESEncryptBlock(inputBlock, Ciphertext + blk * CRYPTO_AES_BLOCK_SIZE, Key, true); + CryptoAESEncryptBlock(inputBlock, Ciphertext + blk * CRYPTO_AES_BLOCK_SIZE, Key); if (blk + 1 == bufBlocks) { memcpy(IV, inputBlock, CRYPTO_AES_BLOCK_SIZE); } @@ -286,7 +278,7 @@ int CryptoAESEncryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Cipherte memset(&inputBlock[numInputUnevenBytes], 0x00, CRYPTO_AES_BLOCK_SIZE - numInputUnevenBytes); } CryptoMemoryXOR(IV, inputBlock, CRYPTO_AES_BLOCK_SIZE); - CryptoAESEncryptBlock(inputBlock, Ciphertext + blk * CRYPTO_AES_BLOCK_SIZE, Key, true); + CryptoAESEncryptBlock(inputBlock, Ciphertext + blk * CRYPTO_AES_BLOCK_SIZE, Key); memcpy(IV, Ciphertext + blk * CRYPTO_AES_BLOCK_SIZE, CRYPTO_AES_BLOCK_SIZE); } } diff --git a/Firmware/Chameleon-Mini/Application/CryptoAES128.h b/Firmware/Chameleon-Mini/Application/CryptoAES128.h index 3cbe4ae6..f043841e 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoAES128.h +++ b/Firmware/Chameleon-Mini/Application/CryptoAES128.h @@ -149,7 +149,7 @@ int CryptoAESEncryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Cipherte int CryptoAESDecryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Ciphertext, uint8_t *IV, const uint8_t *Key); -typedef uint8_t (*CryptoAESFuncType)(uint8_t *, uint8_t *, uint8_t *); +typedef void (*CryptoAESFuncType)(uint8_t *Plaintext, uint8_t *Ciphertext, const uint8_t *Key); typedef struct { CryptoAESFuncType cryptFunc; uint16_t blockSize; diff --git a/Firmware/Chameleon-Mini/Application/CryptoTDEA.h b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h index 60ecfd2c..ca89bcce 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoTDEA.h +++ b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h @@ -51,7 +51,7 @@ typedef uint8_t Crypto3KTDEAKeyType[CRYPTO_3KTDEA_KEY_SIZE]; /* Prototype the CBC function pointer in case anyone needs it */ typedef void (*CryptoTDEACBCFuncType)(uint16_t Count, const void *Plaintext, void *Ciphertext, void *IV, const uint8_t *Keys); -typedef void (*CryptoTDEAFuncType)(const void *PlainText, void *Ciphertext, const uint8_t *Keys); +typedef void (*CryptoTDEAFuncType)(void *PlainText, void *Ciphertext, const uint8_t *Keys); void CryptoEncryptDES(void *Plaintext, void *Ciphertext, const uint8_t *Keys); void CryptoDecryptDES(void *Plaintext, void *Ciphertext, const uint8_t *Keys); @@ -64,8 +64,8 @@ int DecryptDESBuffer(uint16_t Count, void *Plaintext, const void *Ciphertext, co * \param Ciphertext Destination buffer to contain ciphertext * \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE) */ -void CryptoEncrypt2KTDEA(const void *Plaintext, void *Ciphertext, const uint8_t *Keys); -void CryptoDecrypt2KTDEA(const void *Plaintext, void *Ciphertext, const uint8_t *Keys); +void CryptoEncrypt2KTDEA(void *Plaintext, void *Ciphertext, const uint8_t *Keys); +void CryptoDecrypt2KTDEA(void *Plaintext, void *Ciphertext, const uint8_t *Keys); int Encrypt2K3DESBuffer(uint16_t Count, const void *Plaintext, void *Ciphertext, const uint8_t *IV, const uint8_t *Keys); int Decrypt2K3DESBuffer(uint16_t Count, void *Plaintext, const void *Ciphertext, const uint8_t *IV, const uint8_t *Keys); diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c index 1f884fe1..1793c7d1 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c @@ -281,7 +281,7 @@ BYTE ReadKeyCryptoType(uint8_t AppSlot, uint8_t KeyId) { void WriteKeyCryptoType(uint8_t AppSlot, uint8_t KeyId, BYTE Value) { if (AppSlot >= DESFIRE_MAX_SLOTS || !KeyIdValid(AppSlot, KeyId)) { - return 0x00; + return; } SIZET keyTypesBlockId = GetAppProperty(DESFIRE_APP_KEY_TYPES_ARRAY_BLOCK_ID, AppSlot); BYTE keyTypesArray[DESFIRE_MAX_KEYS]; @@ -368,7 +368,7 @@ BYTE LookupFileNumberByIndex(uint8_t AppSlot, BYTE FileIndex) { BYTE LookupNextFreeFileSlot(uint8_t AppSlot) { if (AppSlot >= DESFIRE_MAX_SLOTS) { - return; + return DESFIRE_MAX_FILES; } SIZET fileNumbersHashmapBlockId = GetAppProperty(DESFIRE_APP_FILE_NUMBER_ARRAY_MAP_BLOCK_ID, AppSlot); BYTE fileNumbersHashmap[DESFIRE_MAX_FILES]; diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c index b38ea8aa..cd57682e 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c @@ -175,19 +175,19 @@ CommandStatusIdType CommandDESFireSetCommMode(char *OutParam, const char *InPara if (!strcasecmp_P(valueStr, PSTR("Plaintext"))) { DesfireCommMode = DESFIRE_COMMS_PLAINTEXT; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK_ID; } else if (!strcasecmp_P(valueStr, PSTR("Plaintext:MAC"))) { DesfireCommMode = DESFIRE_COMMS_PLAINTEXT_MAC; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK_ID; } else if (!strcasecmp_P(valueStr, PSTR("Enciphered:3K3DES"))) { DesfireCommMode = DESFIRE_COMMS_CIPHERTEXT_DES; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK_ID; } else if (!strcasecmp_P(valueStr, PSTR("Enciphered:AES128"))) { DesfireCommMode = DESFIRE_COMMS_CIPHERTEXT_AES128; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK_ID; } return COMMAND_ERR_INVALID_USAGE_ID; } @@ -205,7 +205,7 @@ CommandStatusIdType CommandDESFireSetEncryptionMode(char *OutParam, const char * bool setAESCryptoMode = true, setDESCryptoMode = true; bool ecbModeEnabled = true; if (modeStartPos == NULL) { - modeStartPos = &valueStr; + modeStartPos = &valueStr[0]; } else { uint8_t prefixLength = (uint8_t)(modeStartPos - valueStr); if (prefixLength == 0) { @@ -231,7 +231,7 @@ CommandStatusIdType CommandDESFireSetEncryptionMode(char *OutParam, const char * if (setAESCryptoMode) { __CryptoAESOpMode = ecbModeEnabled ? CRYPTO_AES_ECB_MODE : CRYPTO_AES_CBC_MODE; } - return COMMAND_INFO_OK; + return COMMAND_INFO_OK_ID; } //The rest of the file was added by tomaspre diff --git a/Firmware/Chameleon-Mini/Application/EM4233.c b/Firmware/Chameleon-Mini/Application/EM4233.c index b56ed61a..1807309a 100644 --- a/Firmware/Chameleon-Mini/Application/EM4233.c +++ b/Firmware/Chameleon-Mini/Application/EM4233.c @@ -12,6 +12,8 @@ * (Only EM4233_Read_Single and EM4233_Read_Multiple have been checked up to now) */ +#ifdef CONFIG_EM4233_SUPPORT + #include "../Random.h" #include "ISO15693-A.h" #include "EM4233.h" @@ -680,3 +682,5 @@ void EM4233SetUid(ConfigurationUidType NewUid) { memcpy(Uid, NewUid, ActiveConfiguration.UidSize); MemoryWriteBlock(NewUid, EM4233_MEM_UID_ADDRESS, ActiveConfiguration.UidSize); } + +#endif /* CONFIG_EM4233_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/LegicPrime.c b/Firmware/Chameleon-Mini/Application/LegicPrime.c new file mode 100644 index 00000000..d7d44d4d --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/LegicPrime.c @@ -0,0 +1,113 @@ +/* + * LegicPrime.c + * + * Created on: 5.7.2024 + * Author: Ladislav Marko + * Inspired by MifareClassic.c + */ +#if defined(CONFIG_LEGIC_PRIME_SUPPORT) + +#include "LegicPrime.h" + +char legic_log_str[64]; + +#include "../Codec/ISO14443-2F.h" +#include "../Memory.h" + +#define MEM_UID_ADDRESS 0x00 +#define MEM_UID_CRC_ADDRESS 0x04 +#define MEM_DCF_LOW_ADDRESS 0x05 +#define MEM_DCF_HIGH_ADDRESS 0x06 +#define MEM_BACKUP_ADDRESS 0x0D +#define MEM_BACKUP_CRC_ADDRESS 0x13 + +#define MEM_REPLAY_ADDRESS 0x1000 + +/* LEGIC prime card layout + * UID [4 Bytes] + * UID CRC [1 Byte] + * Decremental filed low byte (DCF) [1 byte] + * Decremental filed high byte (DCF) [1 byte] + * 0x9F 0xFF 0x00 0x00 0x00 0x11 [6 bytes] + * Backup [6 bytes] + * Backup CRC [1 byte] + * 0x00 0x00 [2 bytes] + * additional segments + * */ +uint8_t response_index; + +uint16_t LegicPrimeAppProcess(uint8_t *Buffer, uint16_t BitCount) { + + uint8_t tmpbf[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + + switch(BitCount){ + case 0: + return ISO14443F_APP_NO_RESPONSE; + case 7: + // Probably start of setup phase + MemoryReadBlock(tmpbf, MEM_REPLAY_ADDRESS, 1); + memcpy(Buffer, tmpbf, 1); + return 6; + case 6: + // Probably end of setup phase + return ISO14443F_APP_NO_RESPONSE; + case 9: + // Probably reading MIM256 card + // Fallthrough to case 11 + case 11: + // Probably reading MIM1024 card + switch(response_index){ + case 0: + case 1: + case 2: + case 3: + case 4: + MemoryReadBlock(tmpbf, MEM_REPLAY_ADDRESS + 1 + (response_index * 2), 2); + response_index++; + break; + default: + sprintf(legic_log_str, "Legic APP Processing response index that is too high"); + LogEntry(LOG_INFO_GENERIC, legic_log_str, strlen(legic_log_str)); + return ISO14443F_APP_NO_RESPONSE; + } + + memcpy(Buffer, tmpbf, 2); + return 12; + default: + sprintf(legic_log_str, "Legic APP Processing unknown response"); + LogEntry(LOG_INFO_GENERIC, legic_log_str, strlen(legic_log_str)); + return ISO14443F_APP_NO_RESPONSE; //TODO: die horribly here? + } +} + +void LegicPrimeGetUid(ConfigurationUidType Uid) { + sprintf(legic_log_str, "LEGIC GET UID"); + LogEntry(LOG_INFO_GENERIC, legic_log_str, strlen(legic_log_str)); + MemoryReadBlock(Uid, MEM_UID_ADDRESS, LEGIC_PRIME_UID_SIZE); +} + +void LegicPrimeSetUid(ConfigurationUidType Uid) { + sprintf(legic_log_str, "LEGIC SET UID"); + MemoryWriteBlock(Uid, MEM_UID_ADDRESS, LEGIC_PRIME_UID_SIZE); + //TODO: Write also the LEGIC prime UID CRC to MEM_UID_CRC_ADDRESS + LogEntry(LOG_INFO_GENERIC, legic_log_str, strlen(legic_log_str)); +} + +void LegicPrimeAppInit(void) { + response_index = 0; + sprintf(legic_log_str, "LEGIC APP INIT"); + LogEntry(LOG_INFO_GENERIC, legic_log_str, strlen(legic_log_str)); + + // Prepare some captured communication beforehand or upload through the SEND/UPLOAD terminal functionality + // Card ID: 0x57 0x46 0x5f 0x85 + uint8_t capture[] = {0x39, 0x3e, 0x5, 0x45, 0x5, 0xfb, 0x2, 0x41, 0x0, 0xac, 0xc}; + MemoryWriteBlock(capture, MEM_REPLAY_ADDRESS, 11); + // Card ID: 0x81 0xAB 0xB8 0x4A + // uint8_t capture2[] = {0x19, 0x27, 0xb, 0x9b, 0x1, 0xa1, 0x1, 0x6d, 0xa, 0x52, 0x3}; + // MemoryWriteBlock(capture2, MEM_REPLAY_ADDRESS, 11); +} + +void LegicPrimeAppReset(void) { + response_index = 0; +} +#endif diff --git a/Firmware/Chameleon-Mini/Application/LegicPrime.h b/Firmware/Chameleon-Mini/Application/LegicPrime.h new file mode 100644 index 00000000..13e010d6 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/LegicPrime.h @@ -0,0 +1,23 @@ +/* + * LegicPrime.h + * + * Created on: 5.7.2024 + * Author: Ladislav Marko + * Inspired by MifareClassic.h + */ + +#ifndef LEGIC_PRIME_H +#define LEGIC_PRIME_H +#include "Application.h" + +#define LEGIC_PRIME_UID_SIZE 4 +#define LEGIC_PRIME_MEM_SIZE 256 // There are two main LEGIC prime variants -- 256 and 1024 bytes, so we use the small one now + +void LegicPrimeAppInit(void); +void LegicPrimeAppReset(void); + +uint16_t LegicPrimeAppProcess(uint8_t *Buffer, uint16_t BitCount); + +void LegicPrimeGetUid(ConfigurationUidType Uid); +void LegicPrimeSetUid(ConfigurationUidType Uid); +#endif //LEGIC_PRIME_H diff --git a/Firmware/Chameleon-Mini/Application/NTAG215.c b/Firmware/Chameleon-Mini/Application/NTAG215.c index 4f75c9d0..ee0cd94a 100644 --- a/Firmware/Chameleon-Mini/Application/NTAG215.c +++ b/Firmware/Chameleon-Mini/Application/NTAG215.c @@ -9,6 +9,8 @@ * Thanks to skuser for the MifareUltralight code used as a starting point */ +#ifdef CONFIG_NTAG215_SUPPORT + #include "ISO14443-3A.h" #include "../Codec/ISO14443-2A.h" #include "../Memory.h" @@ -428,3 +430,5 @@ void NTAG215SetUid(ConfigurationUidType Uid) { MemoryWriteBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE); MemoryWriteBlock(&BCC2, UID_BCC2_ADDRESS, ISO14443A_CL_BCC_SIZE); } + +#endif /* CONFIG_NTAG215_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/Sl2s2002.c b/Firmware/Chameleon-Mini/Application/Sl2s2002.c index ffdbed1e..a5595374 100644 --- a/Firmware/Chameleon-Mini/Application/Sl2s2002.c +++ b/Firmware/Chameleon-Mini/Application/Sl2s2002.c @@ -9,6 +9,7 @@ * should be performed (see TITagitstandard.c) - ceres-c */ +#ifdef CONFIG_SL2S2002_SUPPORT #include "Sl2s2002.h" #include "../Codec/ISO15693.h" @@ -161,4 +162,4 @@ void Sl2s2002SetUid(ConfigurationUidType Uid) { } - +#endif /* CONFIG_SL2S2002_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/TITagitstandard.c b/Firmware/Chameleon-Mini/Application/TITagitstandard.c index 2d4618c9..a2bd7437 100644 --- a/Firmware/Chameleon-Mini/Application/TITagitstandard.c +++ b/Firmware/Chameleon-Mini/Application/TITagitstandard.c @@ -7,6 +7,8 @@ * Modified by ceres-c & MrMoDDoM to finish things up */ +#ifdef CONFIG_TITAGITSTANDARD_SUPPORT + #include "ISO15693-A.h" #include "TITagitstandard.h" @@ -225,3 +227,5 @@ void TITagitstandardFlipUid(ConfigurationUidType Uid) { *tail-- = tmp; } } + +#endif /* CONFIG_TITAGITSTANDARD_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/Vicinity.c b/Firmware/Chameleon-Mini/Application/Vicinity.c index c3bc1716..d48de11b 100644 --- a/Firmware/Chameleon-Mini/Application/Vicinity.c +++ b/Firmware/Chameleon-Mini/Application/Vicinity.c @@ -9,6 +9,7 @@ * should be performed (see TITagitstandard.c) - ceres-c */ +#ifdef CONFIG_VICINITY_SUPPORT #include "Vicinity.h" #include "../Codec/ISO15693.h" @@ -112,4 +113,4 @@ void VicinitySetUid(ConfigurationUidType Uid) { } - +#endif /* CONFIG_VICINITY_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Chameleon-Mini.c b/Firmware/Chameleon-Mini/Chameleon-Mini.c index 54501d4d..08c0f92b 100644 --- a/Firmware/Chameleon-Mini/Chameleon-Mini.c +++ b/Firmware/Chameleon-Mini/Chameleon-Mini.c @@ -7,12 +7,12 @@ int main(void) { PinInit(); MemoryInit(); CodecInitCommon(); - ConfigurationInit(); TerminalInit(); RandomInit(); ButtonInit(); AntennaLevelInit(); LogInit(); + ConfigurationInit(); SystemInterruptInit(); while (1) { diff --git a/Firmware/Chameleon-Mini/Codec/Codec.c b/Firmware/Chameleon-Mini/Codec/Codec.c index 80c4de9c..8317a189 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.c +++ b/Firmware/Chameleon-Mini/Codec/Codec.c @@ -30,6 +30,7 @@ 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); +void (* volatile isr_func_CODEC_TIMER_SAMPLING_OVF_vect)(void) = NULL; 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; diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index 0005d8cf..32e0954a 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -79,6 +79,7 @@ #include "../Settings.h" #include "ISO14443-2A.h" +#include "ISO14443-2F.h" #include "Reader14443-2A.h" #include "SniffISO14443-2A.h" #include "ISO15693.h" @@ -92,6 +93,11 @@ #define ISO14443A_FRAME_DELAY_PREV0 1172 #define ISO14443A_RX_PENDING_TIMEOUT 4 // ms +/* Timing definitions for ISO14443F */ +#define ISO14443F_SUBCARRIER_DIVIDER 64 +#define ISO14443F_BIT_RATE_CYCLES 271 + + #define CODEC_BUFFER_SIZE 256 #define CODEC_CARRIER_FREQ 13560000 @@ -124,11 +130,15 @@ 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); +extern void (* volatile isr_func_CODEC_TIMER_SAMPLING_OVF_vect)(void); +void isr_SNIFF_ISO15693_CODEC_TIMER_SAMPLING_OVF_VECT(void); +void isr_ISO14443_2F_CODEC_TIMER_SAMPLING_OVF_VECT(void); 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_2F_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); diff --git a/Firmware/Chameleon-Mini/Codec/ISO14443-2F.c b/Firmware/Chameleon-Mini/Codec/ISO14443-2F.c new file mode 100644 index 00000000..6cc4287e --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/ISO14443-2F.c @@ -0,0 +1,380 @@ +/* + * ISO14443-2F.c + * + * This code implements LEGIC prime physical layer for card emulation. + * + * Created on: 5.7.2024 + * Author: Ladislav Marko + * Inspired by ISO14443-2A.c and ISO15693.c + */ + +#include "ISO14443-2F.h" +#include "../System.h" +#include "../Application/Application.h" +#include "../LEDHook.h" +#include "Codec.h" +#include "Log.h" + +// ------------------------ LEGIC CODEC SECTION ------------------------------- +// For reading the reader's data: +// Sampling of the reader is done using internal clock, synchronized to the first field modulation pause. +// +// Reader's "bitrate" is variable. Received bit 1 takes 80us HIGH and a bit 0 takes 40us HIGH. +// After both, there needs to be 20us of LOW. +// +// We can thus say, that 1 is composited of 80us of HIGH and 20us LOW, which takes 100us in total, +// and 0 is composited of 40us of HIGH and 20us of LOW, which takes 60us in total. +// Greatest common divisor of 100 and 60 is 20, so we need to measure every 20us to be sure we are synced. +// Thus, every time we measure LOW, we'll look into our memory and if we encountered +// 4 HIGHs before, we have just received a 1 or if we read just 2 HIGHs, we have received a 0. +// Effectively we need to sample each 20us which makes our bitrate 50 kbps +// which in turn makes our BIT_RATE_CYCLES 542 (.4) +// +// v ^ +// o | +// l | 1 0 1 0 +// t | +----------------+ +--------+ +--*----*----*----*--+ L +--*----*--+ L +// a | | | | | | * * * * | * | * * | * +// g | | | | | | * * * * | * | * * | * +// e | | +----+ +----+ H H H H +--*--+ H H +--*-- +// +-------------------------------------------------------------------------------------------> time +// +// NOTE: a dash takes 5us, stars symbolise a measurement that should be every 20us +// we decode HHHHL as 1 and HHL as 0 +// +// +// For sending data: +// Sending uses the carrier frequency as a clock source and is synchronized to reader's last modulation pause. +// +// We need to wait +-320us after last reader bit and then start sending card data. +// We use on-off keying with 1/64 of the carrier wave as the subcarrier with bit duration of 100us. +// +// The reader usually responds after 230us with new data. + +// This is +-20 microseconds +#define READER_SIGNAL_SAMPLE_RATE_IN_SYSTEM_CYCLES ((uint16_t) (((uint64_t) F_CPU * ISO14443F_BIT_RATE_CYCLES) / CODEC_CARRIER_FREQ)) + +// This is +-30 microseconds +#define FIRST_SAMPLING_OFFSET_IN_SYSTEM_CYCLES 659 + +//This is +-100 microseconds +#define TRANSMIT_RATE_IN_SYSTEM_CYCLES 1361 + +//This is +-320 microseconds +#define FIRST_TRANSMIT_OFFSET_IN_SYSTEM_CYCLES 4334 + +// Enum for states of the recieve functions that demodulate data from the reader +// The cycle should be DONT -> DO -> END -> DONT +typedef enum { + DONT_RECEIVE, + DO_RECEIVE, + END_RECEIVE //Give away control after all data have been received +} ReceiveStateType; + +// Enum for states of the transmit functions that modulate data to the reader +// The cycle should be NONE -> START -> BIT -> BIT -> ... -> BIT -> END -> NONE +typedef enum { + TRANSMIT_NONE, + TRANSMIT_START, + TRANSMIT_BIT, + TRANSMIT_END +} TransmitStateType; + +/* Define pseudo variables to use fast register access. This is useful for global vars */ +#define ReceiveStateRegister Codec8Reg0 +#define TransmitStateRegister Codec8Reg1 +#define SampleIdxRegister Codec8Reg2 +#define SampleRegister Codec8Reg3 +#define BitSent CodecCount16Register1 +#define BitCount CodecCount16Register2 + +/* Set pin PE0 to HIGH - used only for debugging during development */ +INLINE void set_PE0_high(void){ + PORTE.DIRSET |= PIN0_bm; + PORTE.OUTSET |= PIN0_bm; +} + +/* Set pin PE0 to LOW - used only for debugging during development */ +INLINE void set_PE0_low(void){ + PORTE.OUTCLR |= PIN0_bm; +} + +/* Handles the end of reading data from the reader when the physical layer data make sense */ +INLINE void ISO14443_F_DEMOD_END(void) { + SampleIdxRegister = 0; + /* Disable demodulation interrupt */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; /* Disconnect system clock from demod timer */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; /* Remove action from timer */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCAINTLVL_OFF_gc; /* Disable CCA interrupts */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCAIF_bm; /* Clear CCA interrupt flag */ + + /* By this time, the LOADMOD timer is aligned to the last modulation + * edge of the reader. So we disable the auto-synchronization and + * let it count the frame delay time in the background, and generate + * an interrupt once it has reached the FDT. */ + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; /* Disable restarts on modulation ends */ + CODEC_TIMER_LOADMOD.PERBUF = TRANSMIT_RATE_IN_SYSTEM_CYCLES; /* Prepare to change state every 100 us */ + CODEC_TIMER_LOADMOD.PER = FIRST_TRANSMIT_OFFSET_IN_SYSTEM_CYCLES; /* +- 320 microseconds offset from now */ + CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; /* Clear overflow interrupt flag */ + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_HI_gc; /* Set overflow interrupt level to high */ + + TransmitStateRegister = TRANSMIT_START; + ReceiveStateRegister = END_RECEIVE; + +} + +// v ^ Trigger here +// o | | +// l | Reader charging card/previous communication V 1 0 +// t |------------------------------------------------| +----------------+ +--------+ +// a | | | | | | +// g | | | | | | +// e | |----| +----+ +----- +// +-------------------------------------------------------------------------------------------> time +void EnableFirstModulationPauseInterrupt(void){ + /* Start looking out for modulation pause via interrupt. */ + CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0IF_bm; /* Clear the Interrupt 0 flag on the DEMOD port (Port B) */ + /* Set Pin 1 as source for Interrupt 0 on the DEMOD port (Port B) */ + CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; +} + +/* Handles the end of reading data from the reader when the physical layer data don't make sense */ +INLINE void ISO14443_F_GARBAGE(void){ + SampleIdxRegister = 0; + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; /* Disconnect system clock from demod timer */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; /* Remove action from timer */ + CODEC_TIMER_SAMPLING.INTCTRLB = TC_OVFINTLVL_OFF_gc; /* Disable CCA interrupts */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_OVFIF_bm; /* Clear OVF interrupt flag */ + EnableFirstModulationPauseInterrupt(); /* Start listening for the reader's field changes again */ +} + +/* Starts Loadmod timer as a free-running timer and syncs it to reader's modulation ends, so it will be accurate when + * we really need to start using it later */ +void PrepareLoadmodTimer(void){ + CODEC_TIMER_LOADMOD.CTRLA = CODEC_TIMER_CARRIER_CLKSEL; /* Use Carrier wave as timer source */ + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODEND_EVSEL; /* Restart time on modulation ends */ + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; /* Disable interrupt on overflow */ + CODEC_TIMER_LOADMOD.CNT = 0; + CODEC_TIMER_LOADMOD.PER = 0xFFFF; /* Set period to too high of a value */ +} + +/* Starts waiting for first demodulation pause to start sampling reader's field (and also prepares Loadmod timer) */ +INLINE void StartDemod(void) { + PrepareLoadmodTimer(); + + /* Activate Power for demodulator */ + CodecSetDemodPower(true); + ReceiveStateRegister = DO_RECEIVE; + SampleRegister = 0; + SampleIdxRegister = 0; + BitCount = 0; + + EnableFirstModulationPauseInterrupt(); +} + +/* This handles the interrupt enabled in EnableFirstModulationPauseInterrupt() + * + * This should trigger at the start of the reader's first modulation pause. It then starts the sampling timer + * to simply wait for a predefined interval, to align the timer about 10us into the first data transmission from + * the reader, and then to start sampling each 20 microseconds with the counter overflow (OVF) event + * v ^ NOW PER2 PER2 PER2 + * o | |--PER--| | | | + * l | V | | | | + * t | ----+ +--*----*----*----*--+ O +--*----*--+ O + * a | | | * * * * | * | * * | * + * g | | | * * * * | * | * * | * + * e | +----+ O O O O +--*--+ O O +--*-- + * +-------------------------------------------------------------------------------------------> time + * + * a dash takes 5us, O symbolises CODEC_TIMER_SAMPLING overflow interrupt that will get handled by + * the isr_ISO14443_2F_CODEC_TIMER_SAMPLING_OVF_VECT function + + * NOTE: PERBUF will become PER the first time CNT==PER will be true, here denoted PER2 + * read chapter 14 about Timer/Counter Type 0 and 1 of ATxmega manual for more information */ +ISR_SHARED isr_ISO14443_2F_CODEC_DEMOD_IN_INT0_VECT(void) { + /* Configure sampling-timer free running and sync to first modulation-pause. */ + /* CodecInitCommon(); sets Event channel 0 to signal the beginning (rising edge) of a modulation pause and Event channel 1 to + * signal the end (falling edge) of a modulation pause. */ + CODEC_TIMER_SAMPLING.CNT = 0; /* Reset the timer's initial value*/ + CODEC_TIMER_SAMPLING.PER = FIRST_SAMPLING_OFFSET_IN_SYSTEM_CYCLES; /* Set the timer's period, so we land +-10us into readers data signal */ + CODEC_TIMER_SAMPLING.PERBUF = READER_SIGNAL_SAMPLE_RATE_IN_SYSTEM_CYCLES; /* Set the timer's next period, so we sample each 20us */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV1_gc; /* Select the system clock (with no prescaler) as the timer source */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; /* Turn of any event actions */ + CODEC_TIMER_SAMPLING.INTCTRLA = TC_OVFINTLVL_HI_gc; /* Mark timer overflow interrupt as high level */ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_OVFIF_bm; /* Clear timer overflow interrupt flag */ + + /* Disable this interrupt. From now on we will sample the field using our CODEC_TIMER_SAMPLING OVF interrupt */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; + +} +INLINE void DisableLoadmodTimer(void){ + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; +} + +INLINE void SetBitOnPositionInBufferToValue(volatile uint8_t * buffer, uint16_t position, uint8_t value){ + uint16_t byte_offset = position / 8; + uint8_t bit_offset = position % 8; + + if (value){ + buffer[byte_offset] |= (1 << bit_offset); // Set bit to 1 + } else { + buffer[byte_offset] &= ~(1 << bit_offset); // Set bit to 0 + } +} + +uint8_t GetBitOnPositionInBuffer(const uint8_t * buffer, uint16_t position){ + uint16_t byte_offset = position / 8; + uint8_t bit_offset = position % 8; + + return (buffer[byte_offset] & (1 << bit_offset)) >> bit_offset; +} + + +// This triggers every 20us and samples the readers field +// from SamplePin's raw value which it converts to logical bits +// if 2 highs and a low are observed, it stores a logical 0 into CodecBuffer +// if 4 highs and a low are observed, it stores a logical 1 into CodecBuffer +ISR_SHARED isr_ISO14443_2F_CODEC_TIMER_SAMPLING_OVF_VECT(void){ + if (ReceiveStateRegister != DO_RECEIVE) { + return; + } + SampleIdxRegister++; + + uint8_t SamplePin = CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK; + + /* Shift sampled bit into sampling register */ + SampleRegister = (SampleRegister << 1) | (!SamplePin ? 0x01 : 0x00); + + if (!(SampleRegister & 0x1)) { // if last read bit is a zero + //SynchronizeSamplingTimerToDemodEnd(); + if (!(SampleRegister ^ 0x1E)) { + // We have read a 1 + SetBitOnPositionInBufferToValue(CodecBuffer, BitCount, 1); + BitCount++; + } else if (!(SampleRegister ^ 0x06)) { + // We have read a 0 + SetBitOnPositionInBufferToValue(CodecBuffer, BitCount, 0); + BitCount++; + } else { + ISO14443_F_GARBAGE(); + } + SampleRegister = 0; + SampleIdxRegister = 0; + } + + if (SampleIdxRegister > 4) { + // No more bits to be read or an error occurred during transmission as 5x HIGH should not happen + if (BitCount > 0){ + ISO14443_F_DEMOD_END(); + } else { + ISO14443_F_GARBAGE(); + } + } +} + +// Modulate as a card to send card response every 100 microseconds +ISR_SHARED isr_ISO14443_2F_CODEC_TIMER_LOADMOD_OVF_VECT(void) { + static void *JumpTable[] = { + [TRANSMIT_NONE] = && TRANSMIT_NONE_LABEL, + [TRANSMIT_START] = && TRANSMIT_START_LABEL, + [TRANSMIT_BIT] = && TRANSMIT_BIT_LABEL, + [TRANSMIT_END] = && TRANSMIT_END_LABEL + }; + + if ((TransmitStateRegister >= TRANSMIT_NONE) && (TransmitStateRegister <= TRANSMIT_END)) { + goto *JumpTable[TransmitStateRegister]; + } else { + TerminalSendString("ERROR: Jump to unregistered label!\r\n"); + //TODO: Log error to memory as well + return; + } + +TRANSMIT_NONE_LABEL: + return; + +TRANSMIT_START_LABEL: + TransmitStateRegister = TRANSMIT_BIT; + BitSent = 0; + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, ISO14443F_SUBCARRIER_DIVIDER); + CodecStartSubcarrier(); + /* Fallthrough */ +TRANSMIT_BIT_LABEL: + CodecSetLoadmodState(GetBitOnPositionInBuffer(CodecBuffer, BitSent)); + BitSent++; + if (BitSent >= BitCount){ + TransmitStateRegister = TRANSMIT_END; + } + return; + +TRANSMIT_END_LABEL: + TransmitStateRegister = TRANSMIT_NONE; + CodecSetLoadmodState(false); + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); + + DisableLoadmodTimer(); + StartDemod(); +} + +void ISO14443FCodecInit(void) { + /* Initialize some global vars and start looking out for reader commands */ + ReceiveStateRegister = DONT_RECEIVE; + TransmitStateRegister = TRANSMIT_NONE; + + isr_func_CODEC_DEMOD_IN_INT0_VECT = &isr_ISO14443_2F_CODEC_DEMOD_IN_INT0_VECT; + isr_func_CODEC_TIMER_SAMPLING_OVF_vect = &isr_ISO14443_2F_CODEC_TIMER_SAMPLING_OVF_VECT; + isr_func_CODEC_TIMER_LOADMOD_OVF_VECT = &isr_ISO14443_2F_CODEC_TIMER_LOADMOD_OVF_VECT; + CodecInitCommon(); + StartDemod(); +} + +void ISO14443FCodecDeInit(void) { + /* Gracefully shutdown codec */ + CODEC_DEMOD_IN_PORT.INT0MASK = 0; + ReceiveStateRegister = DONT_RECEIVE; + TransmitStateRegister = TRANSMIT_NONE; + + + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCAINTLVL_OFF_gc; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCAIF_bm; + + + CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; + CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; + + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); + CodecSetDemodPower(false); + CodecSetLoadmodState(false); + +} + +void ISO14443FCodecTask(void) { + if (ReceiveStateRegister == END_RECEIVE) { + LEDHook(LED_CODEC_RX, LED_PULSE); /* Signal data received */ + + /* Zero out unused bytes for logging */ + for (uint16_t i = 0; i < (BitCount % 8); i++){ + CodecBuffer[(BitCount + 7) / 8] &= ~(1u << i); + } + LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, (BitCount+7)/8 ); + + + uint16_t AnswerBitCount; + AnswerBitCount = ApplicationProcess(CodecBuffer, BitCount); + + if (AnswerBitCount != ISO14443F_APP_NO_RESPONSE) { + ReceiveStateRegister = DONT_RECEIVE; + LogEntry(LOG_INFO_CODEC_TX_DATA, CodecBuffer, (AnswerBitCount + 7) / 8); + BitCount = AnswerBitCount; + TransmitStateRegister = TRANSMIT_START; + } else { + /* No data to be processed. Disable loadmodding and start listening again */ + DisableLoadmodTimer(); + StartDemod(); + } + } +} \ No newline at end of file diff --git a/Firmware/Chameleon-Mini/Codec/ISO14443-2F.h b/Firmware/Chameleon-Mini/Codec/ISO14443-2F.h new file mode 100644 index 00000000..dc979ace --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/ISO14443-2F.h @@ -0,0 +1,20 @@ +/* + * ISO14443-2F.h + * + * Created on: 5.7.2024 + * Author: Ladislav Marko + * Inspired by ISO14443-2A.h and ISO15693.h + */ + +#ifndef ISO14443_F_H_ +#define ISO14443_F_H_ +#include "Codec.h" + +#define ISO14443F_APP_NO_RESPONSE 0x0000 + +/* Codec Interface */ +void ISO14443FCodecInit(void); +void ISO14443FCodecDeInit(void); +void ISO14443FCodecTask(void); + +#endif //ISO14443_F_H_ diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.c b/Firmware/Chameleon-Mini/Codec/ISO15693.c index 02214847..c66f5918 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.c @@ -25,6 +25,8 @@ * The ISR will now be invoked every 32 carrier pulses (see ISO15693-2:2006, section 8.2), even when waiting for data. */ +#if defined (CONFIG_ISO15693_SNIFF_SUPPORT) || defined (CONFIG_SL2S2002_SUPPORT) || defined (CONFIG_TITAGITSTANDARD_SUPPORT) || defined (CONFIG_TITAGITPLUS_SUPPORT) || defined (CONFIG_EM4233_SUPPORT) || defined (CONFIG_VICINITY_SUPPORT) + #include "ISO15693.h" #include "../System.h" #include "../Application/Application.h" @@ -684,3 +686,5 @@ void ISO15693CodecTask(void) { StartISO15693Demod(); } } + +#endif /* defined (CONFIG_ISO15693_SNIFF_SUPPORT) || defined (CONFIG_SL2S2002_SUPPORT) || defined (CONFIG_TITAGITSTANDARD_SUPPORT) || defined (CONFIG_TITAGITPLUS_SUPPORT) || defined (CONFIG_EM4233_SUPPORT) || defined (CONFIG_VICINITY_SUPPORT) */ diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S b/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S index 82ba110d..1537b9d1 100644 --- a/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S @@ -1,3 +1,5 @@ +#if defined(CONFIG_ISO14443A_READER_SUPPORT) || defined(CONFIG_ISO14443A_SNIFF_SUPPORT) + #include #define Zero R0 @@ -109,3 +111,5 @@ pop BitCountL pop Tmp pop Zero reti ; 2 + +#endif /* defined(CONFIG_ISO14443A_READER_SUPPORT) || defined(CONFIG_ISO14443A_SNIFF_SUPPORT) */ diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c index 5a33692d..79ae311e 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c @@ -5,6 +5,8 @@ // modified from ISO14443-2A.c and Reader14443-2A.c // +#ifdef CONFIG_ISO14443A_SNIFF_SUPPORT + #include "SniffISO14443-2A.h" #include "Reader14443-2A.h" @@ -527,3 +529,5 @@ void Sniff14443ACodecTask(void) { } + +#endif /* CONFIG_ISO14443A_SNIFF_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c index 5a02fc23..c8976851 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c @@ -5,6 +5,8 @@ * Author: ceres-c */ +#ifdef CONFIG_ISO15693_SNIFF_SUPPORT + #include "SniffISO15693.h" #include "Codec.h" #include "../System.h" @@ -542,7 +544,7 @@ ISR(CODEC_TIMER_LOADMOD_CCC_VECT) { * This interrupt handles the VICC SOF timeout when the card does not answer * and restarts reader sniffing */ -ISR(CODEC_TIMER_SAMPLING_OVF_VECT) { +ISR_SHARED isr_SNIFF_ISO15693_CODEC_TIMER_SAMPLING_OVF_VECT(void) { Flags.CardDemodFinished = 1; /* Call cleanup function */ @@ -804,3 +806,5 @@ void SniffISO15693CodecTask(void) { ReaderSniffInit(); } } + +#endif /* CONFIG_ISO15693_SNIFF_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index 99a61ff3..653d878a 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -20,6 +20,9 @@ /* Map IDs to text */ static const MapEntryType PROGMEM ConfigurationMap[] = { { .Id = CONFIG_NONE, .Text = "NONE" }, +#ifdef CONFIG_LEGIC_PRIME_SUPPORT + { .Id = CONFIG_LEGIC_PRIME, .Text = "LEGIC_PRIME" }, +#endif #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT { .Id = CONFIG_MF_ULTRALIGHT, .Text = "MF_ULTRALIGHT" }, { .Id = CONFIG_MF_ULTRALIGHT_EV1_80B, .Text = "MF_ULTRALIGHT_EV1_80B" }, @@ -109,6 +112,25 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .ReadOnly = true, .TagFamily = TAG_FAMILY_NONE }, +#ifdef CONFIG_LEGIC_PRIME_SUPPORT + [CONFIG_LEGIC_PRIME] = { + .CodecInitFunc = ISO14443FCodecInit, + .CodecDeInitFunc = ISO14443FCodecDeInit, + .CodecTaskFunc = ISO14443FCodecTask, + .ApplicationInitFunc = LegicPrimeAppInit, + .ApplicationInitRunOnceFunc = ApplicationInitDummy, + .ApplicationResetFunc = LegicPrimeAppReset, + .ApplicationTaskFunc = ApplicationTaskDummy, + .ApplicationTickFunc = ApplicationTickDummy, + .ApplicationProcessFunc = LegicPrimeAppProcess, + .ApplicationGetUidFunc = LegicPrimeGetUid, + .ApplicationSetUidFunc = LegicPrimeSetUid, + .UidSize = LEGIC_PRIME_UID_SIZE, + .MemorySize = LEGIC_PRIME_MEM_SIZE, + .ReadOnly = false, + .TagFamily = TAG_FAMILY_ISO14443F + }, +#endif #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT [CONFIG_MF_ULTRALIGHT] = { .CodecInitFunc = ISO14443ACodecInit, diff --git a/Firmware/Chameleon-Mini/Configuration.h b/Firmware/Chameleon-Mini/Configuration.h index 9a0f5aba..e55fc898 100644 --- a/Firmware/Chameleon-Mini/Configuration.h +++ b/Firmware/Chameleon-Mini/Configuration.h @@ -22,6 +22,9 @@ typedef enum { /* This HAS to be the first element */ CONFIG_NONE = 0, +#ifdef CONFIG_LEGIC_PRIME_SUPPORT + CONFIG_LEGIC_PRIME, +#endif #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT CONFIG_MF_ULTRALIGHT, CONFIG_MF_ULTRALIGHT_C, @@ -84,6 +87,7 @@ typedef enum { #define TAG_FAMILY_NONE 0 #define TAG_FAMILY_ISO14443A 1 #define TAG_FAMILY_ISO14443B 2 +#define TAG_FAMILY_ISO14443F 3 #define TAG_FAMILY_ISO15693 5 diff --git a/Firmware/Chameleon-Mini/ISRSharing.S b/Firmware/Chameleon-Mini/ISRSharing.S index a1d197cb..7830aec6 100644 --- a/Firmware/Chameleon-Mini/ISRSharing.S +++ b/Firmware/Chameleon-Mini/ISRSharing.S @@ -40,6 +40,10 @@ reti CODEC_DEMOD_IN_INT0_VECT: call_isr isr_func_CODEC_DEMOD_IN_INT0_VECT +.global CODEC_TIMER_SAMPLING_OVF_VECT +CODEC_TIMER_SAMPLING_OVF_VECT: + call_isr isr_func_CODEC_TIMER_SAMPLING_OVF_vect + .global CODEC_TIMER_SAMPLING_CCC_VECT CODEC_TIMER_SAMPLING_CCC_VECT: call_isr isr_func_TCD0_CCC_vect diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index 95c028c5..51071774 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -14,6 +14,7 @@ SETTINGS ?= ## : custom build targets found in the BuildScripts/* directory CONFIG_SETTINGS += -DCONFIG_MF_CLASSIC_MINI_4B_SUPPORT CONFIG_SETTINGS += -DCONFIG_MF_CLASSIC_1K_SUPPORT +#CONFIG_SETTINGS += -DCONFIG_LEGIC_PRIME_SUPPORT CONFIG_SETTINGS += -DCONFIG_MF_CLASSIC_1K_7B_SUPPORT CONFIG_SETTINGS += -DCONFIG_MF_CLASSIC_4K_SUPPORT CONFIG_SETTINGS += -DCONFIG_MF_CLASSIC_4K_7B_SUPPORT @@ -35,6 +36,7 @@ CONFIG_SETTINGS += -DCONFIG_NTAG215_SUPPORT #CONFIG_SETTINGS += -DDEFAULT_CONFIGURATION=CONFIG_MF_CLASSIC_4K #CONFIG_SETTINGS += -DDEFAULT_CONFIGURATION=CONFIG_MF_ULTRALIGHT #CONFIG_SETTINGS += -DDEFAULT_CONFIGURATION=CONFIG_ISO14443A_READER +#CONFIG_SETTINGS += -DDEFAULT_CONFIGURATION=CONFIG_LEGIC_PRIME CONFIG_SETTINGS += -DDEFAULT_CONFIGURATION=CONFIG_NONE ## : Support magic mode on mifare classic configuration @@ -206,6 +208,7 @@ SRC += Terminal/Terminal.c \ Terminal/CommandLine.c SRC += Codec/Codec.c \ Codec/ISO14443-2A.c \ + Codec/ISO14443-2F.c \ Codec/Reader14443-2A.c \ Codec/SniffISO14443-2A.c \ Codec/Reader14443-ISR.S \ @@ -213,6 +216,7 @@ SRC += Codec/Codec.c \ Codec/SniffISO15693.c SRC += Application/MifareUltralight.c \ Application/MifareClassic.c \ + Application/LegicPrime.c \ Application/ISO14443-3A.c \ Application/Crypto1.c \ Application/Reader14443A.c \