From 45b7e60584cc592480e94ea2cfc14868f3359331 Mon Sep 17 00:00:00 2001 From: jpg-consulting Date: Sat, 7 Jan 2017 11:29:46 +0100 Subject: [PATCH] Added RATS (Request for Answer To Select) #271 Added ATQA information for card identification Added MIFARE DESFire type Improved RATS Added the "Dumping memory contents not implemented for that PICC type." for ISO-14443 cards updated keyword.txt Added PPS (Protocol and Parameter Selection Request) Change baud rates in PPS and hardcoded CID Improved ATS response. Made ATS response more user-friendly Enable CRC for T=CL after succesfull PPS. Added TCL_Transceive and TCL_Deselect methods. Response to TCL_Transceive takes care if R-Block is received Added private methos to help parse PCB Block response Using a structure to transceive PCB blocks. Renamed some methods and structures. Change some method and structure names and fixed a bug in PPS. Added sanity check to PPS command Added method to send R-Block (ACK/NAK). Also takes care if chaining is present in I-Block response. PPS works at 106 and 212 Kbps (Higher speeds not working) --- MFRC522.cpp | 906 ++++++++++++++++++++++++++++++++++++++++++++++++++- MFRC522.h | 85 ++++- keywords.txt | 17 + 3 files changed, 1000 insertions(+), 8 deletions(-) diff --git a/MFRC522.cpp b/MFRC522.cpp index 695f95f..cfaab12 100644 --- a/MFRC522.cpp +++ b/MFRC522.cpp @@ -214,6 +214,12 @@ void MFRC522::PCD_Init() { PCD_Reset(); } + // Reset baud rates + PCD_WriteRegister(TxModeReg, 0x00); + PCD_WriteRegister(RxModeReg, 0x00); + // Reset ModWidthReg + PCD_WriteRegister(ModWidthReg, 0x26); + // When communicating with a PICC we need a timeout if something goes wrong. // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. @@ -785,6 +791,102 @@ MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct // Set correct uid->size uid->size = 3 * cascadeLevel + 1; + // IF SAK bit 6 = 1 then it is ISO/IEC 14443-4 (T=CL) + // A Request ATS command should be sent + // We also check SAK bit 3 is cero, as it stands for UID complete (1 would tell us it is incomplete) + if ((uid->sak & 0x24) == 0x20) { + Ats ats; + result = PICC_RequestATS(&ats); + if (result == STATUS_OK) { + // Check the ATS + if (ats.size > 0) + { + // TA1 has been transmitted? + // PPS must be supported... + if (ats.ta1.transmitted) + { + // TA1 + // 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Description + // ---+---+---+---+---+---+---+---+------------------------------------------ + // 0 | - | - | - | 0 | - | - | - | Different D for each direction supported + // 1 | - | - | - | 0 | - | - | - | Only same D for both direction supported + // - | x | x | x | 0 | - | - | - | DS (Send D) + // - | - | - | - | 0 | x | x | x | DR (Receive D) + // + // D to bitrate table + // 3 | 2 | 1 | Value + // ---+---+---+----------------------------- + // 1 | - | - | 848 kBaud is supported + // - | 1 | - | 424 kBaud is supported + // - | - | 1 | 212 kBaud is supported + // 0 | 0 | 0 | Only 106 kBaud is supported + // + // Note: 106 kBaud is always supported + // + // I have almost constant timeouts when changing speeds :( + TagBitRates ds = BITRATE_106KBITS; + TagBitRates dr = BITRATE_106KBITS; + + //// Not working at 848 or 424 + //if (ats.ta1.ds & 0x04) + //{ + // ds = BITRATE_848KBITS; + //} + //else if (ats.ta1.ds & 0x02) + //{ + // ds = BITRATE_424KBITS; + //} + //else if (ats.ta1.ds & 0x01) + //{ + // ds = BITRATE_212KBITS; + //} + //else + //{ + // ds = BITRATE_106KBITS; + //} + + if (ats.ta1.ds & 0x01) + { + ds = BITRATE_212KBITS; + } + else + { + ds = BITRATE_106KBITS; + } + + //// Not working at 848 or 424 + //if (ats.ta1.dr & 0x04) + //{ + // dr = BITRATE_848KBITS; + //} + //else if (ats.ta1.dr & 0x02) + //{ + // dr = BITRATE_424KBITS; + //} + //else if (ats.ta1.dr & 0x01) + //{ + // dr = BITRATE_212KBITS; + //} + //else + //{ + // dr = BITRATE_106KBITS; + //} + + if (ats.ta1.dr & 0x01) + { + dr = BITRATE_212KBITS; + } + else + { + dr = BITRATE_106KBITS; + } + + PICC_PPS(ds, dr); + } + } + } + } + return STATUS_OK; } // End PICC_Select() @@ -821,6 +923,614 @@ MFRC522::StatusCode MFRC522::PICC_HaltA() { return result; } // End PICC_HaltA() +/** + * Transmits a Request command for Answer To Select (ATS). + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522::PICC_RequestATS(Ats *ats) +{ + byte count; + MFRC522::StatusCode result; + + byte bufferATS[FIFO_SIZE]; + byte bufferSize = FIFO_SIZE; + + memset(bufferATS, 0, FIFO_SIZE); + + // Build command buffer + bufferATS[0] = PICC_CMD_RATS; + + // The CID defines the logical number of the addressed card and has a range of 0 + // through 14; 15 is reserved for future use (RFU). + // + // FSDI codes the maximum frame size (FSD) that the terminal can receive. + // + // FSDI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F + // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- + // FSD (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 + // + bufferATS[1] = 0x50; // FSD=64, CID=0 + + // Calculate CRC_A + result = PCD_CalculateCRC(bufferATS, 2, &bufferATS[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(bufferATS, 4, bufferATS, &bufferSize, NULL, 0, true); + if (result != STATUS_OK) { + PICC_HaltA(); + } + + // Set the ats structure data + ats->size = bufferATS[0]; + + // T0 byte: + // + // b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | Meaning + //----+----+----+----+----+----+----+----+--------------------------- + // 0 | ...| ...| ...| ...|... | ...| ...| Set to 0 (RFU) + // 0 | 1 | x | x | ...|... | ...| ...| TC1 transmitted + // 0 | x | 1 | x | ...|... | ...| ...| TB1 transmitted + // 0 | x | x | 1 | ...|... | ...| ...| TA1 transmitted + // 0 | ...| ...| ...| x | x | x | x | Maximum frame size (FSCI) + // + // FSCI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F + // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- + // FSC (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 + // + // Default FSCI is 2 (32 bytes) + if (ats->size > 0x01) + { + // TC1, TB1 and TA1 where NOT transmitted + ats->ta1.transmitted = (bool)(bufferATS[1] & 0x40); + ats->tb1.transmitted = (bool)(bufferATS[1] & 0x20); + ats->tc1.transmitted = (bool)(bufferATS[1] & 0x10); + + // Decode FSCI + switch (bufferATS[1] & 0x0F) + { + case 0x00: + ats->fsc = 16; + break; + case 0x01: + ats->fsc = 24; + break; + case 0x02: + ats->fsc = 32; + break; + case 0x03: + ats->fsc = 40; + break; + case 0x04: + ats->fsc = 48; + break; + case 0x05: + ats->fsc = 64; + break; + case 0x06: + ats->fsc = 96; + break; + case 0x07: + ats->fsc = 128; + break; + case 0x08: + // This value cannot be hold by a byte + // The reason I ignore it is that MFRC255 FIFO is 64 bytes so this is not a possible value (or atleast it shouldn't) + //ats->fsc = 256; + break; + // TODO: What to do with RFU (Reserved for future use)? + } + + // TA1 + if (ats->ta1.transmitted) + { + ats->ta1.sameD = (bool)(bufferATS[2] & 0x80); + ats->ta1.ds = (TagBitRates)((bufferATS[2] & 0x70) >> 4); + ats->ta1.dr = (TagBitRates)(bufferATS[2] & 0x07); + } + else + { + // Default TA1 + ats->ta1.ds = BITRATE_106KBITS; + ats->ta1.dr = BITRATE_106KBITS; + } + + // TB1 + if (ats->tb1.transmitted) + { + uint8_t tb1Index = 2; + + if (ats->ta1.transmitted) + tb1Index++; + + ats->tb1.fwi = (bufferATS[tb1Index] & 0xF0) >> 4; + ats->tb1.sfgi = bufferATS[tb1Index] & 0x0F; + } + else + { + // Defaults for TB1 + ats->tb1.fwi = 0; // TODO: Don't know the default for this! + ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + } + + // TC1 + if (ats->tc1.transmitted) + { + uint8_t tc1Index = 2; + + if (ats->ta1.transmitted) + tc1Index++; + if (ats->tb1.transmitted) + tc1Index++; + + ats->tc1.supportsCID = (bool)(bufferATS[tc1Index] & 0x02); + ats->tc1.supportsNAD = (bool)(bufferATS[tc1Index] & 0x01); + } + else + { + // Defaults for TC1 + ats->tc1.supportsCID = true; + ats->tc1.supportsNAD = false; + } + } + else + { + // TC1, TB1 and TA1 where NOT transmitted + ats->ta1.transmitted = false; + ats->tb1.transmitted = false; + ats->tc1.transmitted = false; + + // Default FSCI + ats->fsc = 32; // Defaults to FSCI 2 (32 bytes) + + // Default TA1 + ats->ta1.sameD = false; + ats->ta1.ds = BITRATE_106KBITS; + ats->ta1.dr = BITRATE_106KBITS; + + // Defaults for TB1 + ats->tb1.transmitted = false; + ats->tb1.fwi = 0; // TODO: Don't know the default for this! + ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + + // Defaults for TC1 + ats->tc1.transmitted = false; + ats->tc1.supportsCID = true; + ats->tc1.supportsNAD = false; + } + + memcpy(ats->data, bufferATS, bufferSize - 2); + + return result; +} // End PICC_RequestATS() + +/** + * Transmits Protocol and Parameter Selection Request (PPS) without parameter 1 + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522::PICC_PPS() +{ + StatusCode result; + + byte ppsBuffer[4]; + byte ppsBufferSize = 4; + // Start byte: The start byte (PPS) consists of two parts: + // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. + // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. + ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS + ppsBuffer[1] = 0x00; // PPS0 indicates whether PPS1 is present + + // Calculate CRC_A + result = PCD_CalculateCRC(ppsBuffer, 2, &ppsBuffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(ppsBuffer, 4, ppsBuffer, &ppsBufferSize, NULL, 0, true); + if (result == STATUS_OK) + { + // Enable CRC for T=CL + byte txReg = PCD_ReadRegister(TxModeReg) | 0x80; + byte rxReg = PCD_ReadRegister(RxModeReg) | 0x80; + + PCD_WriteRegister(TxModeReg, txReg); + PCD_WriteRegister(RxModeReg, rxReg); + } + + return result; +} // End PICC_PPS() + +/** + * Transmits Protocol and Parameter Selection Request (PPS) + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522::PICC_PPS(TagBitRates sendBitRate, ///< DS + TagBitRates receiveBitRate ///< DR +) { + StatusCode result; + + byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; + byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; + + byte ppsBuffer[5]; + byte ppsBufferSize = 5; + // Start byte: The start byte (PPS) consists of two parts: + // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. + // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. + ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS + ppsBuffer[1] = 0x11; // PPS0 indicates whether PPS1 is present + + // Bit 8 - Set to '0' as MFRC522 allows different bit rates for send and receive + // Bit 4 - Set to '0' as it is Reserved for future use. + //ppsBuffer[2] = (((sendBitRate & 0x03) << 4) | (receiveBitRate & 0x03)) & 0xE7; + ppsBuffer[2] = (((sendBitRate & 0x03) << 2) | (receiveBitRate & 0x03)) & 0xE7; + + // Calculate CRC_A + result = PCD_CalculateCRC(ppsBuffer, 3, &ppsBuffer[3]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(ppsBuffer, 5, ppsBuffer, &ppsBufferSize, NULL, 0, true); + if (result == STATUS_OK) + { + // Make sure it is an answer to our PPS + // We should receive our PPS byte and 2 CRC bytes + if ((ppsBufferSize == 3) && (ppsBuffer[0] == 0xD0)) { + byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; + byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; + + // Set bit rate and enable CRC for T=CL + txReg = (txReg & 0x8F) | ((receiveBitRate & 0x03) << 4) | 0x80; + rxReg = (rxReg & 0x8F) | ((sendBitRate & 0x03) << 4) | 0x80; + rxReg &= 0xF0; //Enforce although this should be set already + + // From ConfigIsoType + //rxReg |= 0x06; + + PCD_WriteRegister(TxModeReg, txReg); + PCD_WriteRegister(RxModeReg, rxReg); + + // At 212kBps + switch (sendBitRate) { + case BITRATE_212KBITS: + { + //PCD_WriteRegister(ModWidthReg, 0x13); + PCD_WriteRegister(ModWidthReg, 0x15); + } + break; + case BITRATE_424KBITS: + { + PCD_WriteRegister(ModWidthReg, 0x0A); + } + break; + case BITRATE_848KBITS: + { + PCD_WriteRegister(ModWidthReg, 0x05); + } + break; + default: + { + PCD_WriteRegister(ModWidthReg, 0x26); // Default value + } + break; + } + + //PCD_WriteRegister(RxThresholdReg, 0x84); // ISO-14443.4 Type A (default) + //PCD_WriteRegister(ControlReg, 0x10); + + delayMicroseconds(10); + } + else + { + return STATUS_ERROR; + } + } + + return result; +} // End PICC_PPS() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with ISO/IEC 14433-4 cards +///////////////////////////////////////////////////////////////////////////////////// + +MFRC522::StatusCode MFRC522::TCL_Transceive(PcbBlock *send, PcbBlock *back) +{ + MFRC522::StatusCode result; + byte inBuffer[FIFO_SIZE]; + byte inBufferSize = FIFO_SIZE; + byte outBuffer[send->inf.size + 5]; // PCB + CID + NAD + INF + EPILOGUE (CRC) + byte outBufferOffset = 1; + byte inBufferOffset = 1; + + // Set the PCB byte + outBuffer[0] = send->prologue.pcb; + + // Set the CID byte if available + if (send->prologue.pcb & 0x08) { + outBuffer[outBufferOffset] = send->prologue.cid; + outBufferOffset++; + } + + // Set the NAD byte if available + if (send->prologue.pcb & 0x04) { + outBuffer[outBufferOffset] = send->prologue.nad; + outBufferOffset++; + } + + // Copy the INF field if available + if (send->inf.size > 0) { + memcpy(&outBuffer[outBufferOffset], send->inf.data, send->inf.size); + outBufferOffset += send->inf.size; + } + + // Is the CRC enabled for transmission? + byte txModeReg = PCD_ReadRegister(TxModeReg); + if ((txModeReg & 0x80) != 0x80) { + // Calculate CRC_A + result = PCD_CalculateCRC(outBuffer, outBufferOffset, &outBuffer[outBufferOffset]); + if (result != STATUS_OK) { + return result; + } + + outBufferOffset += 2; + } + + // Transceive the block + result = PCD_TransceiveData(outBuffer, outBufferOffset, inBuffer, &inBufferSize); + if (result != STATUS_OK) { + return result; + } + + // We want to turn the received array back to a PcbBlock + back->prologue.pcb = inBuffer[0]; + + // CID byte is present? + if (send->prologue.pcb & 0x08) { + back->prologue.cid = inBuffer[inBufferOffset]; + inBufferOffset++; + } + + // NAD byte is present? + if (send->prologue.pcb & 0x04) { + back->prologue.nad = inBuffer[inBufferOffset]; + inBufferOffset++; + } + + // Check if CRC is taken care of by MFRC522 + byte rxModeReg = PCD_ReadRegister(TxModeReg); + if ((rxModeReg & 0x80) != 0x80) { + Serial.print("CRC is not taken care of by MFRC522: "); + Serial.println(rxModeReg, HEX); + + // Check the CRC + // We need at least the CRC_A value. + if ((int)(inBufferSize - inBufferOffset) < 2) { + return STATUS_CRC_WRONG; + } + + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + byte controlBuffer[2]; + MFRC522::StatusCode status = PCD_CalculateCRC(inBuffer, inBufferSize - 2, controlBuffer); + if (status != STATUS_OK) { + return status; + } + + if ((inBuffer[inBufferSize - 2] != controlBuffer[0]) || (inBuffer[inBufferSize - 1] != controlBuffer[1])) { + return STATUS_CRC_WRONG; + } + + // Take away the CRC bytes + inBufferSize -= 2; + } + + // Got more data? + if (inBufferSize > inBufferOffset) { + if ((inBufferSize - inBufferOffset) > back->inf.size) { + return STATUS_NO_ROOM; + } + + memcpy(back->inf.data, &inBuffer[inBufferOffset], inBufferSize - inBufferOffset); + back->inf.size = inBufferSize - inBufferOffset; + } else { + back->inf.size = 0; + } + + // If the response is a R-Block check NACK + if (((inBuffer[0] & 0xC0) == 0x80) && (inBuffer[0] & 0x20)) { + return STATUS_MIFARE_NACK; + } + + return result; +} +/** + * Send an I-Block (Application) + */ +MFRC522::StatusCode MFRC522::TCL_Transceive(TagInfo *tag, byte *sendData, byte sendLen, byte *backData, byte *backLen) +{ + MFRC522::StatusCode result; + + PcbBlock out; + PcbBlock in; + byte outBuffer[FIFO_SIZE]; + byte outBufferSize = FIFO_SIZE; + byte totalBackLen = *backLen; + + // This command sends an I-Block + out.prologue.pcb = 0x02; + + if (tag->ats.tc1.supportsCID) { + out.prologue.pcb |= 0x08; + out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 + } + + // This command doe not support NAD + out.prologue.pcb &= 0xFB; + out.prologue.nad = 0x00; + + // Set the block number + if (tag->blockNumber) { + out.prologue.pcb |= 0x01; + } + + // Do we have data to send? + if (sendData && (sendLen > 0)) { + out.inf.size = sendLen; + out.inf.data = sendData; + } else { + out.inf.size = 0; + out.inf.data = NULL; + } + + // Initialize the receiving data + in.inf.data = outBuffer; + in.inf.size = outBufferSize; + + result = TCL_Transceive(&out, &in); + if (result != STATUS_OK) { + return result; + } + + // Swap block number on success + if (tag->blockNumber) + tag->blockNumber = false; + else + tag->blockNumber = true; + + if (backData && (backLen > 0)) { + if (*backLen < in.inf.size) + return STATUS_NO_ROOM; + + *backLen = in.inf.size; + memcpy(backData, in.inf.data, in.inf.size); + } + + // Check chaining + if (in.prologue.pcb & 0x10 == 0x00) + return result; + + // Result is chained + // Send an ACK to receive more data + // TODO: Should be checked I've never needed to send an ACK + while (in.prologue.pcb & 0x10) { + byte ackData[FIFO_SIZE]; + byte ackDataSize = FIFO_SIZE; + + result = TCL_TransceiveRBlock(tag, true, ackData, &ackDataSize); + if (result != STATUS_OK) + return result; + + if (backData && (backLen > 0)) { + if ((*backLen + ackDataSize) > totalBackLen) + return STATUS_NO_ROOM; + + memcpy(&(backData[*backLen]), ackData, ackDataSize); + *backLen += ackDataSize; + } + } + + return result; +} // End TCL_Transceive() + +/** + * Send R-Block to the PICC. + */ +MFRC522::StatusCode MFRC522::TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData, byte *backLen) +{ + MFRC522::StatusCode result; + + PcbBlock out; + PcbBlock in; + byte outBuffer[FIFO_SIZE]; + byte outBufferSize = FIFO_SIZE; + + // This command sends an R-Block + if (ack) + out.prologue.pcb = 0xA2; // ACK + else + out.prologue.pcb = 0xB2; // NAK + + + if (tag->ats.tc1.supportsCID) { + out.prologue.pcb |= 0x08; + out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 + } + + // This command doe not support NAD + out.prologue.pcb &= 0xFB; + out.prologue.nad = 0x00; + + // Set the block number + if (tag->blockNumber) { + out.prologue.pcb |= 0x01; + } + + // No INF data for R-Block + out.inf.size = 0; + out.inf.data = NULL; + + // Initialize the receiving data + in.inf.data = outBuffer; + in.inf.size = outBufferSize; + + result = TCL_Transceive(&out, &in); + if (result != STATUS_OK) { + return result; + } + + // Swap block number on success + if (tag->blockNumber) + tag->blockNumber = false; + else + tag->blockNumber = true; + + if (backData && backLen) { + if (*backLen < in.inf.size) + return STATUS_NO_ROOM; + + *backLen = in.inf.size; + memcpy(backData, in.inf.data, in.inf.size); + } + + return result; +} // End TCL_TransceiveRBlock() + +/** + * Send an S-Block to deselect the card. + */ +MFRC522::StatusCode MFRC522::TCL_Deselect(TagInfo *tag) +{ + MFRC522::StatusCode result; + byte outBuffer[4]; + byte outBufferSize = 1; + byte inBuffer[FIFO_SIZE]; + byte inBufferSize = FIFO_SIZE; + + outBuffer[0] = 0xC2; + if (tag->ats.tc1.supportsCID) + { + outBuffer[0] |= 0x08; + outBuffer[1] = 0x00; // CID is hardcoded + outBufferSize = 2; + } + + result = PCD_TransceiveData(outBuffer, outBufferSize, inBuffer, &inBufferSize); + if (result != STATUS_OK) { + return result; + } + + // TODO:Maybe do some checks? In my test it returns: CA 00 (Same data as I sent to my card) + + return result; +} // End TCL_Deselect() ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with MIFARE PICCs @@ -1253,6 +1963,36 @@ const __FlashStringHelper *MFRC522::GetStatusCodeName(MFRC522::StatusCode code / } } // End GetStatusCodeName() +/** + * Get the PICC type. + * + * @return PICC_Type + */ +MFRC522::PICC_Type MFRC522::PICC_GetType(TagInfo *tag ///< The TagInfo returned from PICC_Select(). +) { + // http://www.nxp.com/documents/application_note/AN10833.pdf + // 3.2 Coding of Select Acknowledge (SAK) + // ignore 8-bit (iso14443 starts with LSBit = bit 1) + // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) + byte sak = tag->uid.sak & 0x7F; + switch (sak) { + case 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete + case 0x09: return PICC_TYPE_MIFARE_MINI; + case 0x08: return PICC_TYPE_MIFARE_1K; + case 0x18: return PICC_TYPE_MIFARE_4K; + case 0x00: return PICC_TYPE_MIFARE_UL; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; + case 0x01: return PICC_TYPE_TNP3XXX; + case 0x20: + if (tag->atqa == 0x0344) + return PICC_TYPE_MIFARE_DESFIRE; + return PICC_TYPE_ISO_14443_4; + case 0x40: return PICC_TYPE_ISO_18092; + default: return PICC_TYPE_UNKNOWN; + } +} // End PICC_GetType() + /** * Translates the SAK (Select Acknowledge) to a PICC type. * @@ -1295,6 +2035,7 @@ const __FlashStringHelper *MFRC522::PICC_GetTypeName(PICC_Type piccType ///< One case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); + case PICC_TYPE_MIFARE_DESFIRE: return F("MIFARE DESFire"); case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); case PICC_TYPE_UNKNOWN: @@ -1327,7 +2068,59 @@ void MFRC522::PCD_DumpVersionToSerial() { /** * Dumps debug info about the selected PICC to Serial. * On success the PICC is halted after dumping the data. - * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + */ +void MFRC522::PICC_DumpToSerial(TagInfo *tag) +{ + MIFARE_Key key; + + // Dump UID, SAK and Type + PICC_DumpDetailsToSerial(tag); + + // Dump contents + PICC_Type piccType = PICC_GetType(tag->uid.sak); + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + case PICC_TYPE_MIFARE_1K: + case PICC_TYPE_MIFARE_4K: + // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + PICC_DumpMifareClassicToSerial(&tag->uid, piccType, &key); + break; + + case PICC_TYPE_MIFARE_UL: + PICC_DumpMifareUltralightToSerial(); + break; + + case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_MIFARE_DESFIRE: + PICC_DumpISO14443_4(tag); + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} + +/** + * Dumps debug info about the selected PICC to Serial. + * On success the PICC is halted after dumping the data. + * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + * + * @DEPRECATED Kept for bakward compatibility */ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). ) { @@ -1354,6 +2147,7 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro break; case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_MIFARE_DESFIRE: case PICC_TYPE_ISO_18092: case PICC_TYPE_MIFARE_PLUS: case PICC_TYPE_TNP3XXX: @@ -1373,6 +2167,47 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro /** * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. */ +void MFRC522::PICC_DumpDetailsToSerial(TagInfo *tag ///< Pointer to TagInfo struct returned from a successful PICC_Select(). +) { + // ATQA + Serial.print(F("Card ATQA:")); + if (((tag->atqa & 0xFF00u) >> 8) < 0x10) + Serial.print(F(" 0")); + Serial.print((tag->atqa & 0xFF00u) >> 8, HEX); + if ((tag->atqa & 0x00FFu) < 0x10) + Serial.print(F("0")); + else + Serial.print(F(" ")); + Serial.println(tag->atqa & 0x00FFu, HEX); + + // UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < tag->uid.size; i++) { + if (tag->uid.uidByte[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(tag->uid.uidByte[i], HEX); + } + Serial.println(); + + // SAK + Serial.print(F("Card SAK: ")); + if (tag->uid.sak < 0x10) + Serial.print(F("0")); + Serial.println(tag->uid.sak, HEX); + + // (suggested) PICC type + PICC_Type piccType = PICC_GetType(tag); + Serial.print(F("PICC type: ")); + Serial.println(PICC_GetTypeName(piccType)); +} // End PICC_DumpDetailsToSerial() + +/** + * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. + * + * @DEPRECATED kept for backward compatibility + */ void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). ) { // UID @@ -1630,6 +2465,26 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { } } // End PICC_DumpMifareUltralightToSerial() +/** + * Dumps memory contents of a ISO-14443-4 PICC. + */ +void MFRC522::PICC_DumpISO14443_4(TagInfo *tag) +{ + // ATS + if (tag->ats.size > 0x00) { // The first byte is the ATS length including the length byte + Serial.print(F("Card ATS:")); + for (byte offset = 0; offset < tag->ats.size; offset++) { + if (tag->ats.data[offset] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(tag->ats.data[offset], HEX); + } + Serial.println(); + } + +} // End PICC_DumpISO14443_4 + /** * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1). */ @@ -1862,8 +2717,42 @@ bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { bool MFRC522::PICC_IsNewCardPresent() { byte bufferATQA[2]; byte bufferSize = sizeof(bufferATQA); + + // Reset baud rates + PCD_WriteRegister(TxModeReg, 0x00); + PCD_WriteRegister(RxModeReg, 0x00); + // Reset ModWidthReg + PCD_WriteRegister(ModWidthReg, 0x26); + MFRC522::StatusCode result = PICC_RequestA(bufferATQA, &bufferSize); - return (result == STATUS_OK || result == STATUS_COLLISION); + + if (result == STATUS_OK || result == STATUS_COLLISION) { + tag.atqa = ((uint16_t)bufferATQA[1] << 8) | bufferATQA[0]; + tag.ats.size = 0; + tag.ats.fsc = 32; // default FSC value + + // Defaults for TA1 + tag.ats.ta1.transmitted = false; + tag.ats.ta1.sameD = false; + tag.ats.ta1.ds = BITRATE_106KBITS; + tag.ats.ta1.dr = BITRATE_106KBITS; + + // Defaults for TB1 + tag.ats.tb1.transmitted = false; + tag.ats.tb1.fwi = 0; // TODO: Don't know the default for this! + tag.ats.tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + + // Defaults for TC1 + tag.ats.tc1.transmitted = false; + tag.ats.tc1.supportsCID = true; + tag.ats.tc1.supportsNAD = false; + + memset(tag.ats.data, 0, FIFO_SIZE - 2); + + tag.blockNumber = false; + return true; + } + return false; } // End PICC_IsNewCardPresent() /** @@ -1875,6 +2764,15 @@ bool MFRC522::PICC_IsNewCardPresent() { * @return bool */ bool MFRC522::PICC_ReadCardSerial() { - MFRC522::StatusCode result = PICC_Select(&uid); - return (result == STATUS_OK); + MFRC522::StatusCode result = PICC_Select(&tag.uid); + + // Backward compatibility + uid.size = tag.uid.size; + uid.sak = tag.uid.sak; + memcpy(uid.uidByte, tag.uid.uidByte, sizeof(tag.uid.uidByte)); + + if (result != STATUS_OK) + return false; + + return true; } // End diff --git a/MFRC522.h b/MFRC522.h index 976ded7..1b4b7f8 100644 --- a/MFRC522.h +++ b/MFRC522.h @@ -135,6 +135,9 @@ const byte FM17522_firmware_reference[] PROGMEM = { class MFRC522 { public: + // Size of the MFRC522 FIFO + static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + // MFRC522 registers. Described in chapter 9 of the datasheet. // When using SPI all addresses are shifted one bit left in the "SPI address byte" (section 8.1.2.3) enum PCD_Register : byte { @@ -251,6 +254,7 @@ public: PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. // The read/write commands can also be used for MIFARE Ultralight. @@ -284,6 +288,7 @@ public: PICC_TYPE_MIFARE_4K , // MIFARE Classic protocol, 4KB PICC_TYPE_MIFARE_UL , // MIFARE Ultralight or Ultralight C PICC_TYPE_MIFARE_PLUS , // MIFARE Plus + PICC_TYPE_MIFARE_DESFIRE, // MIFARE DESFire PICC_TYPE_TNP3XXX , // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure PICC_TYPE_NOT_COMPLETE = 0xff // SAK indicates UID is not complete. }; @@ -301,6 +306,14 @@ public: STATUS_CRC_WRONG , // The CRC_A does not match STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK. }; + + // ISO/IEC 14443-4 bit rates + enum TagBitRates : byte { + BITRATE_106KBITS = 0x00, + BITRATE_212KBITS = 0x01, + BITRATE_424KBITS = 0x02, + BITRATE_848KBITS = 0x03 + }; // A struct used for passing the UID of a PICC. typedef struct { @@ -308,17 +321,66 @@ public: byte uidByte[10]; byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. } Uid; - + + // Structure to store ISO/IEC 14443-4 ATS + typedef struct { + byte size; + byte fsc; // Frame size for proximity card + + struct { + bool transmitted; + bool sameD; // Only the same D for both directions supported + TagBitRates ds; // Send D + TagBitRates dr; // Receive D + } ta1; + + struct { + bool transmitted; + byte fwi; // Frame waiting time integer + byte sfgi; // Start-up frame guard time integer + } tb1; + + struct { + bool transmitted; + bool supportsCID; + bool supportsNAD; + } tc1; + + // Raw data from ATS + byte data[FIFO_SIZE - 2]; // ATS cannot be bigger than FSD - 2 bytes (CRC), according to ISO 14443-4 5.2.2 + } Ats; + + // A struct used for passing the PICC information + typedef struct { + uint16_t atqa; + Uid uid; + Ats ats; + + // For Block PCB + bool blockNumber; + } TagInfo; + // A struct used for passing a MIFARE Crypto1 key typedef struct { byte keyByte[MF_KEY_SIZE]; } MIFARE_Key; + + // A struct used for passing PCB Block + typedef struct { + struct { + byte pcb; + byte cid; + byte nad; + } prologue; + struct { + byte size; + byte *data; + } inf; + } PcbBlock; // Member variables Uid uid; // Used by PICC_ReadCardSerial(). - - // Size of the MFRC522 FIFO - static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + TagInfo tag; ///////////////////////////////////////////////////////////////////////////////////// // Functions for setting up the Arduino @@ -362,7 +424,18 @@ public: StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); StatusCode PICC_Select(Uid *uid, byte validBits = 0); StatusCode PICC_HaltA(); + StatusCode PICC_RequestATS(Ats *ats); + StatusCode PICC_PPS(); // PPS command without bitrate parameter + StatusCode PICC_PPS(TagBitRates sendBitRate, TagBitRates receiveBitRate); // Different D values + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with ISO/IEC 14433-4 cards + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode TCL_Transceive(PcbBlock *send, PcbBlock *back); + StatusCode TCL_Transceive(TagInfo * tag, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL); + StatusCode TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData = NULL, byte *backLen = NULL); + StatusCode TCL_Deselect(TagInfo *tag); + ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// @@ -387,17 +460,21 @@ public: //const char *GetStatusCodeName(byte code); static const __FlashStringHelper *GetStatusCodeName(StatusCode code); static PICC_Type PICC_GetType(byte sak); + static PICC_Type PICC_GetType(TagInfo *tag); // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory //const char *PICC_GetTypeName(byte type); static const __FlashStringHelper *PICC_GetTypeName(PICC_Type type); // Support functions for debuging void PCD_DumpVersionToSerial(); + void PICC_DumpToSerial(TagInfo *tag); void PICC_DumpToSerial(Uid *uid); + void PICC_DumpDetailsToSerial(TagInfo *tag); void PICC_DumpDetailsToSerial(Uid *uid); void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); void PICC_DumpMifareUltralightToSerial(); + void PICC_DumpISO14443_4(TagInfo *tag); // Advanced functions for MIFARE void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); diff --git a/keywords.txt b/keywords.txt index ae2a39e..f1989c6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,8 +13,11 @@ PICC_Command KEYWORD1 MIFARE_Misc KEYWORD1 PICC_Type KEYWORD1 StatusCode KEYWORD1 +TagBitRates KEYWORD1 Uid KEYWORD1 +CardInfo KEYWORD1 MIFARE_Key KEYWORD1 +PcbBlock KEYWORD1 ####################################### # KEYWORD2 Methods and functions @@ -47,6 +50,13 @@ PICC_WakeupA KEYWORD2 PICC_REQA_or_WUPA KEYWORD2 PICC_Select KEYWORD2 PICC_HaltA KEYWORD2 +PICC_RATS KEYWORD2 +PICC_PPS KEYWORD2 + +# Functions for communicating with ISO/IEC 14433-4 cards +TCL_Transceive KEYWORD2 +TCL_TransceiveRBlock KEYWORD2 +TCL_Deselect KEYWORD2 # Functions for communicating with MIFARE PICCs PCD_Authenticate KEYWORD2 @@ -72,6 +82,7 @@ PICC_DumpDetailsToSerial KEYWORD2 PICC_DumpMifareClassicToSerial KEYWORD2 PICC_DumpMifareClassicSectorToSerial KEYWORD2 PICC_DumpMifareUltralightToSerial KEYWORD2 +PICC_DumpISO14443_4 KEYWORD2 # Advanced functions for MIFARE MIFARE_SetAccessBits KEYWORD2 @@ -168,6 +179,7 @@ PICC_CMD_SEL_CL1 LITERAL1 PICC_CMD_SEL_CL2 LITERAL1 PICC_CMD_SEL_CL3 LITERAL1 PICC_CMD_HLTA LITERAL1 +PICC_CMD_RATS LITERAL1 PICC_CMD_MF_AUTH_KEY_A LITERAL1 PICC_CMD_MF_AUTH_KEY_B LITERAL1 PICC_CMD_MF_READ LITERAL1 @@ -187,6 +199,7 @@ PICC_TYPE_MIFARE_1K LITERAL1 PICC_TYPE_MIFARE_4K LITERAL1 PICC_TYPE_MIFARE_UL LITERAL1 PICC_TYPE_MIFARE_PLUS LITERAL1 +PICC_TYPE_MIFARE_DESFIRE LITERAL1 PICC_TYPE_TNP3XXX LITERAL1 PICC_TYPE_NOT_COMPLETE LITERAL1 STATUS_OK LITERAL1 @@ -199,3 +212,7 @@ STATUS_INVALID LITERAL1 STATUS_CRC_WRONG LITERAL1 STATUS_MIFARE_NACK LITERAL1 FIFO_SIZE LITERAL1 +BITRATE_106KBITS LITERAL1 +BITRATE_212KBITS LITERAL1 +BITRATE_424KBITS LITERAL1 +BITRATE_848KBITS LITERAL1 \ No newline at end of file