PN532 NFC library for Seeed Studio's NFC Shield
Fork of PN532 by
Revision 3:4189a10038e6, committed 2013-11-21
- Comitter:
- yihui
- Date:
- Thu Nov 21 04:30:49 2013 +0000
- Parent:
- 2:f618fb2169c4
- Child:
- 4:0774b8298eb8
- Commit message:
- sync with https://github.com/Seeed-Studio/PN532/releases/tag/v0.9.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MifareClassic.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,275 @@ +#include "MifareClassic.h" +#include "PN532_debug.h" + +#define BLOCK_SIZE 16 +#define LONG_TLV_SIZE 4 +#define SHORT_TLV_SIZE 2 + +#define MIFARE_CLASSIC ("Mifare Classic") + +MifareClassic::MifareClassic(PN532& nfcShield) +{ + _nfcShield = &nfcShield; +} + +MifareClassic::~MifareClassic() +{ +} + +NfcTag MifareClassic::read(uint8_t *uid, unsigned int uidLength) +{ + uint8_t key[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; + int currentBlock = 4; + int messageStartIndex = 0; + int messageLength = 0; + uint8_t data[BLOCK_SIZE]; + + // read first block to get message length + int success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (success) + { + success = _nfcShield->mifareclassic_ReadDataBlock(currentBlock, data); + if (success) + { + if (!decodeTlv(data, messageLength, messageStartIndex)) { + return NfcTag(uid, uidLength, "ERROR"); // TODO should the error message go in NfcTag? + } + } + else + { + DMSG("Error. Failed read block "); + DMSG_INT(currentBlock); + return NfcTag(uid, uidLength, MIFARE_CLASSIC); + } + } + else + { + DMSG("Tag is not NDEF formatted."); + // TODO set tag.isFormatted = false + return NfcTag(uid, uidLength, MIFARE_CLASSIC); + } + + // this should be nested in the message length loop + int index = 0; + int bufferSize = getBufferSize(messageLength); + uint8_t buffer[bufferSize]; + + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Message Length "); + DMSG_INT(messageLength); + DMSG("Buffer Size "); + DMSG_INT(bufferSize); + #endif + + while (index < bufferSize) + { + + // authenticate on every sector + if (_nfcShield->mifareclassic_IsFirstBlock(currentBlock)) + { + success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (!success) + { + DMSG("Error. Block Authentication failed for "); + DMSG_INT(currentBlock); + // TODO error handling + } + } + + // read the data + success = _nfcShield->mifareclassic_ReadDataBlock(currentBlock, &buffer[index]); + if (success) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Block "); + DMSG_INT(currentBlock); + _nfcShield->PrintHexChar(&buffer[index], BLOCK_SIZE); + #endif + } + else + { + DMSG("Read failed "); + DMSG_INT(currentBlock); + // TODO handle errors here + } + + index += BLOCK_SIZE; + currentBlock++; + + // skip the trailer block + if (_nfcShield->mifareclassic_IsTrailerBlock(currentBlock)) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Skipping block "); + DMSG_INT(currentBlock); + #endif + currentBlock++; + } + } + + return NfcTag(uid, uidLength, MIFARE_CLASSIC, &buffer[messageStartIndex], messageLength); +} + +int MifareClassic::getBufferSize(int messageLength) +{ + + int bufferSize = messageLength; + + // TLV header is 2 or 4 uint8_ts, TLV terminator is 1 uint8_t. + if (messageLength < 0xFF) + { + bufferSize += SHORT_TLV_SIZE + 1; + } + else + { + bufferSize += LONG_TLV_SIZE + 1; + } + + // bufferSize needs to be a multiple of BLOCK_SIZE + if (bufferSize % BLOCK_SIZE != 0) + { + bufferSize = ((bufferSize / BLOCK_SIZE) + 1) * BLOCK_SIZE; + } + + return bufferSize; +} + +// skip null tlvs (0x0) before the real message +// technically unlimited null tlvs, but we assume +// T & L of TLV in the first block we read +int MifareClassic::getNdefStartIndex(uint8_t *data) +{ + + for (int i = 0; i < BLOCK_SIZE; i++) + { + if (data[i] == 0x0) + { + // do nothing, skip + } + else if (data[i] == 0x3) + { + return i; + } + else + { + DMSG("Unknown TLV "); + DMSG_HEX(data[i]); + return -2; + } + } + + return -1; +} + +// Decode the NDEF data length from the Mifare TLV +// Leading null TLVs (0x0) are skipped +// Assuming T & L of TLV will be in the first block +// messageLength and messageStartIndex written to the parameters +// success or failure status is returned +// +// { 0x3, LENGTH } +// { 0x3, 0xFF, LENGTH, LENGTH } +bool MifareClassic::decodeTlv(uint8_t *data, int &messageLength, int &messageStartIndex) +{ + int i = getNdefStartIndex(data); + + if (i < 0 || data[i] != 0x3) + { + DMSG("Error. Can't decode message length."); + return false; + } + else + { + if (data[i+1] == 0xFF) + { + messageLength = ((0xFF & data[i+2]) << 8) | (0xFF & data[i+3]); + messageStartIndex = i + LONG_TLV_SIZE; + } + else + { + messageLength = data[i+1]; + messageStartIndex = i + SHORT_TLV_SIZE; + } + } + + return true; +} + +bool MifareClassic::write(NdefMessage& m, uint8_t * uid, unsigned int uidLength) +{ + + uint8_t encoded[m.getEncodedSize()]; + m.encode(encoded); + + uint8_t buffer[getBufferSize(sizeof(encoded))]; + memset(buffer, 0, sizeof(buffer)); + + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("sizeof(encoded) "));DMSG(sizeof(encoded); + DMSG("sizeof(buffer) "));DMSG(sizeof(buffer); + #endif + + if (sizeof(encoded) < 0xFF) + { + buffer[0] = 0x3; + buffer[1] = sizeof(encoded); + memcpy(&buffer[2], encoded, sizeof(encoded)); + buffer[2+sizeof(encoded)] = 0xFE; // terminator + } + else + { + buffer[0] = 0x3; + buffer[1] = 0xFF; + buffer[2] = ((sizeof(encoded) >> 8) & 0xFF); + buffer[3] = (sizeof(encoded) & 0xFF); + memcpy(&buffer[4], encoded, sizeof(encoded)); + buffer[4+sizeof(encoded)] = 0xFE; // terminator + } + + // Write to tag + int index = 0; + int currentBlock = 4; + uint8_t key[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; // this is Sector 1 - 15 key + + while (index < sizeof(buffer)) + { + + if (_nfcShield->mifareclassic_IsFirstBlock(currentBlock)) + { + int success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (!success) + { + DMSG("Error. Block Authentication failed for ");DMSG_INT(currentBlock); + return false; + } + } + + int write_success = _nfcShield->mifareclassic_WriteDataBlock (currentBlock, &buffer[index]); + if (write_success) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Wrote block ");Serial.print(currentBlock);DMSG(" - "); + _nfcShield->PrintHexChar(&buffer[index], BLOCK_SIZE); + #endif + } + else + { + DMSG("Write failed ");DMSG_INT(currentBlock); + return false; + } + index += BLOCK_SIZE; + currentBlock++; + + if (_nfcShield->mifareclassic_IsTrailerBlock(currentBlock)) + { + // can't write to trailer block + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Skipping block ");DMSG(currentBlock); + #endif + currentBlock++; + } + + } + + return true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MifareClassic.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,22 @@ +#ifndef MifareClassic_h +#define MifareClassic_h + +#include <PN532.h> +#include <Ndef.h> +#include <NfcTag.h> + +class MifareClassic +{ + public: + MifareClassic(PN532& nfcShield); + ~MifareClassic(); + NfcTag read(uint8_t *uid, unsigned int uidLength); + bool write(NdefMessage& ndefMessage, uint8_t *uid, unsigned int uidLength); + private: + PN532* _nfcShield; + int getBufferSize(int messageLength); + int getNdefStartIndex(uint8_t *data); + bool decodeTlv(uint8_t *data, int &messageLength, int &messageStartIndex); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MifareUltralight.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,165 @@ +#include "MifareUltralight.h" + +#include "PN532_debug.h" + +#define ULTRALIGHT_PAGE_SIZE 4 +#define ULTRALIGHT_READ_SIZE 4 // we should be able to read 16 uint8_ts at a time + +#define ULTRALIGHT_DATA_START_PAGE 4 +#define ULTRALIGHT_MESSAGE_LENGTH_INDEX 1 +#define ULTRALIGHT_DATA_START_INDEX 2 +#define ULTRALIGHT_MAX_PAGE 63 + +#define NFC_FORUM_TAG_TYPE_2 ("NFC Forum Type 2") + +MifareUltralight::MifareUltralight(PN532& nfcShield) +{ + nfc = &nfcShield; + ndefStartIndex = 0; + messageLength = 0; +} + +MifareUltralight::~MifareUltralight() +{ +} + +NfcTag MifareUltralight::read(uint8_t * uid, unsigned int uidLength) +{ + if (isUnformatted()) + { + DMSG("WARNING: Tag is not formatted."); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); + } + + readCapabilityContainer(); // meta info for tag + findNdefMessage(); + calculateBufferSize(); + + if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE + NdefMessage message = NdefMessage(); + message.addEmptyRecord(); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, message); + } + + bool success; + uint8_t page; + uint8_t index = 0; + uint8_t buffer[bufferSize]; + for (page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page++) + { + // read the data + success = nfc->mifareultralight_ReadPage(page, &buffer[index]); + if (success) + { + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Page ");Serial.print(page);DMSG(" "); + nfc->PrintHexChar(&buffer[index], ULTRALIGHT_PAGE_SIZE); + #endif + } + else + { + DMSG("Read failed ");DMSG_INT(page); + // TODO error handling + messageLength = 0; + break; + } + + if (index >= (messageLength + ndefStartIndex)) + { + break; + } + + index += ULTRALIGHT_PAGE_SIZE; + } + + NdefMessage ndefMessage = NdefMessage(&buffer[ndefStartIndex], messageLength); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, ndefMessage); + +} + +bool MifareUltralight::isUnformatted() +{ + uint8_t page = 4; + uint8_t data[ULTRALIGHT_READ_SIZE]; + bool success = nfc->mifareultralight_ReadPage (page, data); + if (success) + { + return (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF); + } + else + { + DMSG("Error. Failed read page ");DMSG_INT(page); + return false; + } +} + +// page 3 has tag capabilities +void MifareUltralight::readCapabilityContainer() +{ + uint8_t data[ULTRALIGHT_PAGE_SIZE]; + int success = nfc->mifareultralight_ReadPage (3, data); + if (success) + { + // See AN1303 - different rules for Mifare Family uint8_t2 = (additional data + 48)/8 + tagCapacity = data[2] * 8; + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Tag capacity "));Serial.print(tagCapacity);DMSG(F(" uint8_ts"); + #endif + + // TODO future versions should get lock information + } +} + +// read enough of the message to find the ndef message length +void MifareUltralight::findNdefMessage() +{ + int page; + uint8_t data[12]; // 3 pages + uint8_t* data_ptr = &data[0]; + + // the nxp read command reads 4 pages, unfortunately adafruit give me one page at a time + bool success = true; + for (page = 4; page < 6; page++) + { + success = success && nfc->mifareultralight_ReadPage(page, data_ptr); + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Page "));Serial.print(page);Serial.print(F(" - "); + nfc->PrintHexChar(data_ptr, 4); + #endif + data_ptr += ULTRALIGHT_PAGE_SIZE; + } + + if (success) + { + if (data[0] == 0x03) + { + messageLength = data[1]; + ndefStartIndex = 2; + } + else if (data[5] == 0x3) // page 5 uint8_t 1 + { + // TODO should really read the lock control TLV to ensure uint8_t[5] is correct + messageLength = data[6]; + ndefStartIndex = 7; + } + } + + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("messageLength ");DMSG(messageLength); + DMSG("ndefStartIndex ");DMSG(ndefStartIndex); + #endif +} + +// buffer is larger than the message, need to handle some data before and after +// message and need to ensure we read full pages +void MifareUltralight::calculateBufferSize() +{ + // TLV terminator 0xFE is 1 uint8_t + bufferSize = messageLength + ndefStartIndex + 1; + + if (bufferSize % ULTRALIGHT_READ_SIZE != 0) + { + // buffer must be an increment of page size + bufferSize = ((bufferSize / ULTRALIGHT_READ_SIZE) + 1) * ULTRALIGHT_READ_SIZE; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MifareUltralight.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,26 @@ +#ifndef MifareUltralight_h +#define MifareUltralight_h + +#include <PN532.h> +#include <NfcTag.h> +#include <Ndef.h> + +class MifareUltralight +{ + public: + MifareUltralight(PN532& nfcShield); + ~MifareUltralight(); + NfcTag read(uint8_t *uid, unsigned int uidLength); + private: + PN532* nfc; + unsigned int tagCapacity; + unsigned int messageLength; + unsigned int bufferSize; + unsigned int ndefStartIndex; + bool isUnformatted(); + void readCapabilityContainer(); + void findNdefMessage(); + void calculateBufferSize(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Ndef.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,52 @@ +#include "Ndef.h" +#include "PN532_debug.h" + +// Borrowed from Adafruit_NFCShield_I2C +void PrintHex(const uint8_t * data, const long numuint8_ts) +{ + uint32_t szPos; + for (szPos=0; szPos < numuint8_ts; szPos++) + { + DMSG("0x"); + // Append leading 0 for small values + if (data[szPos] <= 0xF) + DMSG("0"); + DMSG_HEX(data[szPos]&0xff); + if ((numuint8_ts > 1) && (szPos != numuint8_ts - 1)) + { + DMSG(" "); + } + } + DMSG(""); +} + +// Borrowed from Adafruit_NFCShield_I2C +void PrintHexChar(const uint8_t * data, const long numuint8_ts) +{ + uint32_t szPos; + for (szPos=0; szPos < numuint8_ts; szPos++) + { + // Append leading 0 for small values + DMSG_HEX(data[szPos]); + } + DMSG(" "); + for (szPos=0; szPos < numuint8_ts; szPos++) + { + if (data[szPos] <= 0x1F) + DMSG("."); + else + DMSG("%c", (char)data[szPos]); + } + DMSG("\n"); +} + +// Note if buffer % blockSize != 0, last block will not be written +void DumpHex(const uint8_t * data, const long numuint8_ts, const unsigned int blockSize) +{ + int i; + for (i = 0; i < (numuint8_ts / blockSize); i++) + { + PrintHexChar(data, blockSize); + data += blockSize; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Ndef.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,13 @@ +#ifndef Ndef_h +#define Ndef_h + +/* NOTE: To use the Ndef library in your code, don't include Ndef.h + See README.md for details on which files to include in your sketch. +*/ +#include <stdint.h> + +void PrintHex(const uint8_t *data, const long numuint8_ts); +void PrintHexChar(const uint8_t *data, const long numuint8_ts); +void DumpHex(const uint8_t *data, const long numuint8_ts, const int blockSize); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NdefMessage.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,278 @@ +#include "NdefMessage.h" +#include "PN532_debug.h" + +NdefMessage::NdefMessage(void) +{ + _recordCount = 0; +} + +NdefMessage::NdefMessage(const uint8_t * data, const int numuint8_ts) +{ + #ifdef NDEF_DEBUG + DMSG("Decoding "));Serial.print(numuint8_ts);DMSG(F(" uint8_ts"); + PrintHexChar(data, numuint8_ts); + //DumpHex(data, numuint8_ts, 16); + #endif + + _recordCount = 0; + + int index = 0; + + while (index <= numuint8_ts) + { + + // decode tnf - first uint8_t is tnf with bit flags + // see the NFDEF spec for more info + uint8_t tnf_uint8_t = data[index]; + bool mb = (tnf_uint8_t & 0x80) != 0; + bool me = (tnf_uint8_t & 0x40) != 0; + bool cf = (tnf_uint8_t & 0x20) != 0; + bool sr = (tnf_uint8_t & 0x10) != 0; + bool il = (tnf_uint8_t & 0x8) != 0; + uint8_t tnf = (tnf_uint8_t & 0x7); + + NdefRecord record = NdefRecord(); + record.setTnf(tnf); + + index++; + int typeLength = data[index]; + + int payloadLength = 0; + if (sr) + { + index++; + payloadLength = data[index]; + } + else + { + payloadLength = ((0xFF & data[++index]) << 24) | ((0xFF & data[++index]) << 26) | + ((0xFF & data[++index]) << 8) | (0xFF & data[++index]); + } + + int idLength = 0; + if (il) + { + index++; + idLength = data[index]; + } + + index++; + record.setType(&data[index], typeLength); + index += typeLength; + + if (il) + { + record.setId(&data[index], idLength); + index += idLength; + } + + record.setPayload(&data[index], payloadLength); + index += payloadLength; + + addRecord(record); + + if (me) break; // last message + } + +} + +NdefMessage::NdefMessage(const NdefMessage& rhs) +{ + + _recordCount = rhs._recordCount; + for (int i = 0; i < _recordCount; i++) + { + _records[i] = rhs._records[i]; + } + +} + +NdefMessage::~NdefMessage() +{ +} + +NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) +{ + + if (this != &rhs) + { + + // delete existing records + for (int i = 0; i < _recordCount; i++) + { + // TODO Dave: is this the right way to delete existing records? + _records[i] = NdefRecord(); + } + + _recordCount = rhs._recordCount; + for (int i = 0; i < _recordCount; i++) + { + _records[i] = rhs._records[i]; + } + } + return *this; +} + +unsigned int NdefMessage::getRecordCount() +{ + return _recordCount; +} + +int NdefMessage::getEncodedSize() +{ + int size = 0; + for (int i = 0; i < _recordCount; i++) + { + size += _records[i].getEncodedSize(); + } + return size; +} + +// TODO change this to return uint8_t* +void NdefMessage::encode(uint8_t* data) +{ + // assert sizeof(data) >= getEncodedSize() + uint8_t* data_ptr = &data[0]; + + for (int i = 0; i < _recordCount; i++) + { + _records[i].encode(data_ptr, i == 0, (i + 1) == _recordCount); + // TODO can NdefRecord.encode return the record size? + data_ptr += _records[i].getEncodedSize(); + } + +} + +bool NdefMessage::addRecord(NdefRecord& record) +{ + + if (_recordCount < MAX_NDEF_RECORDS) + { + _records[_recordCount] = record; + _recordCount++; + return true; + } + else + { + DMSG("WARNING: Too many records. Increase MAX_NDEF_RECORDS."); + return false; + } +} + +void NdefMessage::addMimeMediaRecord(string mimeType, string payload) +{ + + uint8_t payloaduint8_ts[payload.length() + 1]; + payload.copy((char*)payloaduint8_ts, sizeof(payloaduint8_ts)); + + addMimeMediaRecord(mimeType, payloaduint8_ts, payload.length()); +} + +void NdefMessage::addMimeMediaRecord(string mimeType, uint8_t* payload, int payloadLength) +{ + NdefRecord r = NdefRecord(); + r.setTnf(TNF_MIME_MEDIA); + + uint8_t type[mimeType.length() + 1]; + mimeType.copy((char*)type, sizeof(type)); + r.setType(type, mimeType.length()); + + r.setPayload(payload, payloadLength); + + addRecord(r); +} + +void NdefMessage::addTextRecord(string text) +{ + addTextRecord(text, "en"); +} + +void NdefMessage::addTextRecord(string text, string encoding) +{ + NdefRecord r = NdefRecord(); + r.setTnf(TNF_WELL_KNOWN); + + uint8_t RTD_TEXT[1] = { 0x54 }; // TODO this should be a constant or preprocessor + r.setType(RTD_TEXT, sizeof(RTD_TEXT)); + + // X is a placeholder for encoding length + // TODO is it more efficient to build w/o string concatenation? + string payloadString = "X" + encoding + text; + + uint8_t payload[payloadString.length() + 1]; + payloadString.copy((char*)payload, sizeof(payload)); + + // replace X with the real encoding length + payload[0] = encoding.length(); + + r.setPayload(payload, payloadString.length()); + + addRecord(r); +} + +void NdefMessage::addUriRecord(string uri) +{ + NdefRecord* r = new NdefRecord(); + r->setTnf(TNF_WELL_KNOWN); + + uint8_t RTD_URI[1] = { 0x55 }; // TODO this should be a constant or preprocessor + r->setType(RTD_URI, sizeof(RTD_URI)); + + // X is a placeholder for identifier code + string payloadString = "X" + uri; + + uint8_t payload[payloadString.length() + 1]; + payloadString.copy((char*)payload, sizeof(payload)); + + // add identifier code 0x0, meaning no prefix substitution + payload[0] = 0x0; + + r->setPayload(payload, payloadString.length()); + + addRecord(*r); + delete(r); +} + +void NdefMessage::addEmptyRecord() +{ + NdefRecord* r = new NdefRecord(); + r->setTnf(TNF_EMPTY); + addRecord(*r); + delete(r); +} + +NdefRecord NdefMessage::getRecord(int index) +{ + if (index > -1 && index < _recordCount) + { + return _records[index]; + } + else + { + return NdefRecord(); // would rather return NULL + } +} + +NdefRecord NdefMessage::operator[](int index) +{ + return getRecord(index); +} + +void NdefMessage::print() +{ + DMSG("\nNDEF Message "); + DMSG_INT(_recordCount); + DMSG(" record"); + if (_recordCount == 1) { + DMSG(", "); + } else { + DMSG("s, "); + } + DMSG_INT(getEncodedSize()); + + int i; + for (i = 0; i < _recordCount; i++) + { + _records[i].print(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NdefMessage.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,39 @@ +#ifndef NdefMessage_h +#define NdefMessage_h + +#include <Ndef.h> +#include <NdefRecord.h> + +#define MAX_NDEF_RECORDS 4 + +class NdefMessage +{ + public: + NdefMessage(void); + NdefMessage(const uint8_t *data, const int numuint8_ts); + NdefMessage(const NdefMessage& rhs); + ~NdefMessage(); + NdefMessage& operator=(const NdefMessage& rhs); + + int getEncodedSize(); // need so we can pass array to encode + void encode(uint8_t *data); + + bool addRecord(NdefRecord& record); + void addMimeMediaRecord(string mimeType, string payload); + void addMimeMediaRecord(string mimeType, uint8_t *payload, int payloadLength); + void addTextRecord(string text); + void addTextRecord(string text, string encoding); + void addUriRecord(string uri); + void addEmptyRecord(); + + unsigned int getRecordCount(); + NdefRecord getRecord(int index); + NdefRecord operator[](int index); + + void print(); + private: + NdefRecord _records[MAX_NDEF_RECORDS]; + unsigned int _recordCount; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NdefRecord.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,363 @@ + +#include <string> +#include <string.h> +#include <stdlib.h> + +#include "NdefRecord.h" +#include "PN532_debug.h" + +NdefRecord::NdefRecord() +{ + //DMSG("NdefRecord Constructor 1"); + _tnf = 0; + _typeLength = 0; + _payloadLength = 0; + _idLength = 0; + _type = (uint8_t *)NULL; + _payload = (uint8_t *)NULL; + _id = (uint8_t *)NULL; +} + +NdefRecord::NdefRecord(const NdefRecord& rhs) +{ + //DMSG("NdefRecord Constructor 2 (copy)"); + + _tnf = rhs._tnf; + _typeLength = rhs._typeLength; + _payloadLength = rhs._payloadLength; + _idLength = rhs._idLength; + _type = (uint8_t *)NULL; + _payload = (uint8_t *)NULL; + _id = (uint8_t *)NULL; + + if (_typeLength) + { + _type = (uint8_t*)malloc(_typeLength); + memcpy(_type, rhs._type, _typeLength); + } + + if (_payloadLength) + { + _payload = (uint8_t*)malloc(_payloadLength); + memcpy(_payload, rhs._payload, _payloadLength); + } + + if (_idLength) + { + _id = (uint8_t*)malloc(_idLength); + memcpy(_id, rhs._id, _idLength); + } + +} + +// TODO NdefRecord::NdefRecord(tnf, type, payload, id) + +NdefRecord::~NdefRecord() +{ + //DMSG("NdefRecord Destructor"); + if (_typeLength) + { + free(_type); + } + + if (_payloadLength) + { + free(_payload); + } + + if (_idLength) + { + free(_id); + } +} + +NdefRecord& NdefRecord::operator=(const NdefRecord& rhs) +{ + //DMSG("NdefRecord ASSIGN"); + + if (this != &rhs) + { + // free existing + if (_typeLength) + { + free(_type); + } + + if (_payloadLength) + { + free(_payload); + } + + if (_idLength) + { + free(_id); + } + + _tnf = rhs._tnf; + _typeLength = rhs._typeLength; + _payloadLength = rhs._payloadLength; + _idLength = rhs._idLength; + + if (_typeLength) + { + _type = (uint8_t*)malloc(_typeLength); + memcpy(_type, rhs._type, _typeLength); + } + + if (_payloadLength) + { + _payload = (uint8_t*)malloc(_payloadLength); + memcpy(_payload, rhs._payload, _payloadLength); + } + + if (_idLength) + { + _id = (uint8_t*)malloc(_idLength); + memcpy(_id, rhs._id, _idLength); + } + } + return *this; +} + +// size of records in uint8_ts +int NdefRecord::getEncodedSize() +{ + int size = 2; // tnf + typeLength + if (_payloadLength > 0xFF) + { + size += 4; + } + else + { + size += 1; + } + + if (_idLength) + { + size += 1; + } + + size += (_typeLength + _payloadLength + _idLength); + + return size; +} + +void NdefRecord::encode(uint8_t *data, bool firstRecord, bool lastRecord) +{ + // assert data > getEncodedSize() + + uint8_t* data_ptr = &data[0]; + + *data_ptr = getTnfuint8_t(firstRecord, lastRecord); + data_ptr += 1; + + *data_ptr = _typeLength; + data_ptr += 1; + + if (_payloadLength <= 0xFF) { // short record + *data_ptr = _payloadLength; + data_ptr += 1; + } else { // long format + // 4 uint8_ts but we store length as an int + data_ptr[0] = 0x0; // (_payloadLength >> 24) & 0xFF; + data_ptr[1] = 0x0; // (_payloadLength >> 16) & 0xFF; + data_ptr[2] = (_payloadLength >> 8) & 0xFF; + data_ptr[3] = _payloadLength & 0xFF; + data_ptr += 4; + } + + if (_idLength) + { + *data_ptr = _idLength; + data_ptr += 1; + } + + //DMSG(2); + memcpy(data_ptr, _type, _typeLength); + data_ptr += _typeLength; + + memcpy(data_ptr, _payload, _payloadLength); + data_ptr += _payloadLength; + + if (_idLength) + { + memcpy(data_ptr, _id, _idLength); + data_ptr += _idLength; + } +} + +uint8_t NdefRecord::getTnfuint8_t(bool firstRecord, bool lastRecord) +{ + int value = _tnf; + + if (firstRecord) { // mb + value = value | 0x80; + } + + if (lastRecord) { // + value = value | 0x40; + } + + // chunked flag is always false for now + // if (cf) { + // value = value | 0x20; + // } + + if (_payloadLength <= 0xFF) { + value = value | 0x10; + } + + if (_idLength) { + value = value | 0x8; + } + + return value; +} + +uint8_t NdefRecord::getTnf() +{ + return _tnf; +} + +void NdefRecord::setTnf(uint8_t tnf) +{ + _tnf = tnf; +} + +unsigned int NdefRecord::getTypeLength() +{ + return _typeLength; +} + +int NdefRecord::getPayloadLength() +{ + return _payloadLength; +} + +unsigned int NdefRecord::getIdLength() +{ + return _idLength; +} + +string NdefRecord::getType() +{ + char type[_typeLength + 1]; + memcpy(type, _type, _typeLength); + type[_typeLength] = '\0'; // null terminate + return string(type); +} + +// this assumes the caller created type correctly +void NdefRecord::getType(uint8_t* type) +{ + memcpy(type, _type, _typeLength); +} + +void NdefRecord::setType(const uint8_t * type, const unsigned int numuint8_ts) +{ + if(_typeLength) + { + free(_type); + } + + _type = (uint8_t*)malloc(numuint8_ts); + memcpy(_type, type, numuint8_ts); + _typeLength = numuint8_ts; +} + +// assumes the caller sized payload properly +void NdefRecord::getPayload(uint8_t *payload) +{ + memcpy(payload, _payload, _payloadLength); +} + +void NdefRecord::setPayload(const uint8_t * payload, const int numuint8_ts) +{ + if (_payloadLength) + { + free(_payload); + } + + _payload = (uint8_t*)malloc(numuint8_ts); + memcpy(_payload, payload, numuint8_ts); + _payloadLength = numuint8_ts; +} + +string NdefRecord::getId() +{ + char id[_idLength + 1]; + memcpy(id, _id, _idLength); + id[_idLength] = '\0'; // null terminate + return string(id); +} + +void NdefRecord::getId(uint8_t *id) +{ + memcpy(id, _id, _idLength); +} + +void NdefRecord::setId(const uint8_t * id, const unsigned int numuint8_ts) +{ + if (_idLength) + { + free(_id); + } + + _id = (uint8_t*)malloc(numuint8_ts); + memcpy(_id, id, numuint8_ts); + _idLength = numuint8_ts; +} + +void NdefRecord::print() +{ + DMSG(" NDEF Record"); + DMSG(" TNF 0x"); + DMSG_HEX(_tnf); + DMSG(" "); + switch (_tnf) { + case TNF_EMPTY: + DMSG("Empty"); + break; + case TNF_WELL_KNOWN: + DMSG("Well Known"); + break; + case TNF_MIME_MEDIA: + DMSG("Mime Media"); + break; + case TNF_ABSOLUTE_URI: + DMSG("Absolute URI"); + break; + case TNF_EXTERNAL_TYPE: + DMSG("External"); + break; + case TNF_UNKNOWN: + DMSG("Unknown"); + break; + case TNF_UNCHANGED: + DMSG("Unchanged"); + break; + case TNF_RESERVED: + DMSG("Reserved"); + break; + default: + DMSG("\n"); + } + DMSG(" Type Length 0x"); + DMSG_HEX(_typeLength); + DMSG(" Payload Length 0x"); + DMSG_HEX(_payloadLength); + if (_idLength) + { + DMSG(" Id Length 0x"); + DMSG_HEX(_idLength); + } + DMSG(" Type ");PrintHexChar(_type, _typeLength); + // TODO chunk large payloads so this is readable + DMSG(" Payload ");PrintHexChar(_payload, _payloadLength); + if (_idLength) + { + DMSG(" Id ");PrintHexChar(_id, _idLength); + } + DMSG(" Record is "); + DMSG_INT(getEncodedSize()); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NdefRecord.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,59 @@ +#ifndef NdefRecord_h +#define NdefRecord_h + +#include <string> +#include <Ndef.h> + +using namespace std; + +#define TNF_EMPTY 0x0 +#define TNF_WELL_KNOWN 0x01 +#define TNF_MIME_MEDIA 0x02 +#define TNF_ABSOLUTE_URI 0x03 +#define TNF_EXTERNAL_TYPE 0x04 +#define TNF_UNKNOWN 0x05 +#define TNF_UNCHANGED 0x06 +#define TNF_RESERVED 0x07 + +class NdefRecord +{ + public: + NdefRecord(); + NdefRecord(const NdefRecord& rhs); + ~NdefRecord(); + NdefRecord& operator=(const NdefRecord& rhs); + + int getEncodedSize(); + void encode(uint8_t *data, bool firstRecord, bool lastRecord); + + unsigned int getTypeLength(); + int getPayloadLength(); + unsigned int getIdLength(); + + uint8_t getTnf(); + void getType(uint8_t *type); + void getPayload(uint8_t *payload); + void getId(uint8_t *id); + + // convenience methods + string getType(); + string getId(); + + void setTnf(uint8_t tnf); + void setType(const uint8_t *type, const unsigned int numuint8_ts); + void setPayload(const uint8_t *payload, const int numuint8_ts); + void setId(const uint8_t *id, const unsigned int numuint8_ts); + + void print(); + private: + uint8_t getTnfuint8_t(bool firstRecord, bool lastRecord); + uint8_t _tnf; // 3 bit + unsigned int _typeLength; + int _payloadLength; + unsigned int _idLength; + uint8_t *_type; + uint8_t *_payload; + uint8_t *_id; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NfcAdapter.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,128 @@ +#include <NfcAdapter.h> +#include <PN532_debug.h> + +NfcAdapter::NfcAdapter(PN532Interface &interface) +{ + shield = new PN532(interface); +} + +NfcAdapter::~NfcAdapter(void) +{ + delete shield; +} + +void NfcAdapter::begin() +{ + shield->begin(); + + uint32_t versiondata = shield->getFirmwareVersion(); + if (! versiondata) { + DMSG("Didn't find PN53x board"); + while (1); // halt + } + + DMSG("Found chip PN5%2X\r\n", versiondata >> 24); + DMSG("Firmware V%d.%d\r\n", (versiondata >> 16) & 0xFF, (versiondata >> 8) & 0xFF); + + // configure board to read RFID tags + shield->SAMConfig(); +} + +bool NfcAdapter::tagPresent() +{ + uint8_t success; + uidLength = 0; + + // TODO is cast of uidLength OK? + success = shield->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, (uint8_t*)&uidLength); + + // if (success) + // { + // DMSG("Found an ISO14443A card"); + // DMSG(" UID Length: ");Serial.print(uidLength, DEC);DMSG(" uint8_ts"); + // DMSG(" UID Value: "); + // shield->PrintHex(uid, uidLength); + // DMSG(""); + // } + + return success; +} + +NfcTag NfcAdapter::read() +{ + + uint8_t type = guessTagType(); + + // TODO need an abstraction of Driver + if (type == TAG_TYPE_MIFARE_CLASSIC) + { + #ifdef NDEF_DEBUG + DMSG("Reading Mifare Classic"); + #endif + MifareClassic mifareClassic = MifareClassic(*shield); + return mifareClassic.read(uid, uidLength); + } + else if (type == TAG_TYPE_2) + { + #ifdef NDEF_DEBUG + DMSG("Reading Mifare Ultralight"); + #endif + MifareUltralight ultralight = MifareUltralight(*shield); + return ultralight.read(uid, uidLength); + } + else if (type == TAG_TYPE_UNKNOWN) + { + DMSG("Can not determine tag type"); + //DMSG("Can not determine tag type for ATQA 0x"); + //Serial.print(atqa, HEX);DMSG(" SAK 0x");DMSG(sak, HEX); + return NfcTag(uid, uidLength); + } + else + { + DMSG("No driver for card type "); + DMSG_INT(type); + // TODO should set type here + return NfcTag(uid, uidLength); + } + +} + +bool NfcAdapter::write(NdefMessage& ndefMessage) +{ + bool success; + + if (uidLength == 4) + { + MifareClassic mifareClassic = MifareClassic(*shield); + success = mifareClassic.write(ndefMessage, uid, uidLength); + } + else + { + DMSG("Unsupported Tag"); + success = false; + } + return success; +} + +// TODO this should return a Driver MifareClassic, MifareUltralight, Type 4, Unknown +// Guess Tag Type by looking at the ATQA and SAK values +// Need to follow spec for Card Identification. Maybe AN1303, AN1305 and ??? +unsigned int NfcAdapter::guessTagType() +{ + + // 4 uint8_t id - Mifare Classic + // - ATQA 0x4 && SAK 0x8 + // 7 uint8_t id + // - ATQA 0x44 && SAK 0x8 - Mifare Classic + // - ATQA 0x44 && SAK 0x0 - Mifare Ultralight NFC Forum Type 2 + // - ATQA 0x344 && SAK 0x20 - NFC Forum Type 4 + + if (uidLength == 4) + { + return TAG_TYPE_MIFARE_CLASSIC; + } + else + { + return TAG_TYPE_2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NfcAdapter.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,43 @@ +#ifndef NfcAdapter_h +#define NfcAdapter_h + +#include <PN532Interface.h> +#include <PN532.h> +#include <NfcTag.h> +#include <Ndef.h> + +// Drivers +#include <MifareClassic.h> +#include <MifareUltralight.h> + +#define TAG_TYPE_MIFARE_CLASSIC (0) +#define TAG_TYPE_1 (1) +#define TAG_TYPE_2 (2) +#define TAG_TYPE_3 (3) +#define TAG_TYPE_4 (4) +#define TAG_TYPE_UNKNOWN (99) + +#define IRQ (2) +#define RESET (3) // Not connected by default on the NFC Shield + +class NfcAdapter { + public: + NfcAdapter(PN532Interface &interface); + + ~NfcAdapter(void); + void begin(void); + bool tagPresent(); // tagAvailable + NfcTag read(); + bool write(NdefMessage& ndefMessage); + // FUTURE bool share(NdefMessage& ndefMessage); + // FUTURE bool unshare(); + // FUTURE bool erase(); + // FUTURE bool format(); + private: + PN532* shield; + uint8_t uid[7]; // Buffer to store the returned UID + unsigned int uidLength; // Length of the UID (4 or 7 uint8_ts depending on ISO14443A card type) + unsigned int guessTagType(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NfcDriver.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,9 @@ +// eventually the NFC drivers should extend this class +class NfcDriver +{ + public: + virtual NfcTag read(uint8_t * uid, int uidLength) = 0; + virtual bool write(NdefMessage& message, uint8_t * uid, int uidLength) = 0; + // erase() + // format() +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NfcTag.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,127 @@ +#include <NfcTag.h> +#include <string.h> +#include <PN532_debug.h> + +NfcTag::NfcTag() +{ + _uid = 0; + _uidLength = 0; + _tagType = "Unknown"; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = "Unknown"; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, NdefMessage& ndefMessage) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = new NdefMessage(ndefMessage); +} + +// I don't like this version, but it will use less memory +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, const uint8_t *ndefData, const int ndefDataLength) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = new NdefMessage(ndefData, ndefDataLength); +} + +NfcTag::~NfcTag() +{ + delete _ndefMessage; +} + +NfcTag& NfcTag::operator=(const NfcTag& rhs) +{ + if (this != &rhs) + { + delete _ndefMessage; + _uid = rhs._uid; + _uidLength = rhs._uidLength; + _tagType = rhs._tagType; + // TODO do I need a copy here? + _ndefMessage = rhs._ndefMessage; + } + return *this; +} + +uint8_t NfcTag::getUidLength() +{ + return _uidLength; +} + +void NfcTag::getUid(uint8_t *uid, unsigned int uidLength) +{ + memcpy(_uid, uid, uidLength); +} + +string NfcTag::getUidString() +{ + string uidString = ""; +#if 0 + for (int i = 0; i < _uidLength; i++) + { + if (i > 0) + { + uidString += " "; + } + + if (_uid[i] < 0xF) + { + uidString += "0"; + } + + uidString += string((unsigned int)_uid[i], 16); + } + uidString.toUpperCase(); +#endif + return uidString; +} + +string NfcTag::getTagType() +{ + return _tagType; +} + +bool NfcTag::hasNdefMessage() +{ + return (_ndefMessage != NULL); +} + +NdefMessage NfcTag::getNdefMessage() +{ + return *_ndefMessage; +} + +void NfcTag::print() +{ + DMSG("NFC Tag - "); + DMSG_INT(_tagType); + DMSG("UID - "); + DMSG(getUidString().c_str()); + if (_ndefMessage == NULL) + { + DMSG("\nNo NDEF Message"); + } + else + { + _ndefMessage->print(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NfcTag.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,33 @@ +#ifndef NfcTag_h +#define NfcTag_h + +#include <stdint.h> +#include <NdefMessage.h> + +class NfcTag +{ + public: + NfcTag(); + NfcTag(uint8_t *uid, unsigned int uidLength); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, NdefMessage& ndefMessage); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, const uint8_t *ndefData, const int ndefDataLength); + ~NfcTag(void); + NfcTag& operator=(const NfcTag& rhs); + uint8_t getUidLength(); + void getUid(uint8_t *uid, unsigned int uidLength); + string getUidString(); + string getTagType(); + bool hasNdefMessage(); + NdefMessage getNdefMessage(); + void print(); + private: + uint8_t *_uid; + unsigned int _uidLength; + string _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown + NdefMessage* _ndefMessage; + // TODO capacity + // TODO isFormatted +}; + +#endif
--- a/PN532.cpp Thu Oct 17 06:51:32 2013 +0000 +++ b/PN532.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -1,73 +1,22 @@ /**************************************************************************/ /*! @file PN532.cpp - @author Adafruit Industries & Seeed Technology Inc. - - NXP's PN532 NFC/13.56MHz RFID Transceiver - - @section HISTORY - v1.6 - Ported to mbed - - v1.5 - Modified to support I2C and SPI - - v1.4 - Added setPassiveActivationRetries() - - v1.3 - Modified to work with I2C - - v1.2 - Added writeGPIO() - - Added readGPIO() - - v1.1 - Changed readPassiveTargetID() to handle multiple UID sizes - - Added the following helper functions for text display - static void PrintHex(const uint8_t * data, const uint32_t numBytes) - static void PrintHexChar(const uint8_t * pbtData, const uint32_t numBytes) - - Added the following Mifare Classic functions: - bool mifareclassic_IsFirstBlock (uint32_t uiBlock) - bool mifareclassic_IsTrailerBlock (uint32_t uiBlock) - uint8_t mifareclassic_AuthenticateBlock (uint8_t * uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t * keyData) - uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t * data) - uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t * data) - - Added the following Mifare Ultalight functions: - uint8_t mifareultralight_ReadPage (uint8_t page, uint8_t * buffer) + @author Adafruit Industries & Seeed Studio + @license BSD */ /**************************************************************************/ +#include "PN532.h" +#include "PN532_debug.h" #include <string.h> -#include "PN532.h" -#include "debug.h" - -#include "PN532_SPI.h" #define HAL(func) (_interface->func) -uint8_t pn532response_firmwarevers[] = {0x00, 0xFF, 0x06, 0xFA, 0xD5, 0x03}; - -// Uncomment these lines to enable debug output for PN532(I2C) and/or MIFARE related code -#define PN532DEBUG -// #define MIFAREDEBUG - -#define PN532_PACKBUFFSIZ 128 -uint8_t pn532_packetbuffer[PN532_PACKBUFFSIZ]; - - PN532::PN532(PN532Interface &interface) { _interface = &interface; } -PN532::PN532(PinName mosi, PinName miso, PinName sclk, PinName cs) -{ - _spi = new SPI(mosi, miso, sclk); - _interface = new PN532_SPI(_spi, cs); -} - -PN532::~PN532() -{ - if (_spi) { - delete _spi; - } -} - /**************************************************************************/ /*! @brief Setups the HW @@ -89,11 +38,22 @@ /**************************************************************************/ void PN532::PrintHex(const uint8_t *data, const uint32_t numBytes) { +#ifdef ARDUINO for (uint8_t i = 0; i < numBytes; i++) { - DMSG("0x"); - DMSG_HEX(data[i]); + if (data[i] < 0x10) { + Serial.print(" 0"); + } else { + Serial.print(' '); + } + Serial.print(data[i], HEX); } - DMSG("\n"); + Serial.println(""); +#else + for (uint8_t i = 0; i < numBytes; i++) { + printf(" %2X", data[i]); + } + printf("\n"); +#endif } /**************************************************************************/ @@ -109,11 +69,40 @@ /**************************************************************************/ void PN532::PrintHexChar(const uint8_t *data, const uint32_t numBytes) { +#ifdef ARDUINO for (uint8_t i = 0; i < numBytes; i++) { - DMSG("0x"); - DMSG_HEX(data[i]); + if (data[i] < 0x10) { + Serial.print(" 0"); + } else { + Serial.print(' '); + } + Serial.print(data[i], HEX); + } + Serial.print(" "); + for (uint8_t i = 0; i < numBytes; i++) { + char c = data[i]; + if (c <= 0x1f || c > 0x7f) { + Serial.print('.'); + } else { + Serial.print(c); + } } - DMSG("\n"); + Serial.println(""); +#else + for (uint8_t i = 0; i < numBytes; i++) { + printf(" %2X", data[i]); + } + printf(" "); + for (uint8_t i = 0; i < numBytes; i++) { + char c = data[i]; + if (c <= 0x1f || c > 0x7f) { + printf("."); + } else { + printf("%c", c); + } + printf("\n"); + } +#endif } /**************************************************************************/ @@ -247,6 +236,8 @@ pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second pn532_packetbuffer[3] = 0x01; // use IRQ pin! + DMSG("SAMConfig\n"); + if (HAL(writeCommand)(pn532_packetbuffer, 4)) return false; @@ -274,7 +265,7 @@ if (HAL(writeCommand)(pn532_packetbuffer, 5)) return 0x0; // no ACK - return 1; + return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); } /***** ISO14443A Commands ******/ @@ -670,44 +661,34 @@ /**************************************************************************/ bool PN532::inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength) { - if (sendLength > PN532_PACKBUFFSIZ - 2) { - DMSG("APDU length too long for packet buffer"); - - return false; - } uint8_t i; pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE; pn532_packetbuffer[1] = inListedTag; - for (i = 0; i < sendLength; ++i) { - pn532_packetbuffer[i + 2] = send[i]; - } - if (HAL(writeCommand)(pn532_packetbuffer, sendLength + 2)) { + if (HAL(writeCommand)(pn532_packetbuffer, 2, send, sendLength)) { return false; } - - int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 1000); + int16_t status = HAL(readResponse)(response, *responseLength, 1000); if (status < 0) { return false; } - uint8_t length = status; - - if ((pn532_packetbuffer[0] & 0x3f) != 0) { + if ((response[0] & 0x3f) != 0) { DMSG("Status code indicates an error\n"); return false; } + uint8_t length = status; length -= 1; if (length > *responseLength) { length = *responseLength; // silent truncation... } - for (i = 0; i < length; ++i) { - response[i] = pn532_packetbuffer[1 + i]; + for (uint8_t i = 0; i < length; i++) { + response[i] = response[i + 1]; } *responseLength = length; @@ -746,12 +727,29 @@ return true; } +int8_t PN532::tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout){ + + int8_t status = HAL(writeCommand)(command, len); + if (status < 0) { + return -1; + } + + status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout); + if (status > 0) { + return 1; + } else if (PN532_TIMEOUT == status) { + return 0; + } else { + return -2; + } +} + /** * Peer to Peer */ -int8_t PN532::tgInitAsTarget() +int8_t PN532::tgInitAsTarget(uint16_t timeout) { - static const uint8_t command[] = { + const uint8_t command[] = { PN532_COMMAND_TGINITASTARGET, 0, 0x00, 0x00, //SENS_RES @@ -766,54 +764,49 @@ 0x06, 0x46, 0x66, 0x6D, 0x01, 0x01, 0x10, 0x00// LLCP magic number and version parameter }; + return tgInitAsTarget(command, sizeof(command), timeout); +} - int8_t status = HAL(writeCommand)(command, sizeof(command)); - if (status < 0) { +int16_t PN532::tgGetData(uint8_t *buf, uint8_t len) +{ + buf[0] = PN532_COMMAND_TGGETDATA; + + if (HAL(writeCommand)(buf, 1)) { + return -1; + } + + int16_t status = HAL(readResponse)(buf, len, 3000); + if (0 >= status) { return status; } - return HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 0); -} - -int16_t PN532::tgGetData(uint8_t *buf, uint16_t len) -{ - pn532_packetbuffer[0] = PN532_COMMAND_TGGETDATA; - - if (HAL(writeCommand)(pn532_packetbuffer, 1)) { - return -1; - } + uint16_t length = status - 1; - int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 3000); - if (0 > status) { - return status; - } - uint16_t length = status; - if (length > len) { - return -4; - } - - if (pn532_packetbuffer[0] != 0) { + if (buf[0] != 0) { DMSG("status is not ok\n"); return -5; } - memcpy(buf, pn532_packetbuffer + 1, length - 1); + for (uint8_t i = 0; i < length; i++) { + buf[i] = buf[i + 1]; + } - - return length - 1; + return length; } -bool PN532::tgSetData(const uint8_t *buf, uint16_t len) +bool PN532::tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) { - pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; - if (len > (sizeof(pn532_packetbuffer) + 1)) { + if (hlen > (sizeof(pn532_packetbuffer) - 1)) { return false; } - memcpy(pn532_packetbuffer + 1, buf, len); + for (int8_t i = hlen - 1; i >= 0; i--){ + pn532_packetbuffer[i + 1] = header[i]; + } + pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; - if (HAL(writeCommand)(pn532_packetbuffer, len + 1)) { + if (HAL(writeCommand)(pn532_packetbuffer, hlen + 1, body, blen)) { return false; } @@ -827,3 +820,18 @@ return true; } + +int16_t PN532::inRelease(const uint8_t relevantTarget){ + + pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE; + pn532_packetbuffer[1] = relevantTarget; + + if (HAL(writeCommand)(pn532_packetbuffer, 2)) { + return 0; + } + + // read data packet + return HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); +} + +
--- a/PN532.h Thu Oct 17 06:51:32 2013 +0000 +++ b/PN532.h Thu Nov 21 04:30:49 2013 +0000 @@ -1,37 +1,17 @@ /**************************************************************************/ /*! @file PN532.h - @author Adafruit Industries & Seeed Technology Inc. - - NXP's PN532 NFC/13.56MHz RFID Transceiver - - @section HISTORY - v1.6 - Ported to mbed - - v1.3 - Modified to work with I2C - - v1.1 - Added full command list - - Added 'verbose' mode flag to constructor to toggle debug output - - Changed readPassiveTargetID() to return variable length values - + @author Adafruit Industries & Seeed Studio + @license BSD */ /**************************************************************************/ -#ifndef PN532_h -#define PN532_h +#ifndef __PN532_H__ +#define __PN532_H__ #include <stdint.h> -#include "mbed.h" #include "PN532Interface.h" -#define PN532_PREAMBLE (0x00) -#define PN532_STARTCODE1 (0x00) -#define PN532_STARTCODE2 (0xFF) -#define PN532_POSTAMBLE (0x00) - -#define PN532_HOSTTOPN532 (0xD4) -#define PN532_PN532TOHOST (0xD5) - // PN532 Commands #define PN532_COMMAND_DIAGNOSE (0x00) #define PN532_COMMAND_GETFIRMWAREVERSION (0x02) @@ -70,14 +50,6 @@ #define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) -#define PN532_WAKEUP (0x55) - -#define PN532_I2C_ADDRESS (0x48 >> 1) -#define PN532_I2C_READBIT (0x01) -#define PN532_I2C_BUSY (0x00) -#define PN532_I2C_READY (0x01) -#define PN532_I2C_READYTIMEOUT (20) - #define PN532_MIFARE_ISO14443A (0x00) // Mifare Commands @@ -136,15 +108,10 @@ #define PN532_GPIO_P34 (4) #define PN532_GPIO_P35 (5) -//#define PN532DEBUG - class PN532 { public: PN532(PN532Interface &interface); - PN532(PinName mosi, PinName miso, PinName sclk, PinName cs); - - ~PN532(); void begin(void); @@ -155,9 +122,20 @@ uint8_t readGPIO(void); bool setPassiveActivationRetries(uint8_t maxRetries); - int8_t tgInitAsTarget(); - int16_t tgGetData(uint8_t *buf, uint16_t len); - bool tgSetData(const uint8_t *buf, uint16_t len); + /** + * @brief Init PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t tgInitAsTarget(uint16_t timeout = 0); + int8_t tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout = 0); + + int16_t tgGetData(uint8_t *buf, uint8_t len); + bool tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + int16_t inRelease(const uint8_t relevantTarget = 0); // ISO14443A functions bool inListPassiveTarget(); @@ -180,14 +158,20 @@ static void PrintHex(const uint8_t *data, const uint32_t numBytes); static void PrintHexChar(const uint8_t *pbtData, const uint32_t numBytes); + uint8_t *getBuffer(uint8_t *len) { + *len = sizeof(pn532_packetbuffer) - 4; + return pn532_packetbuffer; + }; + private: uint8_t _uid[7]; // ISO14443A uid uint8_t _uidLen; // uid len uint8_t _key[6]; // Mifare Classic key uint8_t inListedTag; // Tg number of inlisted tag. + uint8_t pn532_packetbuffer[64]; + PN532Interface *_interface; - SPI *_spi; }; #endif
--- a/PN532Interface.h Thu Oct 17 06:51:32 2013 +0000 +++ b/PN532Interface.h Thu Nov 21 04:30:49 2013 +0000 @@ -5,15 +5,15 @@ #include <stdint.h> -#define PN532_PREAMBLE (0x00) -#define PN532_STARTCODE1 (0x00) -#define PN532_STARTCODE2 (0xFF) -#define PN532_POSTAMBLE (0x00) +#define PN532_PREAMBLE (0x00) +#define PN532_STARTCODE1 (0x00) +#define PN532_STARTCODE2 (0xFF) +#define PN532_POSTAMBLE (0x00) -#define PN532_HOSTTOPN532 (0xD4) -#define PN532_PN532TOHOST (0xD5) +#define PN532_HOSTTOPN532 (0xD4) +#define PN532_PN532TOHOST (0xD5) -#define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK +#define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK #define PN532_INVALID_ACK (-1) #define PN532_TIMEOUT (-2) @@ -21,8 +21,8 @@ #define PN532_NO_SPACE (-4) #define REVERSE_BITS_ORDER(b) b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; \ - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \ - b = (b & 0xAA) >> 1 | (b & 0x55) << 1 + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \ + b = (b & 0xAA) >> 1 | (b & 0x55) << 1 class PN532Interface { @@ -32,12 +32,14 @@ /** * @brief write a command and check ack - * @param buf command to write, not contain prefix and suffix - * @param len lenght of command + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body * @return 0 success * not 0 failed */ - virtual int8_t writeCommand(const uint8_t buf[], uint8_t len) = 0; + virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) = 0; /** * @brief read the response of a command, strip prefix and suffix
--- a/PN532_SPI.cpp Thu Oct 17 06:51:32 2013 +0000 +++ b/PN532_SPI.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -1,6 +1,6 @@ #include "PN532_SPI.h" -#include "debug.h" +#include "PN532_debug.h" #define STATUS_READ 2 #define DATA_WRITE 1 @@ -38,10 +38,10 @@ _ss = 1; } -int8_t PN532_SPI::writeCommand(const uint8_t buf[], uint8_t len) +int8_t PN532_SPI::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) { - command = buf[0]; - writeFrame(buf, len); + command = header[0]; + writeFrame(header, hlen, body, blen); uint8_t timeout = PN532_ACK_WAIT_TIME; while (!isReady()) { @@ -148,7 +148,7 @@ return status; } -void PN532_SPI::writeFrame(const uint8_t buf[], uint8_t len) +void PN532_SPI::writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) { _ss = 0; wait_ms(2); // wake up PN532 @@ -158,7 +158,7 @@ write(PN532_STARTCODE1); write(PN532_STARTCODE2); - uint8_t length = len + 1; // length of data field: TFI + DATA + uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA write(length); write(~length + 1); // checksum of length @@ -167,11 +167,17 @@ DMSG("write: "); - for (uint8_t i = 0; i < len; i++) { - write(buf[i]); - sum += buf[i]; + for (uint8_t i = 0; i < hlen; i++) { + write(header[i]); + sum += header[i]; - DMSG_HEX(buf[i]); + DMSG_HEX(header[i]); + } + for (uint8_t i = 0; i < blen; i++) { + write(body[i]); + sum += body[i]; + + DMSG_HEX(header[i]); } uint8_t checksum = ~sum + 1; // checksum of TFI + DATA
--- a/PN532_SPI.h Thu Oct 17 06:51:32 2013 +0000 +++ b/PN532_SPI.h Thu Nov 21 04:30:49 2013 +0000 @@ -13,7 +13,7 @@ virtual void begin(); virtual void wakeup(); - virtual int8_t writeCommand(const uint8_t buf[], uint8_t len); + virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen); virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); private: @@ -22,7 +22,7 @@ uint8_t command; bool isReady(); - void writeFrame(const uint8_t buf[], uint8_t len); + void writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen); int8_t readAckFrame(); inline void write(uint8_t data) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PN532_debug.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,24 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +//#define DEBUG + +#ifdef DEBUG + +#include <stdio.h> + +#define DMSG(args...) printf(args) +#define DMSG_STR(str) printf("%s\n", str) +#define DMSG_INT(num) printf("%d\n", num) +#define DMSG_HEX(num) printf("%2X ", num) + +#else + +#define DMSG(args...) +#define DMSG_STR(str) +#define DMSG_INT(num) +#define DMSG_HEX(num) + +#endif // DEBUG + +#endif // __DEBUG_H__
--- a/debug.h Thu Oct 17 06:51:32 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -#ifndef __DEBUG_H__ -#define __DEBUG_H__ - -//#define DEBUG - -#ifdef DEBUG - -#include <stdio.h> - -#define DMSG(args...) printf(args) -#define DMSG_STR(str) printf("%s\n", str) -#define DMSG_INT(num) printf("%d\n", num) -#define DMSG_HEX(num) printf("%2X ", num) - -#else - -#define DMSG(args...) -#define DMSG_STR(str) -#define DMSG_INT(num) -#define DMSG_HEX(num) - -#endif // DEBUG - -#endif // __DEBUG_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emulatetag.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,242 @@ +/**************************************************************************/ +/*! + @file emulatetag.cpp + @author Armin Wieser + @license BSD +*/ +/**************************************************************************/ + +#include "emulatetag.h" +#include "PN532_debug.h" + +#include <string.h> + +#define MAX_TGREAD + + +// Command APDU +#define C_APDU_CLA 0 +#define C_APDU_INS 1 // instruction +#define C_APDU_P1 2 // parameter 1 +#define C_APDU_P2 3 // parameter 2 +#define C_APDU_LC 4 // length command +#define C_APDU_DATA 5 // data + +#define C_APDU_P1_SELECT_BY_ID 0x00 +#define C_APDU_P1_SELECT_BY_NAME 0x04 + +// Response APDU +#define R_APDU_SW1_COMMAND_COMPLETE 0x90 +#define R_APDU_SW2_COMMAND_COMPLETE 0x00 + +#define R_APDU_SW1_NDEF_TAG_NOT_FOUND 0x6a +#define R_APDU_SW2_NDEF_TAG_NOT_FOUND 0x82 + +#define R_APDU_SW1_FUNCTION_NOT_SUPPORTED 0x6A +#define R_APDU_SW2_FUNCTION_NOT_SUPPORTED 0x81 + +#define R_APDU_SW1_MEMORY_FAILURE 0x65 +#define R_APDU_SW2_MEMORY_FAILURE 0x81 + +#define R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x62 +#define R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x82 + +// ISO7816-4 commands +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_UPDATE_BINARY 0xD6 + +typedef enum { NONE, CC, NDEF } tag_file; // CC ... Compatibility Container + +const uint8_t compatibility_container[] = { + 0, 0x0F, + 0x20, + 0, 0x54, + 0, 0xFF, + 0x04, + 0x06, + 0xE1, 0x04, + 0xFF, 0xFE, + 0x00, + 0x00 +}; + +bool EmulateTag::init(){ + pn532.begin(); + return pn532.SAMConfig(); +} + +void EmulateTag::setNdefFile(const uint8_t* ndef, const int16_t ndefLength){ + if(ndefLength > (NDEF_MAX_LENGTH -2)){ + DMSG("ndef file too large (> NDEF_MAX_LENGHT -2) - aborting"); + return; + } + + ndef_file[0] = ndefLength >> 8; + ndef_file[1] = ndefLength & 0xFF; + memcpy(ndef_file+2, ndef, ndefLength); +} + +void EmulateTag::setUid(uint8_t* uid){ + uidPtr = uid; +} + +bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout){ + + uint8_t command[] = { + PN532_COMMAND_TGINITASTARGET, + 5, // MODE: PICC only, Passive only + + 0x04, 0x00, // SENS_RES + 0x00, 0x00, 0x00, // NFCID1 + 0x20, // SEL_RES + + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, // FeliCaParams + 0,0, + + 0,0,0,0,0,0,0,0,0,0, // NFCID3t + + 0, // length of general bytes + 0 // length of historical bytes + }; + + if(uidPtr != 0){ // if uid is set copy 3 bytes to nfcid1 + memcpy(command + 4, uidPtr, 3); + } + + if(1 != pn532.tgInitAsTarget(command,sizeof(command), tgInitAsTargetTimeout)){ + DMSG("tgInitAsTarget failed or timed out!"); + return false; + } + + tagWrittenByInitiator = false; + + uint8_t rwbuf[128]; + uint8_t sendlen; + int16_t status; + tag_file currentFile = NONE; + uint16_t cc_size = sizeof(compatibility_container); + bool runLoop = true; + + while(runLoop){ + status = pn532.tgGetData(rwbuf, sizeof(rwbuf)); + if(status < 0){ + DMSG("tgGetData failed!\n"); + pn532.inRelease(); + return true; + } + + uint8_t p1 = rwbuf[C_APDU_P1]; + uint8_t p2 = rwbuf[C_APDU_P2]; + uint8_t lc = rwbuf[C_APDU_LC]; + uint16_t p1p2_length = ((int16_t) p1 << 8) + p2; + + switch(rwbuf[C_APDU_INS]){ + case ISO7816_SELECT_FILE: + switch(p1){ + case C_APDU_P1_SELECT_BY_ID: + if(p2 != 0x0c){ + DMSG("C_APDU_P2 != 0x0c\n"); + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + } else if(lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA+1] == 0x03 || rwbuf[C_APDU_DATA+1] == 0x04)){ + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + if(rwbuf[C_APDU_DATA+1] == 0x03){ + currentFile = CC; + } else if(rwbuf[C_APDU_DATA+1] == 0x04){ + currentFile = NDEF; + } + } else { + setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); + } + break; + case C_APDU_P1_SELECT_BY_NAME: + const uint8_t ndef_tag_application_name_v2[] = {0, 0x7, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 }; + if(0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))){ + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + } else{ + DMSG("function not supported\n"); + setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); + } + break; + } + break; + case ISO7816_READ_BINARY: + switch(currentFile){ + case NONE: + setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); + break; + case CC: + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); + }else { + memcpy(rwbuf,compatibility_container + p1p2_length, lc); + setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); + } + break; + case NDEF: + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); + }else { + memcpy(rwbuf, ndef_file + p1p2_length, lc); + setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); + } + break; + } + break; + case ISO7816_UPDATE_BINARY: + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(MEMORY_FAILURE, rwbuf, &sendlen); + } + else{ + memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc); + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + tagWrittenByInitiator = true; + } + break; + default: + DMSG("Command not supported!"); + DMSG_HEX(rwbuf[C_APDU_INS]); + DMSG("\n"); + setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); + } + status = pn532.tgSetData(rwbuf, sendlen); + if(status < 0){ + DMSG("tgSetData failed\n!"); + pn532.inRelease(); + return true; + } + } + pn532.inRelease(); + return true; +} + +void EmulateTag::setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset){ + switch(cmd){ + case COMMAND_COMPLETE: + buf[0] = R_APDU_SW1_COMMAND_COMPLETE; + buf[1] = R_APDU_SW2_COMMAND_COMPLETE; + *sendlen = 2 + sendlenOffset; + break; + case TAG_NOT_FOUND: + buf[0] = R_APDU_SW1_NDEF_TAG_NOT_FOUND; + buf[1] = R_APDU_SW2_NDEF_TAG_NOT_FOUND; + *sendlen = 2; + break; + case FUNCTION_NOT_SUPPORTED: + buf[0] = R_APDU_SW1_FUNCTION_NOT_SUPPORTED; + buf[1] = R_APDU_SW2_FUNCTION_NOT_SUPPORTED; + *sendlen = 2; + break; + case MEMORY_FAILURE: + buf[0] = R_APDU_SW1_MEMORY_FAILURE; + buf[1] = R_APDU_SW2_MEMORY_FAILURE; + *sendlen = 2; + break; + case END_OF_FILE_BEFORE_REACHED_LE_BYTES: + buf[0] = R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES; + buf[1] = R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES; + *sendlen= 2; + break; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emulatetag.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,53 @@ +/**************************************************************************/ +/*! + @file emulatetag.h + @author Armin Wieser + @license BSD + + Implemented using NFC forum documents & library of libnfc +*/ +/**************************************************************************/ + + +#ifndef __EMULATETAG_H__ +#define __EMULATETAG_H__ + +#include "PN532.h" + +#define NDEF_MAX_LENGTH 128 // altough ndef can handle up to 0xfffe in size, arduino cannot. +typedef enum {COMMAND_COMPLETE, TAG_NOT_FOUND, FUNCTION_NOT_SUPPORTED, MEMORY_FAILURE, END_OF_FILE_BEFORE_REACHED_LE_BYTES} responseCommand; + +class EmulateTag{ + +public: +EmulateTag(PN532Interface &interface) : pn532(interface), uidPtr(0), tagWrittenByInitiator(false) { } + + bool init(); + + bool emulate(const uint16_t tgInitAsTargetTimeout = 0); + + /* + * @param uid pointer to byte array of length 3 (uid is 4 bytes - first byte is fixed) or zero for uid + */ + void setUid(uint8_t* uid = 0); + + void setNdefFile(const uint8_t* ndef, const int16_t ndefLength); + + void getContent(uint8_t** buf, uint16_t* length){ + *buf = ndef_file + 2; // first 2 bytes = length + *length = (ndef_file[1] << 8) + ndef_file[0]; + } + + bool writeOccured(){ + return tagWrittenByInitiator; + } + +private: + PN532 pn532; + uint8_t ndef_file[NDEF_MAX_LENGTH]; + uint8_t* uidPtr; + void setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset =0); + bool tagWrittenByInitiator; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/llcp.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,307 @@ + +#include "llcp.h" +#include "PN532_debug.h" + +// LLCP PDU Type Values +#define PDU_SYMM 0x00 +#define PDU_PAX 0x01 +#define PDU_CONNECT 0x04 +#define PDU_DISC 0x05 +#define PDU_CC 0x06 +#define PDU_DM 0x07 +#define PDU_I 0x0c +#define PDU_RR 0x0d + +uint8_t LLCP::SYMM_PDU[2] = {0, 0}; + +inline uint8_t getPType(const uint8_t *buf) +{ + return ((buf[0] & 0x3) << 2) + (buf[1] >> 6); +} + +inline uint8_t getSSAP(const uint8_t *buf) +{ + return buf[1] & 0x3f; +} + +inline uint8_t getDSAP(const uint8_t *buf) +{ + return buf[0] >> 2; +} + +int8_t LLCP::activate(uint16_t timeout) +{ + return link.activateAsTarget(timeout); +} + +int8_t LLCP::waitForConnection(uint16_t timeout) +{ + uint8_t type; + + ns = 0; + nr = 0; + + // Get CONNECT PDU + DMSG("wait for a CONNECT PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CONNECT == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + // Put CC PDU + DMSG("put a CC(Connection Complete) PDU to response the CONNECT PDU\n"); + ssap = getDSAP(headerBuf); + dsap = getSSAP(headerBuf); + headerBuf[0] = (dsap << 2) + ((PDU_CC >> 2) & 0x3); + headerBuf[1] = ((PDU_CC & 0x3) << 6) + ssap; + if (!link.write(headerBuf, 2)) { + return -2; + } + + return 1; +} + +int8_t LLCP::waitForDisconnection(uint16_t timeout) +{ + uint8_t type; + + // Get DISC PDU + DMSG("wait for a DISC PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_DISC == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + // Put DM PDU + DMSG("put a DM(Disconnect Mode) PDU to response the DISC PDU\n"); + // ssap = getDSAP(headerBuf); + // dsap = getSSAP(headerBuf); + headerBuf[0] = (dsap << 2) + (PDU_DM >> 2); + headerBuf[1] = ((PDU_DM & 0x3) << 6) + ssap; + if (!link.write(headerBuf, 2)) { + return -2; + } + + return 1; +} + +int8_t LLCP::connect(uint16_t timeout) +{ + uint8_t type; + + ns = 0; + nr = 0; + + // try to get a SYMM PDU + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + type = getPType(headerBuf); + if (PDU_SYMM != type) { + return -1; + } + + dsap = LLCP_DEFAULT_DSAP; + ssap = LLCP_DEFAULT_SSAP; + + // put a CONNECT PDU + headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_CONNECT >> 2); + headerBuf[1] = ((PDU_CONNECT & 0x03) << 6) + LLCP_DEFAULT_SSAP; + if (!link.write(headerBuf, 2)) { + return -2; + } + + // wait for a CC PDU + DMSG("wait for a CC PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CC == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + + return 1; +} + +int8_t LLCP::disconnect(uint16_t timeout) +{ + uint8_t type; + + // try to get a SYMM PDU + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + type = getPType(headerBuf); + if (PDU_SYMM != type) { + return -1; + } + + // put a DISC PDU + headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_DISC >> 2); + headerBuf[1] = ((PDU_DISC & 0x03) << 6) + LLCP_DEFAULT_SSAP; + if (!link.write(headerBuf, 2)) { + return -2; + } + + // wait for a DM PDU + DMSG("wait for a DM PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CC == type) { + break; + } else if (PDU_DM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + return 1; +} + +bool LLCP::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + uint8_t type; + uint8_t buf[3]; + + // Get a SYMM PDU + if (2 != link.read(buf, sizeof(buf))) { + return false; + } + + if (headerBufLen < (hlen + 3)) { + return false; + } + + for (int8_t i = hlen - 1; i >= 0; i--) { + headerBuf[i + 3] = header[i]; + } + + headerBuf[0] = (dsap << 2) + (PDU_I >> 2); + headerBuf[1] = ((PDU_I & 0x3) << 6) + ssap; + headerBuf[2] = (ns << 4) + nr; + if (!link.write(headerBuf, 3 + hlen, body, blen)) { + return false; + } + + ns++; + + // Get a RR PDU + int16_t status; + do { + status = link.read(headerBuf, headerBufLen); + if (2 > status) { + return false; + } + + type = getPType(headerBuf); + if (PDU_RR == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return false; + } + } else { + return false; + } + } while (1); + + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return false; + } + + return true; +} + +int16_t LLCP::read(uint8_t *buf, uint8_t length) +{ + uint8_t type; + uint16_t status; + + // Get INFO PDU + do { + status = link.read(buf, length); + if (2 > status) { + return -1; + } + + type = getPType(buf); + if (PDU_I == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + uint8_t len = status - 3; + ssap = getDSAP(buf); + dsap = getSSAP(buf); + + headerBuf[0] = (dsap << 2) + (PDU_RR >> 2); + headerBuf[1] = ((PDU_RR & 0x3) << 6) + ssap; + headerBuf[2] = (buf[2] >> 4) + 1; + if (!link.write(headerBuf, 3)) { + return -2; + } + + for (uint8_t i = 0; i < len; i++) { + buf[i] = buf[i + 3]; + } + + nr++; + + return len; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/llcp.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,74 @@ + +#ifndef __LLCP_H__ +#define __LLCP_H__ + +#include "mac_link.h" + +#define LLCP_DEFAULT_TIMEOUT 20000 +#define LLCP_DEFAULT_DSAP 0x04 +#define LLCP_DEFAULT_SSAP 0x20 + +class LLCP { +public: + LLCP(PN532Interface &interface) : link(interface) { + headerBuf = link.getHeaderBuffer(&headerBufLen); + ns = 0; + nr = 0; + }; + + /** + * @brief Actiave PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t activate(uint16_t timeout = 0); + + int8_t waitForConnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t waitForDisconnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t connect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t disconnect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + /** + * @brief write a packet, the packet should be less than (255 - 2) bytes + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body + * @return true success + * false failed + */ + bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + /** + * @brief read a packet, the packet will be less than (255 - 2) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @return >=0 length of the packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len); + + uint8_t *getHeaderBuffer(uint8_t *len) { + uint8_t *buf = link.getHeaderBuffer(len); + len -= 3; // I PDU header has 3 bytes + return buf; + }; + +private: + MACLink link; + uint8_t ssap; + uint8_t dsap; + uint8_t *headerBuf; + uint8_t headerBufLen; + uint8_t ns; // Number of I PDU Sent + uint8_t nr; // Number of I PDU Received + + static uint8_t SYMM_PDU[2]; +}; + +#endif // __LLCP_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mac_link.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,20 @@ + +#include "mac_link.h" +#include "PN532_debug.h" + +int8_t MACLink::activateAsTarget(uint16_t timeout) +{ + pn532.begin(); + pn532.SAMConfig(); + return pn532.tgInitAsTarget(timeout); +} + +bool MACLink::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + return pn532.tgSetData(header, hlen, body, blen); +} + +int16_t MACLink::read(uint8_t *buf, uint8_t len) +{ + return pn532.tgGetData(buf, len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mac_link.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,51 @@ + + +#ifndef __MAC_LINK_H__ +#define __MAC_LINK_H__ + +#include "PN532.h" + +class MACLink { +public: + MACLink(PN532Interface &interface) : pn532(interface) { + + }; + + /** + * @brief Activate PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t activateAsTarget(uint16_t timeout = 0); + + /** + * @brief write a PDU packet, the packet should be less than (255 - 2) bytes + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body + * @return true success + * false failed + */ + bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + /** + * @brief read a PDU packet, the packet will be less than (255 - 2) bytes + * @param buf the buffer to contain the PDU packet + * @param len lenght of the buffer + * @return >=0 length of the PDU packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len); + + uint8_t *getHeaderBuffer(uint8_t *len) { + return pn532.getBuffer(len); + }; + +private: + PN532 pn532; +}; + +#endif // __MAC_LINK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/snep.cpp Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,106 @@ + +#include "snep.h" +#include "PN532_debug.h" + +int8_t SNEP::write(const uint8_t *buf, uint8_t len, uint16_t timeout) +{ + if (0 >= llcp.activate(timeout)) { + DMSG("failed to activate PN532 as a target\n"); + return -1; + } + + if (0 >= llcp.connect(timeout)) { + DMSG("failed to set up a connection\n"); + return -2; + } + + // response a success SNEP message + headerBuf[0] = SNEP_DEFAULT_VERSION; + headerBuf[1] = SNEP_REQUEST_PUT; + headerBuf[2] = 0; + headerBuf[3] = 0; + headerBuf[4] = 0; + headerBuf[5] = len; + if (0 >= llcp.write(headerBuf, 6, buf, len)) { + return -3; + } + + uint8_t rbuf[16]; + if (6 > llcp.read(rbuf, sizeof(rbuf))) { + return -4; + } + + // check SNEP version + if (SNEP_DEFAULT_VERSION != rbuf[0]) { + DMSG("The received SNEP message's major version is different\n"); + // To-do: send Unsupported Version response + return -4; + } + + // expect a put request + if (SNEP_RESPONSE_SUCCESS != rbuf[1]) { + DMSG("Expect a success response\n"); + return -4; + } + + llcp.disconnect(timeout); + + return 1; +} + +int16_t SNEP::read(uint8_t *buf, uint8_t len, uint16_t timeout) +{ + if (0 >= llcp.activate(timeout)) { + DMSG("failed to activate PN532 as a target\n"); + return -1; + } + + if (0 >= llcp.waitForConnection(timeout)) { + DMSG("failed to set up a connection\n"); + return -2; + } + + uint16_t status = llcp.read(buf, len); + if (6 > status) { + return -3; + } + + + // check SNEP version + if (SNEP_DEFAULT_VERSION != buf[0]) { + DMSG("The received SNEP message's major version is different\n"); + // To-do: send Unsupported Version response + return -4; + } + + // expect a put request + if (SNEP_REQUEST_PUT != buf[1]) { + DMSG("Expect a put request\n"); + return -4; + } + + // check message's length + uint32_t length = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5]; + // length should not be more than 244 (header + body < 255, header = 6 + 3 + 2) + if (length > (status - 6)) { + DMSG("The SNEP message is too large: "); + DMSG_INT(length); + DMSG_INT(status - 6); + DMSG("\n"); + return -4; + } + for (uint8_t i = 0; i < length; i++) { + buf[i] = buf[i + 6]; + } + + // response a success SNEP message + headerBuf[0] = SNEP_DEFAULT_VERSION; + headerBuf[1] = SNEP_RESPONSE_SUCCESS; + headerBuf[2] = 0; + headerBuf[3] = 0; + headerBuf[4] = 0; + headerBuf[5] = 0; + llcp.write(headerBuf, 6); + + return length; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/snep.h Thu Nov 21 04:30:49 2013 +0000 @@ -0,0 +1,49 @@ + + +#ifndef __SNEP_H__ +#define __SNEP_H__ + +#include "llcp.h" + +#define SNEP_DEFAULT_VERSION 0x10 // Major: 1, Minor: 0 + +#define SNEP_REQUEST_PUT 0x02 +#define SNEP_REQUEST_GET 0x01 + +#define SNEP_RESPONSE_SUCCESS 0x81 +#define SNEP_RESPONSE_REJECT 0xFF + +class SNEP { +public: + SNEP(PN532Interface &interface) : llcp(interface) { + headerBuf = llcp.getHeaderBuffer(&headerBufLen); + }; + + /** + * @brief write a SNEP packet, the packet should be less than (255 - 2 - 3) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @param timeout max time to wait, 0 means no timeout + * @return >0 success + * =0 timeout + * <0 failed + */ + int8_t write(const uint8_t *buf, uint8_t len, uint16_t timeout = 0); + + /** + * @brief read a SNEP packet, the packet will be less than (255 - 2 - 3) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @param timeout max time to wait, 0 means no timeout + * @return >=0 length of the packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len, uint16_t timeout = 0); + +private: + LLCP llcp; + uint8_t *headerBuf; + uint8_t headerBufLen; +}; + +#endif // __SNEP_H__