123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- #include <MifareUltralight.h>
- MifareUltralight::MifareUltralight(MFRC522 *nfcShield)
- {
- nfc = nfcShield;
- }
- MifareUltralight::~MifareUltralight()
- {
- }
- NfcTag MifareUltralight::read()
- {
- if (isUnformatted())
- {
- #ifdef NDEF_USE_SERIAL
- Serial.println(F("WARNING: Tag is not formatted."));
- #endif
- return NfcTag(nfc->uid.uidByte, nfc->uid.size, NfcTag::TYPE_2);
- }
- uint16_t messageLength = 0;
- uint16_t ndefStartIndex = 0;
- findNdefMessage(&messageLength, &ndefStartIndex);
- uint16_t bufferSize = calculateBufferSize(messageLength, ndefStartIndex);
- if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE
- NdefMessage message = NdefMessage();
- message.addEmptyRecord();
- return NfcTag(nfc->uid.uidByte, nfc->uid.size, NfcTag::TYPE_2, message);
- }
- uint8_t index = 0;
- byte buffer[bufferSize];
- for (uint8_t page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page+=(ULTRALIGHT_READ_SIZE/ULTRALIGHT_PAGE_SIZE))
- {
- // read the data
- byte dataSize = ULTRALIGHT_READ_SIZE + 2;
- MFRC522::StatusCode status = nfc->MIFARE_Read(page, &buffer[index], &dataSize);
- if (status == MFRC522::STATUS_OK)
- {
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("Page "));Serial.print(page);Serial.print(" ");
- PrintHexChar(&buffer[index], ULTRALIGHT_PAGE_SIZE);
- PrintHexChar(&buffer[index+ULTRALIGHT_PAGE_SIZE], ULTRALIGHT_PAGE_SIZE);
- PrintHexChar(&buffer[index+2*ULTRALIGHT_PAGE_SIZE], ULTRALIGHT_PAGE_SIZE);
- PrintHexChar(&buffer[index+3*ULTRALIGHT_PAGE_SIZE], ULTRALIGHT_PAGE_SIZE);
- #endif
- }
- else
- {
- #ifdef NDEF_USE_SERIAL
- Serial.print(F("Read failed "));Serial.println(page);
- #endif
- return NfcTag(nfc->uid.uidByte, nfc->uid.size, NfcTag::TYPE_2);
- }
- if (index >= (messageLength + ndefStartIndex))
- {
- break;
- }
- index += ULTRALIGHT_READ_SIZE;
- }
- return NfcTag(nfc->uid.uidByte, nfc->uid.size, NfcTag::TYPE_2, &buffer[ndefStartIndex], messageLength);
- }
- boolean MifareUltralight::isUnformatted()
- {
- uint8_t page = 4;
- byte dataSize = ULTRALIGHT_READ_SIZE+2;
- byte data[dataSize];
- MFRC522::StatusCode status = nfc->MIFARE_Read(page, data, &dataSize);
- if (status == MFRC522::STATUS_OK && dataSize >= 4)
- {
- return (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF);
- }
- else
- {
- #ifdef NDEF_USE_SERIAL
- Serial.print(F("Error. Failed read page "));Serial.println(page);
- #endif
- return false;
- }
- }
- // page 3 has tag capabilities
- uint16_t MifareUltralight::readTagSize()
- {
- uint16_t tagCapacity = 0;
- byte dataSize = ULTRALIGHT_READ_SIZE+2;
- byte data[dataSize];
- MFRC522::StatusCode status = nfc->MIFARE_Read(3, data, &dataSize);
- if (status == MFRC522::STATUS_OK && dataSize >= 2)
- {
- // See AN1303 - different rules for Mifare Family byte2 = (additional data + 48)/8
- tagCapacity = data[2] * 8;
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("Tag capacity "));Serial.print(tagCapacity);Serial.println(F(" bytes"));
- #endif
- // TODO future versions should get lock information
- }
- return tagCapacity;
- }
- // read enough of the message to find the ndef message length
- void MifareUltralight::findNdefMessage(uint16_t *messageLength, uint16_t *ndefStartIndex)
- {
- byte dataSize = ULTRALIGHT_READ_SIZE + 2;
- byte data[dataSize]; // 3 pages, but 4 + CRC are returned
- if(nfc->MIFARE_Read(4, data, &dataSize) == MFRC522::STATUS_OK)
- {
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.println(F("Pages 4-7"));
- PrintHexChar(data, 18);
- PrintHexChar(data+ULTRALIGHT_PAGE_SIZE, 18);
- PrintHexChar(data+2*ULTRALIGHT_PAGE_SIZE, 18);
- PrintHexChar(data+3*ULTRALIGHT_PAGE_SIZE, 18);
- #endif
- if (data[0] == 0x03)
- {
- *messageLength = data[1];
- *ndefStartIndex = 2;
- }
- else if (data[5] == 0x3) // page 5 byte 1
- {
- // TODO should really read the lock control TLV to ensure byte[5] is correct
- *messageLength = data[6];
- *ndefStartIndex = 7;
- }
- }
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("messageLength "));Serial.println(*messageLength);
- Serial.print(F("ndefStartIndex "));Serial.println(*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
- uint16_t MifareUltralight::calculateBufferSize(uint16_t messageLength, uint16_t ndefStartIndex)
- {
- // TLV terminator 0xFE is 1 byte
- uint16_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;
- }
- //MFRC522 also return CRC
- bufferSize += 2;
- return bufferSize;
- }
- boolean MifareUltralight::write(NdefMessage& m)
- {
- if (isUnformatted())
- {
- #ifdef NDEF_USE_SERIAL
- Serial.println(F("WARNING: Tag is not formatted."));
- #endif
- return false;
- }
- uint16_t tagCapacity = readTagSize(); // meta info for tag
- uint16_t messageLength = m.getEncodedSize();
- uint16_t ndefStartIndex = messageLength < 0xFF ? 2 : 4;
- uint16_t bufferSize = calculateBufferSize(messageLength, ndefStartIndex);
- if(bufferSize>tagCapacity) {
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("Encoded Message length exceeded tag Capacity "));Serial.println(tagCapacity);
- #endif
- return false;
- }
- uint8_t encoded[bufferSize];
- uint8_t * src = encoded;
- unsigned int position = 0;
- uint8_t page = ULTRALIGHT_DATA_START_PAGE;
- // Set message size. With ultralight should always be less than 0xFF but who knows?
- encoded[0] = 0x3;
- if (messageLength < 0xFF)
- {
- encoded[1] = messageLength;
- }
- else
- {
- encoded[1] = 0xFF;
- encoded[2] = ((messageLength >> 8) & 0xFF);
- encoded[3] = (messageLength & 0xFF);
- }
- m.encode(encoded+ndefStartIndex);
- // this is always at least 1 byte copy because of terminator.
- memset(encoded+ndefStartIndex+messageLength,0,bufferSize-ndefStartIndex-messageLength);
- encoded[ndefStartIndex+messageLength] = 0xFE; // terminator
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("messageLength "));Serial.println(messageLength);
- Serial.print(F("Tag Capacity "));Serial.println(tagCapacity);
- PrintHex(encoded,bufferSize);
- #endif
- while (position < bufferSize){ //bufferSize is always times pagesize so no "last chunk" check
- // Although we have to provide 16 bytes to MIFARE_Write only 4 of them are written onto the tag
- byte writeBuffer[16] = {0};
- memcpy (writeBuffer, src, 4);
- // write page
- if (nfc->MIFARE_Write(page, writeBuffer, 16) != MFRC522::STATUS_OK)
- return false;
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("Wrote page "));Serial.print(page);Serial.print(F(" - "));
- PrintHex(src,ULTRALIGHT_PAGE_SIZE);
- #endif
- page++;
- src+=ULTRALIGHT_PAGE_SIZE;
- position+=ULTRALIGHT_PAGE_SIZE;
- }
- return true;
- }
- // Mifare Ultralight can't be reset to factory state
- // zero out tag data like the NXP Tag Write Android application
- boolean MifareUltralight::clean()
- {
- uint16_t tagCapacity = readTagSize();
- uint8_t pages = (tagCapacity / ULTRALIGHT_PAGE_SIZE) + ULTRALIGHT_DATA_START_PAGE;
- // factory tags have 0xFF, but OTP-CC blocks have already been set so we use 0x00
- byte data[16] = { 0 };
- for (int i = ULTRALIGHT_DATA_START_PAGE; i < pages; i++)
- {
- #ifdef MIFARE_ULTRALIGHT_DEBUG
- Serial.print(F("Wrote page "));Serial.print(i);Serial.print(F(" - "));
- PrintHex(data, ULTRALIGHT_PAGE_SIZE);
- #endif
- if (nfc->MIFARE_Write(i, data, 16) != MFRC522::STATUS_OK)
- {
- return false;
- }
- }
- return true;
- }
|