From: Kevin Townsend Date: Mon, 15 Aug 2011 22:01:27 +0000 (+0200) Subject: Added basic support for common Mifare cards X-Git-Url: https://git.rohieb.name/hackover2013-badge-firmware.git/commitdiff_plain/218f2b0a7b5820a21365c9689010d5fffb329549 Added basic support for common Mifare cards --- diff --git a/drivers/sensors/pn532/helpers/pn532_mifare.h b/drivers/sensors/pn532/helpers/pn532_mifare.h new file mode 100644 index 0000000..fedebeb --- /dev/null +++ b/drivers/sensors/pn532/helpers/pn532_mifare.h @@ -0,0 +1,29 @@ +/**************************************************************************/ +/*! + @file pn532_mifare.h +*/ +/**************************************************************************/ + +#ifndef __PN532_MIFARE_H__ +#define __PN532_MIFARE_H__ + +#include "projectconfig.h" + +// These may need to be enlarged for multi card support +#define PN532_RESPONSELEN_INLISTPASSIVETARGET (32) +#define PN532_RESPONSELEN_INDATAEXCHANGE (32) + +typedef enum pn532_mifare_cmd_e +{ + PN532_MIFARE_CMD_AUTH_A = 0x60, + PN532_MIFARE_CMD_AUTH_B = 0x61, + PN532_MIFARE_CMD_READ = 0x30, + PN532_MIFARE_CMD_WRITE = 0xA0, + PN532_MIFARE_CMD_TRANSFER = 0xB0, + PN532_MIFARE_CMD_DECREMENT = 0xC0, + PN532_MIFARE_CMD_INCREMENT = 0xC1, + PN532_MIFARE_CMD_STORE = 0xC2 +} +pn532_mifare_cmd_t; + +#endif diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_classic.c b/drivers/sensors/pn532/helpers/pn532_mifare_classic.c new file mode 100644 index 0000000..b30156a --- /dev/null +++ b/drivers/sensors/pn532/helpers/pn532_mifare_classic.c @@ -0,0 +1,457 @@ +/**************************************************************************/ +/*! + @file pn532_mifare_classic.c +*/ +/**************************************************************************/ + +/* MIFARE CLASSIC DESCRIPTION + ========================== + + MIFARE Classic cards come in 1K and 4K varieties. While several + varieties of chips exist, the two main chipsets used are described + in the following publicly accessible documents: + + MF1S503x Mifare Classic 1K data sheet: + http://www.nxp.com/documents/data_sheet/MF1S503x.pdf + + MF1S70yyX MIFARE Classic 4K data sheet: + http://www.nxp.com/documents/data_sheet/MF1S70YYX.pdf + + Mifare Classic cards typically have a a 4-byte NUID, though you may + find cards with 7 byte IDs as well + + EEPROM MEMORY + ============= + Mifare Classic cards have either 1K or 4K of EEPROM memory. Each + memory block can be configured with different access conditions, + with two seperate authentication keys present in each block. + + The two main Mifare Classic card types are organised as follows: + + 1K Cards: 16 sectors of 4 blocks (0..15) + 4K Cards: 32 sectors of 4 blocks (0..31) and + 8 sectors of 16 blocks (32..39) + + 4 block sectors + =============== + Sector Block Bytes Description + ------ ----- ----- ----------- + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + 1 3 [-------KEY A-------] [Access Bits] [-------KEY A-------] Sector Trailer 1 + 2 [ Data ] Data + 1 [ Data ] Data + 0 [ Data ] Data + + 0 3 [-------KEY A-------] [Access Bits] [-------KEY A-------] Sector Trailer 1 + 2 [ Data ] Data + 1 [ Data ] Data + 0 [ Manufacturer Data ] Manufacturer Block + + Sector Trailer (Block 3) + ------------------------ + The sector trailer block contains the two secret keys (Key A and Key B), as well + as the access conditions for the four blocks. It has the following structure: + + Sector Trailer Bytes + -------------------------------------------------------------- + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + [ Key A ] [Access Bits] [ Key B ] + + For more information in using Keys to access the clock contents, see + Accessing Data Blocks further below. + + Data Blocks (Blocks 0..2) + ------------------------- + Data blocks are 16 bytes wide and, depending on the permissions set in the + access bits, can be read from and written to. You are free to use the 16 data + bytes in any way you wish. You can easily store text input, store four 32-bit + integer values, a 16 character uri, etc. + + Data Blocks as "Value Blocks" + ----------------------------- + An alternative to storing random data in the 16 byte-wide blocks is to + configure them as "Value Blocks". Value blocks allow performing electronic + purse functions (valid commands are: read, write, increment, decrement, + restore, transfer). + + Each Value block contains a single signed 32-bit value, and this value is + stored 3 times for data integrity and security reasons. It is stored twice + non-inverted, and once inverted. The last 4 bytes are used for a 1-byte + address, which is stored 4 times (twice non-inverted, and twice inverted). + + Data blocks configured as "Value Blocks" have the following structure: + + Value Block Bytes + -------------------------------------------------------------- + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + [ Value ] [ ~Value ] [ Value ] [A ~A A ~A] + + Manufacturer Block (Sector 0, Block 0) + -------------------------------------- + Sector 0 is special since it contains the Manufacturer Block. This block + contains the manufacturer data, and is read-only. It should be avoided + unless you know what you are doing. + + 16 block sectors + ================ + 16 block sectors are identical to 4 block sectors, but with more data blocks. The same + structure described in the 4 block sectors above applies. + + Sector Block Bytes Description + ------ ----- ----- ---------- + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + 32 15 [-------KEY A-------] [Access Bits] [-------KEY B-------] Sector Trailer 32 + 14 [ Data ] Data + 13 [ Data ] Data + ... + 2 [ Data ] Data + 1 [ Data ] Data + 0 [ Data ] Data + + ACCESSING DATA BLOCKS + ===================== + + Before you can access the cards, you must following two steps: + + 1.) You must retrieve the 7 byte UID or the 4-byte NUID of the card. + This can be done using pn532_mifareclassic_WaitForPassiveTarget() + below, which will return the appropriate ID. + + 2.) You must authenticate the sector you wish to access according to the + access rules defined in the Sector Trailer block for that sector. + This can be done using pn532_mifareclassic_AuthenticateBlock(), + passing in the appropriate key value. + + Most new cards have a default Key A of 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF, + but some common values worth trying are: + + 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF + 0XD3 0XF7 0XD3 0XF7 0XD3 0XF7 + 0XA0 0XA1 0XA2 0XA3 0XA4 0XA5 + 0XB0 0XB1 0XB2 0XB3 0XB4 0XB5 + 0X4D 0X3A 0X99 0XC3 0X51 0XDD + 0X1A 0X98 0X2C 0X7E 0X45 0X9A + 0XAA 0XBB 0XCC 0XDD 0XEE 0XFF + 0X00 0X00 0X00 0X00 0X00 0X00 + 0XAB 0XCD 0XEF 0X12 0X34 0X56 + + 3.) Once authenication has succeeded, and depending on the sector + permissions, you can then read/write/increment/decrement the + contents of the specific block, using one of the helper functions + included in this module. + +*/ + +#include + +#include "../pn532.h" +#include "../pn532_bus.h" +#include "pn532_mifare.h" +#include "pn532_mifare_classic.h" + +#include "core/systick/systick.h" + +/**************************************************************************/ +/*! + Indicates whether the specified block number is the first block + in the sector (block 0 relative to the current sector) +*/ +/**************************************************************************/ +bool is_first_block (uint32_t uiBlock) +{ + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock) % 4 == 0); + else + return ((uiBlock) % 16 == 0); +} + +/**************************************************************************/ +/*! + Indicates whether the specified block number is the sector trailer +*/ +/**************************************************************************/ +bool is_trailer_block (uint32_t uiBlock) +{ + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock + 1) % 4 == 0); + else + return ((uiBlock + 1) % 16 == 0); +} + +/**************************************************************************/ +/*! + Tries to detect MIFARE targets in passive mode. This needs to be done + before anything useful can be accomplished with a tag since you need + the tag's unique UID to communicate with it. + + @param pbtCUID Pointer to the byte array where the card's UID + will be stored once a card is detected + @param pszUIDLen Pointer to the size of the card UID in bytes + + Response for a valid ISO14443A 106KBPS (Mifare Classic, etc.) + should be in the following format. See UM0701-02 section + 7.3.5 for more information + + byte Description + ------------- ------------------------------------------ + b0..6 Frame header and preamble + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID + + SENS_RES SEL_RES Manufacturer/Card Type NFCID Len + -------- ------- ----------------------- --------- + 00 04 08 NXP Mifare Classic 1K 4 bytes + 00 02 18 NXP Mifare Classic 4K 4 bytes + + @note Possible error messages are: + + - PN532_ERROR_WRONGCARDTYPE +*/ +/**************************************************************************/ +pn532_error_t pn532_mifareclassic_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen) +{ + byte_t abtResponse[PN532_RESPONSELEN_INLISTPASSIVETARGET]; + pn532_error_t error; + size_t szLen; + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Waiting for an ISO14443A Card%s", CFG_PRINTF_NEWLINE); + #endif + + /* Try to initialise a single ISO14443A tag at 106KBPS */ + /* Note: To wait for a card with a known UID, append the four byte */ + /* UID to the end of the command. */ + byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS}; + error = pn532Write(abtCommand, sizeof(abtCommand)); + if (error) + return error; + + /* Wait until we get a valid response or a timeout */ + do + { + systickDelay(25); + error = pn532Read(abtResponse, &szLen); + } while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + if (error) + return error; + + /* Check SENSE_RES to make sure this is a Mifare Classic card */ + /* Classic 1K = 00 04 */ + /* Classic 4K = 00 02 */ + /* Classic Emulated = 00 08 */ + if ((abtResponse[10] == 0x02) || + (abtResponse[10] == 0x04) || + (abtResponse[10] == 0x08)) + { + /* Card appears to be Mifare Classic */ + *szCUIDLen = abtResponse[12]; + for (uint8_t i=0; i < *szCUIDLen; i++) + { + pbtCUID[i] = abtResponse[13+i]; + } + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Card Found: %s", CFG_PRINTF_NEWLINE); + PN532_DEBUG(" ATQA: "); + pn532PrintHex(abtResponse+9, 2); + PN532_DEBUG(" SAK: %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID: "); + pn532PrintHex(pbtCUID, *szCUIDLen); + #endif + } + else + { + /* Card is ISO14443A but doesn't appear to be Mifare Classic */ + /* Mifare Ultralight = 0x0044 */ + /* Mifare DESFire = 0x0344 */ + /* Innovision Jewel = 0x0C00 */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Wrong Card Type (Expected ATQA 00 02, 00 04 or 00 08) %s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + PN532_DEBUG(" ATQA : "); + pn532PrintHex(abtResponse+9, 2); + PN532_DEBUG(" SAK : %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID Length : %d%s", abtResponse[12], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID : "); + size_t pos; + for (pos=0; pos < abtResponse[12]; pos++) + { + printf("%02x ", abtResponse[13 + pos]); + } + printf("%s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + #endif + return PN532_ERROR_WRONGCARDTYPE; + } + + return PN532_ERROR_NONE; +} + + +/**************************************************************************/ +/*! + Tries to authenticate a block of memory on a MIFARE card using the + INDATAEXCHANGE command. See section 7.3.8 of the PN532 User Manual + for more information on sending MIFARE and other commands. + + @param pbtCUID Pointer to a byte array containing the card UID + @param szCUIDLen The length (in bytes) of the card's UID (Should + be 4 for MIFARE Classic) + @param uiBlockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param uiKeyType Which key type to use during authentication + (PN532_MIFARE_CMD_AUTH_A or PN532_MIFARE_CMD_AUTH_B) + @param pbtKeys Pointer to a byte array containing the 6 byte + key value +*/ +/**************************************************************************/ +pn532_error_t pn532_mifareclassic_AuthenticateBlock (byte_t * pbtCUID, size_t szCUIDLen, uint32_t uiBlockNumber, uint8_t uiKeyType, byte_t * pbtKeys) +{ + pn532_error_t error; + byte_t abtCommand[17]; + byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE]; + size_t szLen; + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Trying to authenticate card "); + pn532PrintHex(pbtCUID, szCUIDLen); + #endif + + /* Prepare the authentication command */ + abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ + abtCommand[1] = 1; /* Max card numbers */ + abtCommand[2] = (uiKeyType) ? PN532_MIFARE_CMD_AUTH_A : PN532_MIFARE_CMD_AUTH_B; + abtCommand[3] = uiBlockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ + memcpy (abtCommand+4, pbtKeys, 6); + uint8_t i; + for (i = 0; i < szCUIDLen; i++) + { + abtCommand[10+i] = pbtCUID[i]; /* 4 byte card ID */ + } + + /* Send the command */ + error = pn532Write(abtCommand, 10+szCUIDLen); + if (error) + { + /* Problem with the serial bus, etc. */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Authentification failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + /* Read the authentification response */ + memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE); + do + { + systickDelay(25); + error = pn532Read(abtResponse, &szLen); + } + while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + if (error) + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Authentification failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + // ToDo: How to check if authentification really worked (bad key, etc.)? + + /* Output the authentification data */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Authenticated block %d %s", uiBlockNumber, CFG_PRINTF_NEWLINE); + #endif + + // Return OK signal + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + Tries to read an entire 16-byte data block at the specified block + address. + + @param uiBlockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param pbtData Pointer to the byte array that will hold the + retrieved data (if any) + + @note Possible error messages are: + + - PN532_ERROR_BLOCKREADFAILED +*/ +/**************************************************************************/ +pn532_error_t pn532_mifareclassic_ReadDataBlock (uint8_t uiBlockNumber, byte_t * pbtData) +{ + pn532_error_t error; + byte_t abtCommand[4]; + byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE]; + size_t szLen; + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Reading 16 bytes at block %03d%s", uiBlockNumber, CFG_PRINTF_NEWLINE); + #endif + + /* Prepare the command */ + abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE; + abtCommand[1] = 1; /* Card number */ + abtCommand[2] = PN532_MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + abtCommand[3] = uiBlockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + + /* Send the commands */ + error = pn532Write(abtCommand, sizeof(abtCommand)); + if (error) + { + /* Bus error, etc. */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + /* Read the response */ + memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE); + do + { + systickDelay(50); + error = pn532Read(abtResponse, &szLen); + } + while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + if (error) + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + /* Make sure we have a valid response (should be 26 bytes long) */ + if (szLen == 26) + { + /* Copy the 16 data bytes to the output buffer */ + /* Block content starts at byte 8 of a valid response */ + memcpy (pbtData, abtResponse+7, 16); + } + else + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Unexpected response reading block %d. Bad key?%s", uiBlockNumber, CFG_PRINTF_NEWLINE); + #endif + return PN532_ERROR_BLOCKREADFAILED; + } + + /* Display data for debug if requested */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Block %03d: ", uiBlockNumber, CFG_PRINTF_NEWLINE); + pn532PrintHexVerbose(pbtData, 16); + #endif + + // Return OK signal + return PN532_ERROR_NONE; +} diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_classic.h b/drivers/sensors/pn532/helpers/pn532_mifare_classic.h new file mode 100644 index 0000000..81177e9 --- /dev/null +++ b/drivers/sensors/pn532/helpers/pn532_mifare_classic.h @@ -0,0 +1,17 @@ +/**************************************************************************/ +/*! + @file pn532_mifare_classic.h +*/ +/**************************************************************************/ + +#ifndef __PN532_MIFARE_CLASSIC_H__ +#define __PN532_MIFARE_CLASSIC_H__ + +#include "projectconfig.h" +#include "pn532_mifare.h" + +pn532_error_t pn532_mifareclassic_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen); +pn532_error_t pn532_mifareclassic_AuthenticateBlock (byte_t * pbtCUID, size_t szCUIDLen, uint32_t uiBlockNumber, uint8_t uiKeyType, byte_t * pbtKeys); +pn532_error_t pn532_mifareclassic_ReadDataBlock (uint8_t uiBlockNumber, byte_t * pbtData); + +#endif diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c new file mode 100644 index 0000000..f22f4c6 --- /dev/null +++ b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c @@ -0,0 +1,314 @@ +/**************************************************************************/ +/*! + @file pn532_mifare_ultralight.c +*/ +/**************************************************************************/ + +/* MIFARE ULTRALIGHT DESCRIPTION + ============================= + + MIFARE Ultralight cards typically contain 512 bits (64 bytes) of + memory, including 4 bytes (32-bits) of OTP (One Time Programmable) + memory where the individual bits can be written but not erased. + + MF0ICU1 Mifare Ultralight Functional Specification: + http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + + + Mifare Ultralight cards have a 7-byte UID + + EEPROM MEMORY + ============= + Mifare Ultralight cards have 512 bits (64 bytes) of EEPROM memory, + including 4 byte (32 bits) of OTP memory. Unlike Mifare Classic cards, + there is no authentication on a per block level, although the blocks + can be set to "read-only" mode using Lock Bytes (described below). + + EEPROM memory is organised into 16 pages of four bytes eachs, in + the following order + + Page Description + ---- ------------ + 0 Serial Number (4 bytes) + 1 Serial Number (4 bytes) + 2 Byte 0: Serial Number + Byte 1: Internal Memory + Byte 2..3: lock bytes + 3 One-time programmable memory (4 bytes) + 4..15 User memory (4 bytes) + + Lock Bytes (Page 2) + ------------------- + Bytes 2 and 3 of page 2 are referred to as "Lock Bytes". Each + page from 0x03 and higher can individually locked by setting the + corresponding locking bit to "1" to prevent further write access, + effectively making the memory read only. + + For information on the lock byte mechanism, refer to section 8.5.2 of + the datasheet (referenced above). + + OTP Bytes (Page 3) + ------------------ + Page 3 is the OTP memory, and by default all bits on this page are + set to 0. These bits can be bitwise modified using the Mifare WRITE + command, and individual bits can be set to 1, but can not be changed + back to 0. + + Data Pages (Pages 4..15) + ------------------------ + Pages 4 to 15 are can be freely read from and written to, + provided there is no conflict with the Lock Bytes described above. + + After production, the bytes have the following default values: + + Page Byte Values + ---- ---------------------- + 0 1 2 3 + 4 0xFF 0xFF 0xFF 0xFF + 5..15 0x00 0x00 0x00 0x00 + + ACCESSING DATA BLOCKS + ===================== + + Before you can access the cards, you must following two steps: + + 1.) 'Connect' to a Mifare Ultralight card and retrieve the 7 byte + UID of the card. + + 2.) Memory can be read and written directly once a passive mode + connection has been made. No authentication is required for + Mifare Ultralight cards. + +*/ + +#include + +#include "../pn532.h" +#include "../pn532_bus.h" +#include "pn532_mifare_ultralight.h" + +#include "core/systick/systick.h" + +/**************************************************************************/ +/*! + Tries to detect MIFARE targets in passive mode. + + @param pbtCUID Pointer to the byte array where the card's 7 byte + UID will be stored once a card is detected + @param pszUIDLen Pointer to the size of the card UID in bytes + + Response for a valid ISO14443A 106KBPS (Mifare Ultralight, etc.) + should be in the following format. See UM0701-02 section + 7.3.5 for more information + + byte Description + ------------- ------------------------------------------ + b0..6 Frame header and preamble + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID + + SENS_RES SEL_RES Manufacturer/Card Type NFCID Len + -------- ------- ----------------------- --------- + 00 44 00 NXP Mifare Ultralight 7 bytes + + @note Possible error messages are: + + - PN532_ERROR_WRONGCARDTYPE +*/ +/**************************************************************************/ +pn532_error_t pn532_mifareultralight_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen) +{ + byte_t abtResponse[PN532_RESPONSELEN_INLISTPASSIVETARGET]; + pn532_error_t error; + size_t szLen; + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Waiting for an ISO14443A Card%s", CFG_PRINTF_NEWLINE); + #endif + + /* Try to initialise a single ISO14443A tag at 106KBPS */ + /* Note: To wait for a card with a known UID, append the four byte */ + /* UID to the end of the command. */ + byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS}; + error = pn532Write(abtCommand, sizeof(abtCommand)); + if (error) + return error; + + /* Wait until we get a valid response or a timeout */ + do + { + systickDelay(25); + error = pn532Read(abtResponse, &szLen); + } while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + if (error) + return error; + + /* Check SENS_RES to make sure this is a Mifare Ultralight card */ + /* Mifare Ultralight = 00 44 */ + if (abtResponse[10] == 0x44) + { + /* Card appears to be Mifare Ultralight */ + *szCUIDLen = abtResponse[12]; + for (uint8_t i=0; i < *szCUIDLen; i++) + { + pbtCUID[i] = abtResponse[13+i]; + } + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Card Found: %s", CFG_PRINTF_NEWLINE); + PN532_DEBUG(" ATQA: "); + pn532PrintHex(abtResponse+9, 2); + PN532_DEBUG(" SAK: %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID: "); + pn532PrintHex(pbtCUID, *szCUIDLen); + #endif + } + else + { + /* Card is ISO14443A but doesn't appear to be Mifare Ultralight */ + /* Mifare Classic = 0x0002, 0x0004, 0x0008 */ + /* Mifare DESFire = 0x0344 */ + /* Innovision Jewel = 0x0C00 */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Wrong Card Type (Expected ATQA 00 44) %s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + PN532_DEBUG(" ATQA : "); + pn532PrintHex(abtResponse+9, 2); + PN532_DEBUG(" SAK : %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID Length : %d%s", abtResponse[12], CFG_PRINTF_NEWLINE); + PN532_DEBUG(" UID : "); + size_t pos; + for (pos=0; pos < abtResponse[12]; pos++) + { + printf("%02x ", abtResponse[13 + pos]); + } + printf("%s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE); + #endif + return PN532_ERROR_WRONGCARDTYPE; + } + + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + Tries to read an entire 4-byte page at the specified address. + + @param page The page number (0..63 in most cases) + @param pbtBuffer Pointer to the byte array that will hold the + retrieved data (if any) + + @note Possible error messages are: + + - PN532_ERROR_ADDRESSOUTOFRANGE + - PN532_ERROR_BLOCKREADFAILED +*/ +/**************************************************************************/ +pn532_error_t pn532_mifareultralight_ReadPage (uint8_t page, byte_t * pbtBuffer) +{ + pn532_error_t error; + byte_t abtCommand[4]; + byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE]; + size_t szLen; + + if (page >= 64) + { + return PN532_ERROR_ADDRESSOUTOFRANGE; + } + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Reading page %03d%s", page, CFG_PRINTF_NEWLINE); + #endif + + /* Prepare the command */ + abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE; + abtCommand[1] = 1; /* Card number */ + abtCommand[2] = PN532_MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + abtCommand[3] = page; /* Page Number (0..63 in most cases) */ + + /* Send the commands */ + error = pn532Write(abtCommand, sizeof(abtCommand)); + if (error) + { + /* Bus error, etc. */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + /* Read the response */ + memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE); + do + { + systickDelay(50); + error = pn532Read(abtResponse, &szLen); + } + while (error == PN532_ERROR_RESPONSEBUFFEREMPTY); + if (error) + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE); + #endif + return error; + } + + /* Make sure we have a valid response (should be 26 bytes long) */ + if (szLen == 26) + { + /* Copy the 4 data bytes to the output buffer */ + /* Block content starts at byte 8 of a valid response */ + /* Note that the command actually reads 16 byte or 4 */ + /* pages at a time ... we simply discard the last 12 */ + /* bytes */ + memcpy (pbtBuffer, abtResponse+8, 4); + } + else + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Unexpected response reading block %d. Bad key?%s", page, CFG_PRINTF_NEWLINE); + #endif + return PN532_ERROR_BLOCKREADFAILED; + } + + /* Display data for debug if requested */ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Page %02d: ", page, CFG_PRINTF_NEWLINE); + pn532PrintHexVerbose(pbtBuffer, 4); + #endif + + // Return OK signal + return PN532_ERROR_NONE; +} + +//static bool +//read_card (void) +//{ +// uint32_t page; +// bool bFailure = false; +// uint32_t uiReadedPages = 0; +// +// printf ("Reading %d pages |", uiBlocks + 1); +// +// for (page = 0; page <= uiBlocks; page += 4) { +// // Try to read out the data block +// if (nfc_initiator_mifare_cmd (pnd, MC_READ, page, &mp)) { +// memcpy (mtDump.amb[page / 4].mbd.abtData, mp.mpd.abtData, 16); +// } else { +// bFailure = true; +// break; +// } +// +// print_success_or_failure (bFailure, &uiReadedPages); +// print_success_or_failure (bFailure, &uiReadedPages); +// print_success_or_failure (bFailure, &uiReadedPages); +// print_success_or_failure (bFailure, &uiReadedPages); +// } +// printf ("|\n"); +// printf ("Done, %d of %d pages readed.\n", uiReadedPages, uiBlocks + 1); +// fflush (stdout); +// +// return (!bFailure); +//} diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h new file mode 100644 index 0000000..78239c9 --- /dev/null +++ b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h @@ -0,0 +1,16 @@ +/**************************************************************************/ +/*! + @file pn532_mifare_ultralight.h +*/ +/**************************************************************************/ + +#ifndef __PN532_MIFARE_ULTRALIGHT_H__ +#define __PN532_MIFARE_ULTRALIGHT_H__ + +#include "projectconfig.h" +#include "pn532_mifare.h" + +pn532_error_t pn532_mifareultralight_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen); +pn532_error_t pn532_mifareultralight_ReadPage (uint8_t page, byte_t * pbtBuffer); + +#endif diff --git a/drivers/sensors/pn532/pn532.c b/drivers/sensors/pn532/pn532.c index ca91aba..78eae7f 100644 --- a/drivers/sensors/pn532/pn532.c +++ b/drivers/sensors/pn532/pn532.c @@ -41,7 +41,7 @@ void pn532PrintHex(const byte_t * pbtData, const size_t szBytes) @param szBytes Data length in bytes */ /**************************************************************************/ -void pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes) +void pn532PrintHexChar(const byte_t * pbtData, const size_t szBytes) { size_t szPos; for (szPos=0; szPos < szBytes; szPos++) @@ -51,7 +51,7 @@ void pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes) printf(" "); for (szPos=0; szPos < szBytes; szPos++) { - printf("%c", pbtData[szPos] == 0 ? '.' : pbtData[szPos]); + printf("%c", pbtData[szPos] <= 0x1F ? '.' : pbtData[szPos]); } printf(CFG_PRINTF_NEWLINE); } diff --git a/drivers/sensors/pn532/pn532.h b/drivers/sensors/pn532/pn532.h index a48ad1c..d6fa5ac 100644 --- a/drivers/sensors/pn532/pn532.h +++ b/drivers/sensors/pn532/pn532.h @@ -10,7 +10,7 @@ #include "projectconfig.h" // Comment out this line to disable debug output -#define PN532_DEBUGMODE +// #define PN532_DEBUGMODE #define PN532_DEBUG(fmt, args...) printf(fmt, ##args) /* Error messages generated by the stack */ @@ -134,7 +134,7 @@ typedef struct } pn532_pcb_t; void pn532PrintHex(const byte_t * pbtData, const size_t szBytes); -void pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes); +void pn532PrintHexChar(const byte_t * pbtData, const size_t szBytes); pn532_pcb_t * pn532GetPCB(); void pn532Init(); pn532_error_t pn532Read(byte_t *pbtResponse, size_t * pszLen); diff --git a/drivers/sensors/pn532/pn532_bus_uart.c b/drivers/sensors/pn532/pn532_bus_uart.c new file mode 100644 index 0000000..981df74 --- /dev/null +++ b/drivers/sensors/pn532/pn532_bus_uart.c @@ -0,0 +1,298 @@ +/**************************************************************************/ +/*! + @file pn532_bus_uart.c +*/ +/**************************************************************************/ +#include + +#include "pn532.h" +#include "pn532_bus.h" + +#ifdef PN532_BUS_UART + +#include "core/systick/systick.h" +#include "core/gpio/gpio.h" +#include "core/uart/uart.h" + +/**************************************************************************/ +/*! + @brief Builds a standard PN532 frame using the supplied data + + @param pbtFrame Pointer to the field that will hold the frame data + @param pszFrame Pointer to the field that will hold the frame length + @param pbtData Pointer to the data to insert in a frame + @param swData Length of the data to insert in bytes + + @note Possible error messages are: + + - PN532_ERROR_EXTENDEDFRAME +*/ +/**************************************************************************/ +pn532_error_t pn532_bus_BuildFrame(byte_t * pbtFrame, size_t * pszFrame, const byte_t * pbtData, const size_t szData) +{ + if (szData > PN532_NORMAL_FRAME__DATA_MAX_LEN) + { + // Extended frames currently unsupported + return PN532_ERROR_EXTENDEDFRAME; + } + + // LEN - Packet length = data length (len) + checksum (1) + end of stream marker (1) + pbtFrame[3] = szData + 1; + // LCS - Packet length checksum + pbtFrame[4] = 256 - (szData + 1); + // TFI + pbtFrame[5] = 0xD4; + // DATA - Copy the PN53X command into the packet buffer + memcpy (pbtFrame + 6, pbtData, szData); + + // DCS - Calculate data payload checksum + byte_t btDCS = (256 - 0xD4); + size_t szPos; + for (szPos = 0; szPos < szData; szPos++) + { + btDCS -= pbtData[szPos]; + } + pbtFrame[6 + szData] = btDCS; + + // 0x00 - End of stream marker + pbtFrame[szData + 7] = 0x00; + + (*pszFrame) = szData + PN532_NORMAL_FRAME__OVERHEAD; + + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Initialises UART and configures the PN532 +*/ +/**************************************************************************/ +void pn532_bus_HWInit(void) +{ + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Initialising UART (%d)%s", PN532_UART_BAUDRATE, CFG_PRINTF_NEWLINE); + #endif + uartInit(PN532_UART_BAUDRATE); + + // Set reset pin as output and reset device + gpioSetDir(PN532_RSTPD_PORT, PN532_RSTPD_PIN, gpioDirection_Output); + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Resetting the PN532...\r\n"); + #endif + gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 0); + systickDelay(400); + gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 1); + + // Wait for the PN532 to finish booting + systickDelay(100); +} + +/**************************************************************************/ +/*! + @brief Sends the specified command to the PN532, automatically + creating an appropriate frame for it + + @param pdbData Pointer to the byte data to send + @param szData Length in bytes of the data to send + + @note Possible error messages are: + + - PN532_ERROR_BUSY + - PN532_ERROR_NOACK + - PN532_ERROR_INVALIDACK +*/ +/**************************************************************************/ +pn532_error_t pn532_bus_SendCommand(const byte_t * pbtData, const size_t szData) +{ + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check if we're busy + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + + // Flag the stack as busy + pn532->state = PN532_STATE_BUSY; + + // Every packet must start with "00 00 ff" + byte_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; + size_t szFrame = 0; + + // Build the frame + pn532_bus_BuildFrame (abtFrame, &szFrame, pbtData, szData); + + // Keep track of the last command that was sent + pn532->lastCommand = pbtData[0]; + + // Output the frame data for debugging if requested + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Sending (%02d): ", szFrame); + pn532PrintHex(abtFrame, szFrame); + #endif + + // Send data to the PN532 + uartSend (abtFrame, szFrame); + + // Wait for ACK + byte_t abtRxBuf[6]; + uart_pcb_t *uart = uartGetPCB(); + systickDelay(10); // FIXME: How long should we wait for ACK? + if (uart->rxfifo.len < 6) + { + // Unable to read ACK + #ifdef PN532_DEBUGMODE + PN532_DEBUG ("Unable to read ACK%s", CFG_PRINTF_NEWLINE); + #endif + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NOACK; + } + + // Read ACK ... this will also remove it from the buffer + const byte_t abtAck[6] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 }; + abtRxBuf[0] = uartRxBufferRead(); + abtRxBuf[1] = uartRxBufferRead(); + abtRxBuf[2] = uartRxBufferRead(); + abtRxBuf[3] = uartRxBufferRead(); + abtRxBuf[4] = uartRxBufferRead(); + abtRxBuf[5] = uartRxBufferRead(); + + // Make sure the received ACK matches the prototype + if (0 != (memcmp (abtRxBuf, abtAck, 6))) + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG ("Invalid ACK: "); + pn532PrintHex(abtRxBuf, 6); + PN532_DEBUG("%s", CFG_PRINTF_NEWLINE); + #endif + pn532->state = PN532_STATE_READY; + return PN532_ERROR_INVALIDACK; + } + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Reads a response from the PN532 + + @note Possible error message are: + + - PN532_ERROR_BUSY + - PN532_ERROR_RESPONSEBUFFEREMPTY + - PN532_ERROR_PREAMBLEMISMATCH + - PN532_ERROR_APPLEVELERROR + - PN532_ERROR_EXTENDEDFRAME + - PN532_ERROR_LENCHECKSUMMISMATCH +*/ +/**************************************************************************/ +pn532_error_t pn532_bus_ReadResponse(byte_t * pbtResponse, size_t * pszRxLen) +{ + pn532_pcb_t *pn532 = pn532GetPCB(); + + // Check if we're busy + if (pn532->state == PN532_STATE_BUSY) + { + return PN532_ERROR_BUSY; + } + + // Flag the stack as busy + pn532->state = PN532_STATE_BUSY; + + // Reset the app error flag + pn532->appError = PN532_APPERROR_NONE; + + // Read response from uart + if (!uartRxBufferReadArray(pbtResponse, pszRxLen)) + { + pn532->state = PN532_STATE_READY; + return PN532_ERROR_RESPONSEBUFFEREMPTY; + } + + // Display the raw response data for debugging if requested + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Received (%02d): ", *pszRxLen); + pn532PrintHex(pbtResponse, *pszRxLen); + #endif + + // Check preamble + const byte_t pn53x_preamble[3] = { 0x00, 0x00, 0xff }; + if (0 != (memcmp (pbtResponse, pn53x_preamble, 3))) + { + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Frame preamble + start code mismatch%s", CFG_PRINTF_NEWLINE); + #endif + pn532->state = PN532_STATE_READY; + return PN532_ERROR_PREAMBLEMISMATCH; + } + + // Check the frame type + if ((0x01 == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Error frame + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Application level error (0x%02x)%s", pbtResponse[5], CFG_PRINTF_NEWLINE); + #endif + // Set application error message ID + pn532->appError = pbtResponse[5]; + pn532->state = PN532_STATE_READY; + return PN532_ERROR_APPLEVELERROR; + } + else if ((0xff == pbtResponse[3]) && (0xff == pbtResponse[4])) + { + // Extended frame + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Extended frames currently unsupported%s", CFG_PRINTF_NEWLINE); + #endif + pn532->state = PN532_STATE_READY; + return PN532_ERROR_EXTENDEDFRAME; + } + else + { + // Normal frame + if (256 != (pbtResponse[3] + pbtResponse[4])) + { + // TODO: Retry + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Length checksum mismatch%s", CFG_PRINTF_NEWLINE); + #endif + pn532->state = PN532_STATE_READY; + return PN532_ERROR_LENCHECKSUMMISMATCH; + } + // size_t szPayloadLen = abtRx[3] - 2; + } + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +/**************************************************************************/ +/*! + @brief Sends the wakeup sequence to the PN532. +*/ +/**************************************************************************/ +pn532_error_t pn532_bus_Wakeup(void) +{ + size_t szLen; + byte_t abtWakeUp[] = { 0x55,0x55,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 }; + + pn532_pcb_t *pn532 = pn532GetPCB(); + + #ifdef PN532_DEBUGMODE + PN532_DEBUG("Sending Wakeup Sequence%s", CFG_PRINTF_NEWLINE); + #endif + uartSend(abtWakeUp,sizeof(abtWakeUp)); + systickDelay(100); + + byte_t response[32]; + pn532_bus_ReadResponse(response, &szLen); + + // Todo: Check for error ... currently throws a checksum error + // that isn't really an error + + pn532->state = PN532_STATE_READY; + return PN532_ERROR_NONE; +} + +#endif // #ifdef PN532_BUS_UART \ No newline at end of file