+ if(p_hi14a_card) {
+ memcpy(p_hi14a_card->ats, resp, len);
+ p_hi14a_card->ats_len = len;
+ }
+
+ // reset the PCB block number
+ iso14_pcb_blocknum = 0;
+
+ // set default timeout and delay next transfer based on ATS
+ iso14a_set_ATS_times(resp);
+
+ }
+ return 1;
+}
+
+
+void iso14443a_setup(uint8_t fpga_minor_mode) {
+ FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
+ // Set up the synchronous serial port
+ FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A);
+ // connect Demodulated Signal to ADC:
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+ // Signal field is on with the appropriate LED
+ if (fpga_minor_mode == FPGA_HF_ISO14443A_READER_MOD
+ || fpga_minor_mode == FPGA_HF_ISO14443A_READER_LISTEN) {
+ LED_D_ON();
+ } else {
+ LED_D_OFF();
+ }
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode);
+
+ // Set ADC to read field strength
+ AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST;
+ AT91C_BASE_ADC->ADC_MR =
+ ADC_MODE_PRESCALE(63) |
+ ADC_MODE_STARTUP_TIME(1) |
+ ADC_MODE_SAMPLE_HOLD_TIME(15);
+ AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF_LOW);
+
+ // Start the timer
+ StartCountSspClk();
+
+ DemodReset();
+ UartReset();
+ LastTimeProxToAirStart = 0;
+ FpgaSendQueueDelay = 0;
+ LastProxToAirDuration = 20; // arbitrary small value. Avoid lock in EmGetCmd()
+ NextTransferTime = 2*DELAY_ARM2AIR_AS_READER;
+ iso14a_set_timeout(1060); // 10ms default
+}
+
+/* Peter Fillmore 2015
+Added card id field to the function
+ info from ISO14443A standard
+b1 = Block Number
+b2 = RFU (always 1)
+b3 = depends on block
+b4 = Card ID following if set to 1
+b5 = depends on block type
+b6 = depends on block type
+b7,b8 = block type.
+Coding of I-BLOCK:
+b8 b7 b6 b5 b4 b3 b2 b1
+0 0 0 x x x 1 x
+b5 = chaining bit
+Coding of R-block:
+b8 b7 b6 b5 b4 b3 b2 b1
+1 0 1 x x 0 1 x
+b5 = ACK/NACK
+Coding of S-block:
+b8 b7 b6 b5 b4 b3 b2 b1
+1 1 x x x 0 1 0
+b5,b6 = 00 - DESELECT
+ 11 - WTX
+*/
+int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint8_t *res) {
+ uint8_t parity[MAX_PARITY_SIZE];
+ uint8_t real_cmd[cmd_len + 4];
+
+ if (cmd_len) {
+ // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
+ real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
+ if (send_chaining) {
+ real_cmd[0] |= 0x10;
+ }
+ // put block number into the PCB
+ real_cmd[0] |= iso14_pcb_blocknum;
+ memcpy(real_cmd + 1, cmd, cmd_len);
+ } else {
+ // R-block. ACK
+ real_cmd[0] = 0xA2; // r-block + ACK
+ real_cmd[0] |= iso14_pcb_blocknum;
+ }
+ AppendCrc14443a(real_cmd, cmd_len + 1);
+
+ ReaderTransmit(real_cmd, cmd_len + 3, NULL);
+
+ size_t len = ReaderReceive(data, parity);
+ uint8_t *data_bytes = (uint8_t *) data;
+
+ if (!len) {
+ return 0; //DATA LINK ERROR
+ } else {
+ // S-Block WTX
+ while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
+ uint32_t save_iso14a_timeout = iso14a_get_timeout();
+ // temporarily increase timeout
+ iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT));
+ // Transmit WTX back
+ // byte1 - WTXM [1..59]. command FWT=FWT*WTXM
+ data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b
+ // now need to fix CRC.
+ AppendCrc14443a(data_bytes, len - 2);
+ // transmit S-Block
+ ReaderTransmit(data_bytes, len, NULL);
+ // retrieve the result again (with increased timeout)
+ len = ReaderReceive(data, parity);
+ data_bytes = data;
+ // restore timeout
+ iso14a_set_timeout(save_iso14a_timeout);
+ }
+
+ // if we received an I- or R(ACK)-Block with a block number equal to the
+ // current block number, toggle the current block number
+ if (len >= 3 // PCB+CRC = 3 bytes
+ && ((data_bytes[0] & 0xC0) == 0 // I-Block
+ || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
+ && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers
+ {
+ iso14_pcb_blocknum ^= 1;
+ }
+
+ // if we received I-block with chaining we need to send ACK and receive another block of data
+ if (res)
+ *res = data_bytes[0];
+
+ // crc check
+ if (len >= 3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) {
+ return -1;
+ }
+
+ }
+
+ if (len) {
+ // cut frame byte
+ len -= 1;
+ // memmove(data_bytes, data_bytes + 1, len);
+ for (int i = 0; i < len; i++)
+ data_bytes[i] = data_bytes[i + 1];
+ }
+
+ return len;
+}
+
+
+//-----------------------------------------------------------------------------
+// Read an ISO 14443a tag. Send out commands and store answers.
+//
+//-----------------------------------------------------------------------------
+void ReaderIso14443a(UsbCommand *c) {
+
+ iso14a_command_t param = c->arg[0];
+ uint8_t *cmd = c->d.asBytes;
+ size_t len = c->arg[1] & 0xffff;
+ size_t lenbits = c->arg[1] >> 16;
+ uint32_t timeout = c->arg[2];
+ uint32_t arg0 = 0;
+ uint8_t buf[USB_CMD_DATA_SIZE] = {0};
+ uint8_t par[MAX_PARITY_SIZE];
+ bool cantSELECT = false;
+
+ set_tracing(true);
+
+ if (param & ISO14A_CLEAR_TRACE) {
+ clear_trace();
+ }
+
+ if (param & ISO14A_REQUEST_TRIGGER) {
+ iso14a_set_trigger(true);
+ }
+
+ if (param & ISO14A_CONNECT) {
+ LED_A_ON();
+ iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
+ if(!(param & ISO14A_NO_SELECT)) {
+ iso14a_card_select_t *card = (iso14a_card_select_t*)buf;
+ arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS);
+
+ // if we cant select then we cant send data
+ if (arg0 != 1 && arg0 != 2) {
+ // 1 - all is OK with ATS, 2 - without ATS
+ cantSELECT = true;
+ }
+ FpgaDisableTracing();
+ LED_B_ON();
+ cmd_send(CMD_NACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t));
+ LED_B_OFF();
+ }
+ }
+
+ if (param & ISO14A_SET_TIMEOUT) {
+ iso14a_set_timeout(timeout);
+ }
+
+ if (param & ISO14A_APDU && !cantSELECT) {
+ uint8_t res;
+ arg0 = iso14_apdu(cmd, len, (param & ISO14A_SEND_CHAINING), buf, &res);
+ FpgaDisableTracing();
+ LED_B_ON();
+ cmd_send(CMD_ACK, arg0, res, 0, buf, sizeof(buf));
+ LED_B_OFF();
+ }
+
+ if (param & ISO14A_RAW && !cantSELECT) {
+ if (param & ISO14A_APPEND_CRC) {
+ if(param & ISO14A_TOPAZMODE) {
+ AppendCrc14443b(cmd,len);
+ } else {
+ AppendCrc14443a(cmd,len);
+ }
+ len += 2;
+ if (lenbits) lenbits += 16;
+ }
+ if (lenbits > 0) { // want to send a specific number of bits (e.g. short commands)
+ if (param & ISO14A_TOPAZMODE) {
+ int bits_to_send = lenbits;
+ uint16_t i = 0;
+ ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 7), NULL, NULL); // first byte is always short (7bits) and no parity
+ bits_to_send -= 7;
+ while (bits_to_send > 0) {
+ ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 8), NULL, NULL); // following bytes are 8 bit and no parity
+ bits_to_send -= 8;
+ }
+ } else {
+ GetParity(cmd, lenbits/8, par);
+ ReaderTransmitBitsPar(cmd, lenbits, par, NULL); // bytes are 8 bit with odd parity
+ }
+ } else { // want to send complete bytes only
+ if (param & ISO14A_TOPAZMODE) {
+ uint16_t i = 0;
+ ReaderTransmitBitsPar(&cmd[i++], 7, NULL, NULL); // first byte: 7 bits, no paritiy
+ while (i < len) {
+ ReaderTransmitBitsPar(&cmd[i++], 8, NULL, NULL); // following bytes: 8 bits, no paritiy
+ }
+ } else {
+ ReaderTransmit(cmd,len, NULL); // 8 bits, odd parity
+ }
+ }
+ arg0 = ReaderReceive(buf, par);
+ FpgaDisableTracing();
+
+ LED_B_ON();
+ cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf));
+ LED_B_OFF();
+ }
+
+ if (param & ISO14A_REQUEST_TRIGGER) {
+ iso14a_set_trigger(false);
+ }
+
+ if (param & ISO14A_NO_DISCONNECT) {
+ return;
+ }
+
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+ LEDsoff();
+}
+
+
+// Determine the distance between two nonces.
+// Assume that the difference is small, but we don't know which is first.
+// Therefore try in alternating directions.
+static int32_t dist_nt(uint32_t nt1, uint32_t nt2) {