You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1161 lines
34 KiB
1161 lines
34 KiB
3 years ago
|
/*
|
||
|
* Library extends MFRC522.h to support RATS for ISO-14443-4 PICC.
|
||
|
* RATS - Request for Answer To Select.
|
||
|
* NOTE: Please also check the comments in MFRC522Extended.h
|
||
|
* @author JPG-Consulting
|
||
|
*/
|
||
|
|
||
|
#include "MFRC522Extended.h"
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
// Functions for communicating with PICCs
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Transmits SELECT/ANTICOLLISION commands to select a single PICC.
|
||
|
* Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA().
|
||
|
* On success:
|
||
|
* - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.)
|
||
|
* - The UID size and value of the chosen PICC is returned in *uid along with the SAK.
|
||
|
*
|
||
|
* A PICC UID consists of 4, 7 or 10 bytes.
|
||
|
* Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used:
|
||
|
* UID size Number of UID bytes Cascade levels Example of PICC
|
||
|
* ======== =================== ============== ===============
|
||
|
* single 4 1 MIFARE Classic
|
||
|
* double 7 2 MIFARE Ultralight
|
||
|
* triple 10 3 Not currently in use?
|
||
|
*
|
||
|
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||
|
*/
|
||
|
MFRC522::StatusCode MFRC522Extended::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
|
||
|
byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size.
|
||
|
) {
|
||
|
bool uidComplete;
|
||
|
bool selectDone;
|
||
|
bool useCascadeTag;
|
||
|
byte cascadeLevel = 1;
|
||
|
MFRC522::StatusCode result;
|
||
|
byte count;
|
||
|
byte index;
|
||
|
byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level.
|
||
|
int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level.
|
||
|
byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A
|
||
|
byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO.
|
||
|
byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received.
|
||
|
byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte.
|
||
|
byte *responseBuffer;
|
||
|
byte responseLength;
|
||
|
|
||
|
// Description of buffer structure:
|
||
|
// Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
|
||
|
// Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits.
|
||
|
// Byte 2: UID-data or CT See explanation below. CT means Cascade Tag.
|
||
|
// Byte 3: UID-data
|
||
|
// Byte 4: UID-data
|
||
|
// Byte 5: UID-data
|
||
|
// Byte 6: BCC Block Check Character - XOR of bytes 2-5
|
||
|
// Byte 7: CRC_A
|
||
|
// Byte 8: CRC_A
|
||
|
// The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level.
|
||
|
//
|
||
|
// Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
|
||
|
// UID size Cascade level Byte2 Byte3 Byte4 Byte5
|
||
|
// ======== ============= ===== ===== ===== =====
|
||
|
// 4 bytes 1 uid0 uid1 uid2 uid3
|
||
|
// 7 bytes 1 CT uid0 uid1 uid2
|
||
|
// 2 uid3 uid4 uid5 uid6
|
||
|
// 10 bytes 1 CT uid0 uid1 uid2
|
||
|
// 2 CT uid3 uid4 uid5
|
||
|
// 3 uid6 uid7 uid8 uid9
|
||
|
|
||
|
// Sanity checks
|
||
|
if (validBits > 80) {
|
||
|
return STATUS_INVALID;
|
||
|
}
|
||
|
|
||
|
// Prepare MFRC522
|
||
|
PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared.
|
||
|
|
||
|
// Repeat Cascade Level loop until we have a complete UID.
|
||
|
uidComplete = false;
|
||
|
while (!uidComplete) {
|
||
|
// Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2.
|
||
|
switch (cascadeLevel) {
|
||
|
case 1:
|
||
|
buffer[0] = PICC_CMD_SEL_CL1;
|
||
|
uidIndex = 0;
|
||
|
useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
buffer[0] = PICC_CMD_SEL_CL2;
|
||
|
uidIndex = 3;
|
||
|
useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
buffer[0] = PICC_CMD_SEL_CL3;
|
||
|
uidIndex = 6;
|
||
|
useCascadeTag = false; // Never used in CL3.
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return STATUS_INTERNAL_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// How many UID bits are known in this Cascade Level?
|
||
|
currentLevelKnownBits = validBits - (8 * uidIndex);
|
||
|
if (currentLevelKnownBits < 0) {
|
||
|
currentLevelKnownBits = 0;
|
||
|
}
|
||
|
// Copy the known bits from uid->uidByte[] to buffer[]
|
||
|
index = 2; // destination index in buffer[]
|
||
|
if (useCascadeTag) {
|
||
|
buffer[index++] = PICC_CMD_CT;
|
||
|
}
|
||
|
byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level.
|
||
|
if (bytesToCopy) {
|
||
|
byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag
|
||
|
if (bytesToCopy > maxBytes) {
|
||
|
bytesToCopy = maxBytes;
|
||
|
}
|
||
|
for (count = 0; count < bytesToCopy; count++) {
|
||
|
buffer[index++] = uid->uidByte[uidIndex + count];
|
||
|
}
|
||
|
}
|
||
|
// Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
|
||
|
if (useCascadeTag) {
|
||
|
currentLevelKnownBits += 8;
|
||
|
}
|
||
|
|
||
|
// Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
|
||
|
selectDone = false;
|
||
|
while (!selectDone) {
|
||
|
// Find out how many bits and bytes to send and receive.
|
||
|
if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT.
|
||
|
//Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
|
||
|
buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes
|
||
|
// Calculate BCC - Block Check Character
|
||
|
buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
|
||
|
// Calculate CRC_A
|
||
|
result = PCD_CalculateCRC(buffer, 7, &buffer[7]);
|
||
|
if (result != STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
txLastBits = 0; // 0 => All 8 bits are valid.
|
||
|
bufferUsed = 9;
|
||
|
// Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx)
|
||
|
responseBuffer = &buffer[6];
|
||
|
responseLength = 3;
|
||
|
}
|
||
|
else { // This is an ANTICOLLISION.
|
||
|
//Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
|
||
|
txLastBits = currentLevelKnownBits % 8;
|
||
|
count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part.
|
||
|
index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs
|
||
|
buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits
|
||
|
bufferUsed = index + (txLastBits ? 1 : 0);
|
||
|
// Store response in the unused part of buffer
|
||
|
responseBuffer = &buffer[index];
|
||
|
responseLength = sizeof(buffer) - index;
|
||
|
}
|
||
|
|
||
|
// Set bit adjustments
|
||
|
rxAlign = txLastBits; // Having a separate variable is overkill. But it makes the next line easier to read.
|
||
|
PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
|
||
|
|
||
|
// Transmit the buffer and receive the response.
|
||
|
result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign);
|
||
|
if (result == STATUS_COLLISION) { // More than one PICC in the field => collision.
|
||
|
byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
|
||
|
if (valueOfCollReg & 0x20) { // CollPosNotValid
|
||
|
return STATUS_COLLISION; // Without a valid collision position we cannot continue
|
||
|
}
|
||
|
byte collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32.
|
||
|
if (collisionPos == 0) {
|
||
|
collisionPos = 32;
|
||
|
}
|
||
|
if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen
|
||
|
return STATUS_INTERNAL_ERROR;
|
||
|
}
|
||
|
// Choose the PICC with the bit set.
|
||
|
currentLevelKnownBits = collisionPos;
|
||
|
count = (currentLevelKnownBits - 1) % 8; // The bit to modify
|
||
|
index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0.
|
||
|
buffer[index] |= (1 << count);
|
||
|
}
|
||
|
else if (result != STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
else { // STATUS_OK
|
||
|
if (currentLevelKnownBits >= 32) { // This was a SELECT.
|
||
|
selectDone = true; // No more anticollision
|
||
|
// We continue below outside the while.
|
||
|
}
|
||
|
else { // This was an ANTICOLLISION.
|
||
|
// We now have all 32 bits of the UID in this Cascade Level
|
||
|
currentLevelKnownBits = 32;
|
||
|
// Run loop again to do the SELECT.
|
||
|
}
|
||
|
}
|
||
|
} // End of while (!selectDone)
|
||
|
|
||
|
// We do not check the CBB - it was constructed by us above.
|
||
|
|
||
|
// Copy the found UID bytes from buffer[] to uid->uidByte[]
|
||
|
index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[]
|
||
|
bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
|
||
|
for (count = 0; count < bytesToCopy; count++) {
|
||
|
uid->uidByte[uidIndex + count] = buffer[index++];
|
||
|
}
|
||
|
|
||
|
// Check response SAK (Select Acknowledge)
|
||
|
if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A).
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
// Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore.
|
||
|
result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]);
|
||
|
if (result != STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) {
|
||
|
return STATUS_CRC_WRONG;
|
||
|
}
|
||
|
if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes
|
||
|
cascadeLevel++;
|
||
|
}
|
||
|
else {
|
||
|
uidComplete = true;
|
||
|
uid->sak = responseBuffer[0];
|
||
|
}
|
||
|
} // End of while (!uidComplete)
|
||
|
|
||
|
// 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 :(
|
||
|
// default never used, so only delarate
|
||
|
//TagBitRates ds = BITRATE_106KBITS;
|
||
|
//TagBitRates dr = BITRATE_106KBITS;
|
||
|
TagBitRates ds;
|
||
|
TagBitRates dr;
|
||
|
|
||
|
//// TODO 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()
|
||
|
|
||
|
/**
|
||
|
* Transmits a Request command for Answer To Select (ATS).
|
||
|
*
|
||
|
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||
|
*/
|
||
|
MFRC522::StatusCode MFRC522Extended::PICC_RequestATS(Ats *ats)
|
||
|
{
|
||
|
// TODO unused variable
|
||
|
//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)?
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// 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 MFRC522Extended::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 MFRC522Extended::PICC_PPS(TagBitRates sendBitRate, ///< DS
|
||
|
TagBitRates receiveBitRate ///< DR
|
||
|
) {
|
||
|
StatusCode result;
|
||
|
|
||
|
// TODO not used
|
||
|
//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 MFRC522Extended::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 MFRC522Extended::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
|
||
|
// TODO Warning: Value escapes the local scope
|
||
|
in.inf.data = outBuffer;
|
||
|
in.inf.size = outBufferSize;
|
||
|
|
||
|
result = TCL_Transceive(&out, &in);
|
||
|
if (result != STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Swap block number on success
|
||
|
tag->blockNumber = !tag->blockNumber;
|
||
|
|
||
|
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 MFRC522Extended::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
|
||
|
// TODO Warning: Value escapes the local scope
|
||
|
in.inf.data = outBuffer;
|
||
|
in.inf.size = outBufferSize;
|
||
|
|
||
|
result = TCL_Transceive(&out, &in);
|
||
|
if (result != STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Swap block number on success
|
||
|
tag->blockNumber = !tag->blockNumber;
|
||
|
|
||
|
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 MFRC522Extended::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()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
// Support functions
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Get the PICC type.
|
||
|
*
|
||
|
* @return PICC_Type
|
||
|
*/
|
||
|
MFRC522::PICC_Type MFRC522Extended::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()
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
void MFRC522Extended::PICC_DumpToSerial(TagInfo *tag)
|
||
|
{
|
||
|
MIFARE_Key key;
|
||
|
|
||
|
// Dump UID, SAK and Type
|
||
|
PICC_DumpDetailsToSerial(tag);
|
||
|
|
||
|
// Dump contents
|
||
|
PICC_Type piccType = MFRC522::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 card info (UID,SAK,Type) about the selected PICC to Serial.
|
||
|
*/
|
||
|
void MFRC522Extended::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 memory contents of a ISO-14443-4 PICC.
|
||
|
*/
|
||
|
void MFRC522Extended::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
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
// Convenience functions - does not add extra functionality
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Returns true if a PICC responds to PICC_CMD_REQA.
|
||
|
* Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
bool MFRC522Extended::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);
|
||
|
|
||
|
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 = MFRC522Extended::BITRATE_106KBITS;
|
||
|
tag.ats.ta1.dr = MFRC522Extended::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()
|
||
|
|
||
|
/**
|
||
|
* Simple wrapper around PICC_Select.
|
||
|
* Returns true if a UID could be read.
|
||
|
* Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first.
|
||
|
* The read UID is available in the class variable uid.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
bool MFRC522Extended::PICC_ReadCardSerial() {
|
||
|
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));
|
||
|
|
||
|
return (result == STATUS_OK);
|
||
|
} // End
|