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)
This commit is contained in:
jpg-consulting
2017-01-07 11:29:46 +01:00
committed by Rotzbua
parent c4e10ed549
commit 45b7e60584
3 changed files with 1000 additions and 8 deletions

View File

@@ -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(b8b5) is set toD'to identify the PPS. All other values are RFU.
// -The lower nibble(b4b1), 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(b8b5) is set toD'to identify the PPS. All other values are RFU.
// -The lower nibble(b4b1), 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

View File

@@ -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);

View File

@@ -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