From 7ca6bd45fb5acfd37c8be36c5de4b77109df7a9b Mon Sep 17 00:00:00 2001 From: Terry <99769575+terrymeow@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:11:25 +0100 Subject: [PATCH 1/4] Fix compilation errors Some former compilation warnings became errors since GCC 14 https://gcc.gnu.org/gcc-14/porting_to.html#warnings-as-errors --- Firmware/Chameleon-Mini/.gitignore | 16 +++++++------ .../Chameleon-Mini/Application/CryptoAES128.c | 24 +++++++------------ .../Chameleon-Mini/Application/CryptoAES128.h | 2 +- .../Chameleon-Mini/Application/CryptoTDEA.h | 6 ++--- .../DESFire/DESFireApplicationDirectory.c | 4 ++-- .../DESFire/DESFireChameleonTerminal.c | 12 +++++----- 6 files changed, 29 insertions(+), 35 deletions(-) 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/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 From 6bcdf13466de5ebbc3c497a85ee8cbf6e83f7a8d Mon Sep 17 00:00:00 2001 From: Terry <99769575+terrymeow@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:28:55 +0100 Subject: [PATCH 2/4] Add missing ifdefs for configurations to save space --- Firmware/Chameleon-Mini/Application/EM4233.c | 4 ++++ Firmware/Chameleon-Mini/Application/NTAG215.c | 4 ++++ Firmware/Chameleon-Mini/Application/Sl2s2002.c | 3 ++- Firmware/Chameleon-Mini/Application/TITagitstandard.c | 4 ++++ Firmware/Chameleon-Mini/Application/Vicinity.c | 3 ++- Firmware/Chameleon-Mini/Codec/ISO15693.c | 4 ++++ Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S | 4 ++++ Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c | 4 ++++ Firmware/Chameleon-Mini/Codec/SniffISO15693.c | 4 ++++ 9 files changed, 32 insertions(+), 2 deletions(-) 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/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/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..9bd095e5 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" @@ -804,3 +806,5 @@ void SniffISO15693CodecTask(void) { ReaderSniffInit(); } } + +#endif /* CONFIG_ISO15693_SNIFF_SUPPORT */ From 0442b6eb3e87e26c48ec3ac599b36e13db3ed69a Mon Sep 17 00:00:00 2001 From: Ladislav Marko Date: Wed, 19 Nov 2025 18:17:41 +0100 Subject: [PATCH 3/4] Initialize configuration after Log initialization Right now if you call LogEntry() from an AppInit() function the system will hang with no warning. This simple change allows you to call this function in AppInit() without any problems. --- Firmware/Chameleon-Mini/Chameleon-Mini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From 87b58b65eda8c36840c2fa08c9c2fad4979d88f0 Mon Sep 17 00:00:00 2001 From: Ladislav Marko Date: Mon, 5 Jan 2026 17:53:13 +0100 Subject: [PATCH 4/4] Implement basic LEGIC prime card replay emulation --- .../Chameleon-Mini/Application/Application.h | 1 + .../Chameleon-Mini/Application/LegicPrime.c | 113 ++++++ .../Chameleon-Mini/Application/LegicPrime.h | 23 ++ Firmware/Chameleon-Mini/Codec/Codec.c | 1 + Firmware/Chameleon-Mini/Codec/Codec.h | 10 + Firmware/Chameleon-Mini/Codec/ISO14443-2F.c | 380 ++++++++++++++++++ Firmware/Chameleon-Mini/Codec/ISO14443-2F.h | 20 + Firmware/Chameleon-Mini/Codec/SniffISO15693.c | 2 +- Firmware/Chameleon-Mini/Configuration.c | 22 + Firmware/Chameleon-Mini/Configuration.h | 4 + Firmware/Chameleon-Mini/ISRSharing.S | 4 + Firmware/Chameleon-Mini/Makefile | 4 + 12 files changed, 583 insertions(+), 1 deletion(-) create mode 100644 Firmware/Chameleon-Mini/Application/LegicPrime.c create mode 100644 Firmware/Chameleon-Mini/Application/LegicPrime.h create mode 100644 Firmware/Chameleon-Mini/Codec/ISO14443-2F.c create mode 100644 Firmware/Chameleon-Mini/Codec/ISO14443-2F.h 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/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/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/SniffISO15693.c b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c index 9bd095e5..c8976851 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO15693.c @@ -544,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 */ 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 \