From d8e67a18c359da33ac2740f40349ad928093760c Mon Sep 17 00:00:00 2001 From: dev_zzo Date: Fri, 17 Nov 2017 18:58:54 +0100 Subject: [PATCH] UL EV1: initial working version --- .../Application/MifareUltralight.c | 443 +++++++++++++----- .../Application/MifareUltralight.h | 8 +- Firmware/Chameleon-Mini/Configuration.c | 31 ++ Firmware/Chameleon-Mini/Configuration.h | 2 + 4 files changed, 361 insertions(+), 123 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.c b/Firmware/Chameleon-Mini/Application/MifareUltralight.c index 98c5181..6a57e36 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.c +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.c @@ -10,6 +10,7 @@ #include "../Codec/ISO14443-2A.h" #include "../Memory.h" #include "../LEDHook.h" +#include "../Terminal/Terminal.h" #define ATQA_VALUE 0x0044 @@ -20,38 +21,69 @@ #define ACK_FRAME_SIZE 4 /* Bits */ #define NAK_INVALID_ARG 0x00 #define NAK_CRC_ERROR 0x01 +#define NAK_CTR_ERROR 0x04 #define NAK_EEPROM_ERROR 0x05 #define NAK_OTHER_ERROR 0x06 +#define NAK_AUTH_REQUIRED NAK_OTHER_ERROR #define NAK_FRAME_SIZE 4 +/* ISO commands */ +#define CMD_HALT 0x50 +/* EV0 commands */ #define CMD_READ 0x30 #define CMD_READ_FRAME_SIZE 2 /* without CRC bytes */ #define CMD_WRITE 0xA2 #define CMD_WRITE_FRAME_SIZE 6 /* without CRC bytes */ #define CMD_COMPAT_WRITE 0xA0 #define CMD_COMPAT_WRITE_FRAME_SIZE 2 -#define CMD_HALT 0x50 +/* EV1 commands */ +#define CMD_GET_VERSION 0x60 +#define CMD_FAST_READ 0x3A +#define CMD_READ_CNT 0x39 +#define CMD_INCREMENT_CNT 0xA5 +#define CMD_PWD_AUTH 0x1B +#define CMD_READ_SIG 0x3C +#define CMD_CHECK_TEARING_EVENT 0x3E +#define CMD_VCSL 0x4B -#define UID_CL1_ADDRESS 0x00 /* In Card Memory */ -#define UID_CL1_SIZE 3 /* In Bytes */ +/* Tag memory layout; addresses and sizes in bytes */ +#define UID_CL1_ADDRESS 0x00 +#define UID_CL1_SIZE 3 #define UID_BCC1_ADDRESS 0x03 -#define UID_CL2_ADDRESS 0x04 +#define UID_CL2_ADDRESS 0x04 #define UID_CL2_SIZE 4 #define UID_BCC2_ADDRESS 0x08 +#define LOCK_BYTES_1_ADDRESS 0x0A +#define LOCK_BYTES_2_ADDRESS 0x90 +#define CONFIG_AREA_SIZE 0x10 +#define CONF_AUTH0_OFFSET 0x03 +#define CONF_ACCESS_OFFSET 0x04 +#define CONF_PASSWORD_OFFSET 0x08 +#define CONF_PACK_OFFSET 0x0C + +#define CONF_ACCESS_PROT 0x80 +#define CONF_ACCESS_CNFLCK 0x40 + +#define CNT_MAX 2 +#define CNT_SIZE 4 +#define CNT_MAX_VALUE 0x00FFFFFF #define BYTES_PER_PAGE 4 #define PAGE_ADDRESS_MASK 0x0F #define BYTES_PER_READ 16 #define PAGE_READ_MIN 0x00 -#define PAGE_READ_MAX 0x0F #define BYTES_PER_WRITE 4 #define PAGE_WRITE_MIN 0x02 -#define PAGE_WRITE_MAX 0x0F #define BYTES_PER_COMPAT_WRITE 16 +static enum { + UL_EV0, + UL_EV1, +} Flavor; + static enum { STATE_HALT, STATE_IDLE, @@ -61,13 +93,52 @@ static enum { STATE_COMPAT_WRITE } State; -static uint8_t CompatWritePageAddress; static bool FromHalt = false; +static uint8_t PageCount; +static bool ArmedForCompatWrite; +static uint8_t CompatWritePageAddress; +static bool Authenticated; +static uint8_t FirstAuthenticatedPage; +static bool ReadAccessProtected; -void MifareUltralightAppInit(void) +static void AppInitCommon(void) { State = STATE_IDLE; FromHalt = false; + Authenticated = false; + ArmedForCompatWrite = false; +} + +void MifareUltralightAppInit(void) +{ + Flavor = UL_EV0; + PageCount = MIFARE_ULTRALIGHT_PAGES; + FirstAuthenticatedPage = 0xFF; + ReadAccessProtected = false; + AppInitCommon(); +} + +static void AppInitEV1Common(void) +{ + uint8_t ConfigAreaAddress = PageCount * BYTES_PER_PAGE - CONFIG_AREA_SIZE; + uint8_t Access; + Flavor = UL_EV1; + MemoryReadBlock(&FirstAuthenticatedPage, ConfigAreaAddress + CONF_AUTH0_OFFSET, 1); + MemoryReadBlock(&Access, ConfigAreaAddress + CONF_ACCESS_OFFSET, 1); + ReadAccessProtected = !!(Access & CONF_ACCESS_PROT); + AppInitCommon(); +} + +void MifareUltralightEV11AppInit(void) +{ + PageCount = MIFARE_ULTRALIGHT_EV11_PAGES; + AppInitEV1Common(); +} + +void MifareUltralightEV12AppInit(void) +{ + PageCount = MIFARE_ULTRALIGHT_EV12_PAGES; + AppInitEV1Common(); } void MifareUltralightAppReset(void) @@ -80,10 +151,249 @@ void MifareUltralightAppTask(void) } +static bool VerifyAuthentication(uint8_t PageAddress) +{ + /* No authentication for EV0 cards */ + if (Flavor < UL_EV1) { + return true; + } + /* If authenticated; no verification needed */ + if (Authenticated) { + return true; + } + return PageAddress < FirstAuthenticatedPage; +} + +/* Perform access verification and commit data if passed */ +static uint8_t AppWritePage(uint8_t PageAddress, uint8_t* const Buffer) +{ + if (!ActiveConfiguration.ReadOnly) { + MemoryWriteBlock(Buffer, PageAddress * BYTES_PER_PAGE, BYTES_PER_PAGE); + } else { + /* If the chameleon is in read only mode, it silently + * ignores any attempt to write data. */ + } + return 0; +} + +/* Handles processing of MF commands */ +static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) +{ + uint8_t Cmd = Buffer[0]; + + /* All commands here have CRCA appended; verify it right away */ + if (!ISO14443ACheckCRCA(Buffer, ByteCount - 2)) { + Buffer[0] = NAK_CRC_ERROR; + return NAK_FRAME_SIZE; + } + + if (ArmedForCompatWrite) { + ArmedForCompatWrite = false; + AppWritePage(CompatWritePageAddress, &Buffer[2]); + Buffer[0] = ACK_VALUE; + return ACK_FRAME_SIZE; + } + + /* Handle EV0 commands */ + switch (Cmd) { + + case CMD_READ: { + uint8_t PageAddress = Buffer[1]; + uint8_t PageLimit; + uint8_t Offset; + /* For EV1+ cards, ensure the wraparound is at the first protected page */ + if (Flavor >= UL_EV1 && ReadAccessProtected && !Authenticated) { + PageLimit = FirstAuthenticatedPage; + } else { + PageLimit = PageCount; + } + /* Validation */ + if (PageAddress >= PageLimit) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + /* Read out, emulating the wraparound */ + for (Offset = 0; Offset < BYTES_PER_READ; Offset += 4) { + MemoryReadBlock(&Buffer[Offset], PageAddress * BYTES_PER_PAGE, BYTES_PER_PAGE); + PageAddress++; + if (PageAddress == PageLimit) { + PageAddress = 0; + } + } + ISO14443AAppendCRCA(Buffer, BYTES_PER_READ); + return (BYTES_PER_READ + ISO14443A_CRCA_SIZE) * 8; + } + + case CMD_WRITE: { + /* This is a write command containing 4 bytes of data that + * should be written to the given page address. */ + uint8_t PageAddress = Buffer[1]; + /* Validation */ + if ((PageAddress < PAGE_WRITE_MIN) || (PageAddress >= PageCount)) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + if (!VerifyAuthentication(PageAddress)) { + Buffer[0] = NAK_AUTH_REQUIRED; + return NAK_FRAME_SIZE; + } + AppWritePage(PageAddress, &Buffer[2]); + Buffer[0] = ACK_VALUE; + return ACK_FRAME_SIZE; + } + + case CMD_COMPAT_WRITE: { + uint8_t PageAddress = Buffer[1]; + /* Validation */ + if ((PageAddress < PAGE_WRITE_MIN) || (PageAddress >= PageCount)) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + if (!VerifyAuthentication(PageAddress)) { + Buffer[0] = NAK_AUTH_REQUIRED; + return NAK_FRAME_SIZE; + } + /* CRC check passed and page-address is within bounds. + * Store address and proceed to receiving the data. */ + CompatWritePageAddress = PageAddress; + ArmedForCompatWrite = true; + Buffer[0] = ACK_VALUE; + return ACK_FRAME_SIZE; + } + + case CMD_HALT: { + /* Halts the tag. According to the ISO14443, the second + * byte is supposed to be 0. */ + if (Buffer[1] == 0) { + /* According to ISO14443, we must not send anything + * in order to acknowledge the HALT command. */ + State = STATE_HALT; + return ISO14443A_APP_NO_RESPONSE; + } else { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + } + default: + break; + } + /* Handle EV1 commands */ + if (Flavor >= UL_EV1) { + switch (Cmd) { + + case CMD_GET_VERSION: { + /* Provide hardcoded version response */ + Buffer[0] = 0x00; + Buffer[1] = 0x04; + Buffer[2] = 0x03; + Buffer[3] = 0x01; /**/ + Buffer[4] = 0x01; + Buffer[5] = 0x00; + Buffer[6] = PageCount == MIFARE_ULTRALIGHT_EV11_PAGES ? 0x0B : 0x0E; + Buffer[7] = 0x03; + ISO14443AAppendCRCA(Buffer, 8); + return (8 + ISO14443A_CRCA_SIZE) * 8; + } + + case CMD_FAST_READ: { + uint8_t StartPageAddress = Buffer[1]; + uint8_t EndPageAddress = Buffer[2]; + /* Validation */ + if ((StartPageAddress > EndPageAddress) || (StartPageAddress >= PageCount) || (EndPageAddress >= PageCount)) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + /* Check authentication only if protection is read&write */ + if (ReadAccessProtected) { + if (!VerifyAuthentication(StartPageAddress) || !VerifyAuthentication(EndPageAddress)) { + Buffer[0] = NAK_AUTH_REQUIRED; + return NAK_FRAME_SIZE; + } + } + /* NOTE: With the current implementation, reading the password out is possible. */ + ByteCount = (EndPageAddress - StartPageAddress + 1) * BYTES_PER_PAGE; + MemoryReadBlock(Buffer, StartPageAddress * BYTES_PER_PAGE, ByteCount); + ISO14443AAppendCRCA(Buffer, ByteCount); + return (ByteCount + ISO14443A_CRCA_SIZE) * 8; + } + + case CMD_PWD_AUTH: { + uint8_t ConfigAreaAddress = PageCount * BYTES_PER_PAGE - CONFIG_AREA_SIZE; + uint8_t Password[4]; + MemoryReadBlock(Password, ConfigAreaAddress + CONF_PASSWORD_OFFSET, 4); + if (Password[0] != Buffer[1] || Password[1] != Buffer[2] || Password[2] != Buffer[3] || Password[3] != Buffer[4]) { + Buffer[0] = NAK_AUTH_REQUIRED; + return NAK_FRAME_SIZE; + } + Authenticated = 1; + MemoryReadBlock(Buffer, ConfigAreaAddress + CONF_PACK_OFFSET, 2); + ISO14443AAppendCRCA(Buffer, 2); + return (2 + ISO14443A_CRCA_SIZE) * 8; + } + + case CMD_READ_CNT: { + uint8_t CounterId = Buffer[1]; + /* Validation */ + if (CounterId > CNT_MAX) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + MemoryReadBlock(Buffer, (PageCount + CounterId) * BYTES_PER_PAGE, BYTES_PER_PAGE); + ISO14443AAppendCRCA(Buffer, BYTES_PER_PAGE); + return (BYTES_PER_PAGE + ISO14443A_CRCA_SIZE) * 8; + } + + case CMD_INCREMENT_CNT: { + uint8_t CounterId = Buffer[1]; + uint32_t Addend = (Buffer[0]) | (Buffer[1] << 8) | ((uint32_t)Buffer[2] << 16); + uint32_t Counter; + /* Validation */ + if (CounterId > CNT_MAX) { + Buffer[0] = NAK_INVALID_ARG; + return NAK_FRAME_SIZE; + } + MemoryReadBlock(&Counter, (PageCount + CounterId) * BYTES_PER_PAGE, BYTES_PER_PAGE); + Counter += Addend; + if (Counter > CNT_MAX_VALUE) { + Buffer[0] = NAK_CTR_ERROR; + return NAK_FRAME_SIZE; + } + MemoryWriteBlock(&Counter, (PageCount + CounterId) * BYTES_PER_PAGE, BYTES_PER_PAGE); + Buffer[0] = ACK_VALUE; + return ACK_FRAME_SIZE; + } + + case CMD_READ_SIG: + /* Hardcoded response */ + memset(Buffer, 0xCA, 32); + ISO14443AAppendCRCA(Buffer, 32); + return (32 + ISO14443A_CRCA_SIZE) * 8; + + case CMD_CHECK_TEARING_EVENT: + /* Hardcoded response */ + Buffer[0] = 0xBD; + ISO14443AAppendCRCA(Buffer, 1); + return (1 + ISO14443A_CRCA_SIZE) * 8; + + case CMD_VCSL: + /* Hardcoded response */ + Buffer[0] = 0x05; + ISO14443AAppendCRCA(Buffer, 1); + return (1 + ISO14443A_CRCA_SIZE) * 8; + + default: + break; + } + } + /* Command not handled. Switch to idle. */ + State = STATE_IDLE; + return ISO14443A_APP_NO_RESPONSE; +} uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount) { uint8_t Cmd = Buffer[0]; + uint16_t ByteCount; switch(State) { case STATE_IDLE: @@ -144,124 +454,13 @@ uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount) break; case STATE_ACTIVE: + /* Preserve incoming data length */ + ByteCount = (BitCount + 7) >> 3; if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE, FromHalt)) { State = FromHalt ? STATE_HALT : STATE_IDLE; return ISO14443A_APP_NO_RESPONSE; - } else if (Cmd == CMD_READ) { - uint8_t PageAddress = Buffer[1]; - - if (ISO14443ACheckCRCA(Buffer, CMD_READ_FRAME_SIZE)) { - if ( (PageAddress >= PAGE_READ_MIN) - && (PageAddress <= PAGE_READ_MAX) ) { - /* TODO: Missing address wrap around behaviour. - * Implement using a for-loop copying 4 bytes each iteration - * and mask pageaddress */ - MemoryReadBlock(Buffer, PageAddress * BYTES_PER_PAGE, BYTES_PER_READ); - ISO14443AAppendCRCA(Buffer, BYTES_PER_READ); - return (BYTES_PER_READ + ISO14443A_CRCA_SIZE) * 8; - } else { - Buffer[0] = NAK_INVALID_ARG; - return NAK_FRAME_SIZE; - } - } else { - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; - } - } else if (Cmd == CMD_WRITE) { - /* This is a write command containing 4 bytes of data that - * should be written to the given page address. */ - uint8_t PageAddress = Buffer[1]; - if (ISO14443ACheckCRCA(Buffer, CMD_WRITE_FRAME_SIZE)) { - /* CRC check passed */ - if ( (PageAddress >= PAGE_WRITE_MIN) - && (PageAddress <= PAGE_WRITE_MAX) ) { - /* PageAddress is within bounds. */ - - if (!ActiveConfiguration.ReadOnly) { - MemoryWriteBlock(&Buffer[2], PageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE); - } else { - /* If the chameleon is in read only mode, it silently - * ignores any attempt to write data. */ - } - - Buffer[0] = ACK_VALUE; - return ACK_FRAME_SIZE; - - } else { - Buffer[0] = NAK_INVALID_ARG; - return NAK_FRAME_SIZE; - } - } else { - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; - } - } else if (Cmd == CMD_COMPAT_WRITE) { - /* The Mifare compatbility write command is a 2-frame command. - * The first frame contains the page-address and the second frame - * holds the data. */ - uint8_t PageAddress = Buffer[1]; - - if (ISO14443ACheckCRCA(Buffer, CMD_COMPAT_WRITE_FRAME_SIZE)) { - if ( (PageAddress >= PAGE_WRITE_MIN) - && (PageAddress <= PAGE_WRITE_MAX) ) { - /* CRC check passed and page-address is within bounds. - * Store address and proceed to receiving the data. */ - CompatWritePageAddress = PageAddress; - State = STATE_COMPAT_WRITE; - - Buffer[0] = ACK_VALUE; - return ACK_FRAME_SIZE; - } else { - Buffer[0] = NAK_INVALID_ARG; - return NAK_FRAME_SIZE; - } - } else { - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; - } - } else if (Cmd == CMD_HALT) { - /* Halts the tag. According to the ISO14443, the second - * byte is supposed to be 0. */ - if (Buffer[1] == 0) { - if (ISO14443ACheckCRCA(Buffer, 2)) { - /* According to ISO14443, we must not send anything - * in order to acknowledge the HALT command. */ - State = STATE_HALT; - return ISO14443A_APP_NO_RESPONSE; - } else { - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; - } - } else { - Buffer[0] = NAK_INVALID_ARG; - return NAK_FRAME_SIZE; - } - } else { - /* Unknown command. Enter halt state */ - State = STATE_IDLE; - } - break; - - case STATE_COMPAT_WRITE: - /* Compatibility write. Receiving 16 bytes of data of which 4 bytes are valid. */ - if (ISO14443ACheckCRCA(Buffer, BYTES_PER_COMPAT_WRITE)) { - /* We don't perform any checks here. You will be able to program the - * whole memory. Also there is no OTP behaviour. */ - if (!ActiveConfiguration.ReadOnly) { - MemoryWriteBlock(Buffer, CompatWritePageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE); - } else { - /* If we are told to be read only, we silently ignore the write command - * and pretend to have written data. */ - } - - State = STATE_ACTIVE; - Buffer[0] = ACK_VALUE; - return ACK_FRAME_SIZE; - } else { - State = STATE_ACTIVE; - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; } + return AppProcess(Buffer, ByteCount); default: /* Unknown state? Should never happen. */ diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.h b/Firmware/Chameleon-Mini/Application/MifareUltralight.h index 275685c..61577de 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.h +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.h @@ -12,9 +12,15 @@ #include "ISO14443-3A.h" #define MIFARE_ULTRALIGHT_UID_SIZE ISO14443A_UID_SIZE_DOUBLE -#define MIFARE_ULTRALIGHT_MEM_SIZE 64 +#define MIFARE_ULTRALIGHT_PAGE_SIZE 4 +#define MIFARE_ULTRALIGHT_PAGES 16 +#define MIFARE_ULTRALIGHT_EV11_PAGES 20 +#define MIFARE_ULTRALIGHT_EV12_PAGES 41 +#define MIFARE_ULTRALIGHT_MEM_SIZE (MIFARE_ULTRALIGHT_PAGES * MIFARE_ULTRALIGHT_PAGE_SIZE) void MifareUltralightAppInit(void); +void MifareUltralightEV11AppInit(void); +void MifareUltralightEV12AppInit(void); void MifareUltralightAppReset(void); void MifareUltralightAppTask(void); diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index df0707c..2bf5039 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -16,6 +16,7 @@ static const MapEntryType PROGMEM ConfigurationMap[] = { { .Id = CONFIG_NONE, .Text = "NONE" }, #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT { .Id = CONFIG_MF_ULTRALIGHT, .Text = "MF_ULTRALIGHT" }, + { .Id = CONFIG_MF_ULTRALIGHT_EV1_80B, .Text = "MF_ULTRALIGHT_EV1_80B" }, #endif #ifdef CONFIG_MF_CLASSIC_1K_SUPPORT { .Id = CONFIG_MF_CLASSIC_1K, .Text = "MF_CLASSIC_1K" }, @@ -85,6 +86,36 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .MemorySize = MIFARE_ULTRALIGHT_MEM_SIZE, .ReadOnly = false }, + [CONFIG_MF_ULTRALIGHT_EV1_80B] = { + .CodecInitFunc = ISO14443ACodecInit, + .CodecDeInitFunc = ISO14443ACodecDeInit, + .CodecTaskFunc = ISO14443ACodecTask, + .ApplicationInitFunc = MifareUltralightEV11AppInit, + .ApplicationResetFunc = MifareUltralightAppReset, + .ApplicationTaskFunc = MifareUltralightAppTask, + .ApplicationTickFunc = ApplicationTickDummy, + .ApplicationProcessFunc = MifareUltralightAppProcess, + .ApplicationGetUidFunc = MifareUltralightGetUid, + .ApplicationSetUidFunc = MifareUltralightSetUid, + .UidSize = MIFARE_ULTRALIGHT_UID_SIZE, + .MemorySize = MIFARE_ULTRALIGHT_MEM_SIZE, + .ReadOnly = false + }, + [CONFIG_MF_ULTRALIGHT_EV1_164B] = { + .CodecInitFunc = ISO14443ACodecInit, + .CodecDeInitFunc = ISO14443ACodecDeInit, + .CodecTaskFunc = ISO14443ACodecTask, + .ApplicationInitFunc = MifareUltralightEV12AppInit, + .ApplicationResetFunc = MifareUltralightAppReset, + .ApplicationTaskFunc = MifareUltralightAppTask, + .ApplicationTickFunc = ApplicationTickDummy, + .ApplicationProcessFunc = MifareUltralightAppProcess, + .ApplicationGetUidFunc = MifareUltralightGetUid, + .ApplicationSetUidFunc = MifareUltralightSetUid, + .UidSize = MIFARE_ULTRALIGHT_UID_SIZE, + .MemorySize = MIFARE_ULTRALIGHT_MEM_SIZE, + .ReadOnly = false + }, #endif #ifdef CONFIG_MF_CLASSIC_1K_SUPPORT [CONFIG_MF_CLASSIC_1K] = { diff --git a/Firmware/Chameleon-Mini/Configuration.h b/Firmware/Chameleon-Mini/Configuration.h index 52d1e23..2ca4944 100644 --- a/Firmware/Chameleon-Mini/Configuration.h +++ b/Firmware/Chameleon-Mini/Configuration.h @@ -22,6 +22,8 @@ typedef enum { #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT CONFIG_MF_ULTRALIGHT, + CONFIG_MF_ULTRALIGHT_EV1_80B, + CONFIG_MF_ULTRALIGHT_EV1_164B, #endif #ifdef CONFIG_MF_CLASSIC_1K_SUPPORT CONFIG_MF_CLASSIC_1K,