//-----------------------------------------------------------------------------
#include "iso14443a.h"
+#include "iso14443b.h"
#include "epa.h"
#include "cmd.h"
-// Protocol and Parameter Selection Request
+// Protocol and Parameter Selection Request for ISO 14443 type A cards
// use regular (1x) speed in both directions
// CRC is already included
static const uint8_t pps[] = {0xD0, 0x11, 0x00, 0x52, 0xA6};
// lengths of the replay APDUs
static uint8_t apdu_lengths_replay[5];
+// type of card (ISO 14443 A or B)
+static char iso_type = 0;
+
+//-----------------------------------------------------------------------------
+// Wrapper for sending APDUs to type A and B cards
+//-----------------------------------------------------------------------------
+int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
+{
+ switch(iso_type)
+ {
+ case 'a':
+ return iso14_apdu(apdu, (uint16_t) length, response);
+ break;
+ case 'b':
+ return iso14443b_apdu(apdu, length, response);
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
//-----------------------------------------------------------------------------
// Closes the communication channel and turns off the field
//-----------------------------------------------------------------------------
{
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
+ iso_type = 0;
}
//-----------------------------------------------------------------------------
int rapdu_length = 0;
// select the file EF.CardAccess
- rapdu_length = iso14_apdu((uint8_t *)apdu_select_binary_cardaccess,
+ rapdu_length = EPA_APDU((uint8_t *)apdu_select_binary_cardaccess,
sizeof(apdu_select_binary_cardaccess),
response_apdu);
- if (rapdu_length != 6
+ if (rapdu_length < 6
|| response_apdu[rapdu_length - 4] != 0x90
|| response_apdu[rapdu_length - 3] != 0x00)
{
- Dbprintf("epa - no select cardaccess");
+ DbpString("Failed to select EF.CardAccess!");
return -1;
}
// read the file
- rapdu_length = iso14_apdu((uint8_t *)apdu_read_binary,
+ rapdu_length = EPA_APDU((uint8_t *)apdu_read_binary,
sizeof(apdu_read_binary),
response_apdu);
if (rapdu_length <= 6
|| response_apdu[rapdu_length - 4] != 0x90
|| response_apdu[rapdu_length - 3] != 0x00)
{
- Dbprintf("epa - no read cardaccess");
+ Dbprintf("Failed to read EF.CardAccess!");
return -1;
}
// send it
uint8_t response_apdu[262];
- int send_return = iso14_apdu(apdu,
+ int send_return = EPA_APDU(apdu,
sizeof(apdu),
response_apdu);
// check if the command succeeded
apdu[4] = apdu_length - 5;
// send it
uint8_t response_apdu[6];
- int send_return = iso14_apdu(apdu,
+ int send_return = EPA_APDU(apdu,
apdu_length,
response_apdu);
// check if the command succeeded
// now replay the data and measure the timings
for (int i = 0; i < sizeof(apdu_lengths_replay); i++) {
StartCountUS();
- func_return = iso14_apdu(apdus_replay[i].data,
+ func_return = EPA_APDU(apdus_replay[i].data,
apdu_lengths_replay[i],
response_apdu);
timings[i] = GetCountUS();
uint8_t pps_response_par[1];
iso14a_card_select_t card_select_info;
+ // first, look for type A cards
// power up the field
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
// select the card
return_code = iso14443a_select_card(uid, &card_select_info, NULL);
- if (return_code != 1) {
- return 1;
- }
+ if (return_code == 1) {
// send the PPS request
ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL);
return_code = ReaderReceive(pps_response, pps_response_par);
if (return_code != 3 || pps_response[0] != 0xD0) {
return return_code == 0 ? 2 : return_code;
}
+ Dbprintf("ISO 14443 Type A");
+ iso_type = 'a';
return 0;
+ }
+
+ // if we're here, there is no type A card, so we look for type B
+ // power up the field
+ iso14443b_setup();
+ // select the card
+ return_code = iso14443b_select_card();
+ if (return_code == 1) {
+ Dbprintf("ISO 14443 Type B");
+ iso_type = 'b';
+ return 0;
+ }
+ Dbprintf("No card found.");
+ return 1;
}
#include "iso14443crc.h"
-#define RECEIVE_SAMPLES_TIMEOUT 200000
-#define ISO14443B_DMA_BUFFER_SIZE 512
+#define RECEIVE_SAMPLES_TIMEOUT 0x0003FFFF
+#define ISO14443B_DMA_BUFFER_SIZE 256
uint8_t PowerOn = TRUE;
+// PCB Block number for APDUs
+static uint8_t pcb_blocknum = 0;
+
//=============================================================================
// An ISO 14443 Type B tag. We listen for commands from the reader, using
// a UART kind of thing that's implemented in software. When we get a
if(Demod.posCount < 10*2) { // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges
Demod.state = DEMOD_UNSYNCD;
} else {
+ LED_C_ON(); // Got SOF
Demod.state = DEMOD_AWAITING_START_BIT;
Demod.posCount = 0;
Demod.len = 0;
Demod.bitCount++;
if(Demod.bitCount == 10) {
- LED_C_ON();
uint16_t s = Demod.shiftReg;
if((s & 0x200) && !(s & 0x001)) { // stop bit == '1', start bit == '0'
uint8_t b = (s >> 1);
AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS;
- if (!quiet) {
+ if (!quiet && Demod.len == 0) {
Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d",
max,
samples,
FpgaSetupSsc();
+ // Start the timer
+ StartCountSspClk();
+
while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
AT91C_BASE_SSC->SSC_THR = 0xff;
}
}
}
+/* Sends an APDU to the tag
+ * TODO: check CRC and preamble
+ */
+int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response)
+{
+ uint8_t message_frame[message_length + 4];
+ // PCB
+ message_frame[0] = 0x0A | pcb_blocknum;
+ pcb_blocknum ^= 1;
+ // CID
+ message_frame[1] = 0;
+ // INF
+ memcpy(message_frame + 2, message, message_length);
+ // EDC (CRC)
+ ComputeCrc14443(CRC_14443_B, message_frame, message_length + 2, &message_frame[message_length + 2], &message_frame[message_length + 3]);
+ // send
+ CodeAndTransmit14443bAsReader(message_frame, message_length + 4);
+ // get response
+ GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, TRUE);
+ if(Demod.len < 3)
+ {
+ return 0;
+ }
+ // TODO: Check CRC
+ // copy response contents
+ if(response != NULL)
+ {
+ memcpy(response, Demod.output, Demod.len);
+ }
+ return Demod.len;
+}
+
+/* Perform the ISO 14443 B Card Selection procedure
+ * Currently does NOT do any collision handling.
+ * It expects 0-1 cards in the device's range.
+ * TODO: Support multiple cards (perform anticollision)
+ * TODO: Verify CRC checksums
+ */
+int iso14443b_select_card()
+{
+ // WUPB command (including CRC)
+ // Note: WUPB wakes up all tags, REQB doesn't wake up tags in HALT state
+ static const uint8_t wupb[] = { 0x05, 0x00, 0x08, 0x39, 0x73 };
+ // ATTRIB command (with space for CRC)
+ uint8_t attrib[] = { 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00};
+
+ // first, wake up the tag
+ CodeAndTransmit14443bAsReader(wupb, sizeof(wupb));
+ GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE);
+ // ATQB too short?
+ if (Demod.len < 14)
+ {
+ return 2;
+ }
+
+ // select the tag
+ // copy the PUPI to ATTRIB
+ memcpy(attrib + 1, Demod.output + 1, 4);
+ /* copy the protocol info from ATQB (Protocol Info -> Protocol_Type) into
+ ATTRIB (Param 3) */
+ attrib[7] = Demod.output[10] & 0x0F;
+ ComputeCrc14443(CRC_14443_B, attrib, 9, attrib + 9, attrib + 10);
+ CodeAndTransmit14443bAsReader(attrib, sizeof(attrib));
+ GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE);
+ // Answer to ATTRIB too short?
+ if(Demod.len < 3)
+ {
+ return 2;
+ }
+ // reset PCB block number
+ pcb_blocknum = 0;
+ return 1;
+}
+
+// Set up ISO 14443 Type B communication (similar to iso14443a_setup)
+void iso14443b_setup() {
+ FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
+ BigBuf_free();
+ // Set up the synchronous serial port
+ FpgaSetupSsc();
+ // connect Demodulated Signal to ADC:
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+ // Signal field is on with the appropriate LED
+ LED_D_ON();
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD);
+
+ // Start the timer
+ StartCountSspClk();
+
+ DemodReset();
+ UartReset();
+}
//-----------------------------------------------------------------------------
// Read a SRI512 ISO 14443B tag.
*/
void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[])
{
+ iso14443b_setup();
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
BigBuf_free();
- SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
if ( !PowerOn ){
FpgaSetupSsc();
}
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+ // Start the timer
+ StartCountSspClk();
+
+ DemodReset();
+ UartReset();
if ( datalen == 0 && recv == 0 && powerfield == 0){
clear_trace();