/*
 * Copyright (c) 2014-2018, ARM Limited, All Rights Reserved
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * \file pn512_poll.c
 * \copyright Copyright (c) ARM Ltd 2014
 * \author Donatien Garnier
 */

#define __DEBUG__ 0
#ifndef __MODULE__
#define __MODULE__ "pn512_poll.c"
#endif

#include "stack/nfc_errors.h"

#include "pn512.h"
#include "pn512_poll.h"
#include "pn512_transceive.h"
#include "pn512_registers.h"
#include "pn512_rf.h"
#include "pn512_cmd.h"
#include "pn512_internal.h"

#define TIMEOUT 1000

static void pn512_target_anticollision(pn512_t *pPN512, pn512_cb_t cb);
static void pn512_initiator_isoa_anticollision(pn512_t *pPN512, pn512_cb_t cb);

static inline bool pn512_config_initiator(pn512_t *pPN512)
{
    return (pPN512->config.initiators.nfc_iso_dep_a | pPN512->config.initiators.nfc_iso_dep_b |
            pPN512->config.initiators.nfc_nfc_dep_a | pPN512->config.initiators.nfc_nfc_dep_f_212 | pPN512->config.initiators.nfc_nfc_dep_f_424 |
            pPN512->config.initiators.nfc_type1 | pPN512->config.initiators.nfc_type2 | pPN512->config.initiators.nfc_type3) != 0;
}

static inline bool pn512_config_target(pn512_t *pPN512)
{
    return (pPN512->config.targets.nfc_iso_dep_a | pPN512->config.targets.nfc_iso_dep_b |
            pPN512->config.targets.nfc_nfc_dep_a | pPN512->config.targets.nfc_nfc_dep_f_212 | pPN512->config.initiators.nfc_nfc_dep_f_424 |
            pPN512->config.targets.nfc_type1 | pPN512->config.targets.nfc_type2 | pPN512->config.targets.nfc_type3) != 0;
}

void pn512_target_anticollision_complete(pn512_t *pPN512, nfc_err_t ret)
{

    bool iso14443a = pPN512->config.targets.nfc_type2 || pPN512->config.targets.nfc_iso_dep_a || pPN512->config.targets.nfc_nfc_dep_a; //We do not support type 1 card emulation so irrelevant
    bool felica = pPN512->config.targets.nfc_type3 || pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424;

    nfc_transceiver_t *pTransceiver = &pPN512->transceiver;
    if (ret) {
        NFC_WARN("Returned %d", ret);
        pn512_anticollision_callback(pPN512, ret);
        return;
    }

    //Data available in FIFO
    if (pPN512->readLastByteLength != 8) { //We should receive a full byte
        NFC_WARN("Not enough data in FIFO");
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    //If successful, update state machine
    if (iso14443a && felica) {
        //Update current protocol accordingly
        uint8_t txmode = pn512_register_read(pPN512, PN512_REG_TXMODE);
        if ((txmode & 0x03) == 0x00) {
            pn512_framing_set(pPN512, nfc_framing_target_a_106);

            NFC_DBG("A 106");
            felica = false;
        } else if ((txmode & 0x03) == 0x02) {
            if ((txmode & 0x70) == 0x20) {
                //424kbps
                NFC_DBG("F 424");
                pn512_framing_set(pPN512, nfc_framing_target_f_424);
            } else {
                //212kbps
                NFC_DBG("F 212");
                pn512_framing_set(pPN512, nfc_framing_target_f_212);
            }
            iso14443a = false;
        } else {
            //Unsupported mode, exit
            pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED);
            return;
        }
    }

    if (iso14443a) {
        if (ac_buffer_reader_readable(ac_buffer_builder_buffer(&pPN512->readBufBldr)) == 0) {
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }

        //Halt device, so that if anticollision is restarted it's in the correct state (cleared automatically by RF reset)
        pn512_register_write(pPN512, PN512_REG_MIFNFC, 0x62 | 0x04);

        //Copy buffer to peek
        ac_buffer_t readBufDup;
        ac_buffer_dup(&readBufDup, ac_buffer_builder_buffer(&pPN512->readBufBldr));

        uint8_t b0 = ac_buffer_read_nu8(&readBufDup);

        //Get first byte
        //Read FIFO to see if the target was selected as NFC-DEP, ISO-DEP or proprietary (NFC Type 2)
        //F0 --> NFC-DEP
        //E0 --> ISO-DEP
        //Anything else --> NFC Type 2

        //First check if this could be NFC-DEP
        if (pPN512->config.targets.nfc_nfc_dep_a && (b0 == 0xF0)) {
            pTransceiver->active_tech.nfc_nfc_dep_a = true;
        } else if (pPN512->config.targets.nfc_iso_dep_a && (b0 == 0xE0)) {
            pTransceiver->active_tech.nfc_iso_dep_a = true;
        } else if (pPN512->config.targets.nfc_type2) {
            pTransceiver->active_tech.nfc_type2 = true;
        } else {
            //Unknown tech, return error
            pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED);
            return;
        }

        //Give control to higher layer
        pn512_anticollision_callback(pPN512, NFC_OK);
        return;
    } else if (felica) {
        //First check if this could be NFC-DEP
        if ((pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424)) {
            if (pPN512->framing == nfc_framing_target_f_424) {
                pTransceiver->active_tech.nfc_nfc_dep_f_424 = true;
            } else {
                pTransceiver->active_tech.nfc_nfc_dep_f_212 = true;
            }
        } else {
            pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED);
            return;
        }
    }

    //NFC-IP 1 active mode is not supported by other devices so ignore it for now
    pn512_anticollision_callback(pPN512, NFC_OK);
}


void pn512_target_anticollision(pn512_t *pPN512, pn512_cb_t cb)
{
    pPN512->anticollision.cb = cb;

    //Reset active states
    pPN512->transceiver.initiator_ntarget = false;
    pPN512->transceiver.active_tech.nfc_type1 = pPN512->transceiver.active_tech.nfc_type2 = pPN512->transceiver.active_tech.nfc_type3
                                                                                            = pPN512->transceiver.active_tech.nfc_iso_dep_a = pPN512->transceiver.active_tech.nfc_nfc_dep_a =
                                                                                                        pPN512->transceiver.active_tech.nfc_nfc_dep_f_212 = pPN512->transceiver.active_tech.nfc_nfc_dep_f_424 = 0;

    pn512_set_timeout((nfc_transceiver_t *)pPN512, -1); //Should only fail on RF drop

    pn512_transceive_hw(pPN512, pn512_transceive_mode_target_autocoll, pn512_target_anticollision_complete);
}

// ISO A

#define ISO14443A_BUF_SIZE 8

#define REQA 0x26
#define SEL(n) (0x93 + (n)*2)
#define NVB(bits) ( (((2*8 + (bits))>>3)<<4) | ((bits) & 0x7) )
#define HALTA1 0x50
#define HALTA2 0x00
#define CT 0x88

static void pn512_initiator_isoa_anticollision_reqa(pn512_t *pPN512);
static void pn512_initiator_isoa_anticollision_atqa(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isoa_anticollision_cascade_1(pn512_t *pPN512);
static void pn512_initiator_isoa_anticollision_cascade_2(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isoa_anticollision_cascade_3(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isoa_anticollision_cascade_4(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isoa_anticollision_complete(pn512_t *pPN512);

void pn512_initiator_isoa_anticollision(pn512_t *pPN512, pn512_cb_t cb)
{
    pPN512->anticollision.cb = cb;

    // Reset transceive mode
    pPN512->transceive.mode = pn512_transceive_mode_idle;

    pn512_initiator_isoa_anticollision_reqa(pPN512);
}

void pn512_initiator_isoa_anticollision_reqa(pn512_t *pPN512)
{
    ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
    ac_buffer_builder_reset(pDataOutBldr);

    pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength = 0;

    ac_buffer_builder_write_nu8(pDataOutBldr, REQA);
    pn512_set_last_byte_length((nfc_transceiver_t *)pPN512, 7); //Only 7 bits in this first command
    // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x00); // Set MSB to 0, to accept collisions
    pn512_set_crc((nfc_transceiver_t *)pPN512, false, false);
    pn512_set_timeout((nfc_transceiver_t *)pPN512, 4);
    pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));

    pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_atqa);
}

void pn512_initiator_isoa_anticollision_atqa(pn512_t *pPN512, nfc_err_t ret)
{
    // Clear collisions register
    // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x80); // Set MSB to 1, to treat collisions as errors

    if (ret && (ret != NFC_ERR_COLLISION)) { // There might be collisions here
        NFC_WARN("Did not receive ATQA: error %d", ret);
        pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER);
        return;
    }

    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    if (ac_buffer_reader_readable(pResp) != 2) {
        NFC_WARN("Wrong length (%u bytes)", ac_buffer_reader_readable(pResp));
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    NFC_DBG("Got ATQA:");
    NFC_DBG_BLOCK(ac_buffer_dump(pResp);)

    // Ignore ATQA as there can be collisions
    ac_buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.atqa, 2);

    //Start cascading
    pPN512->anticollision.iso_a.cascade_level = 1;
    pPN512->anticollision.iso_a.valid_bits = 0;
    pPN512->anticollision.iso_a.more_targets = false;
    memset(pPN512->anticollision.iso_a.cln, 0, 5);

    pn512_initiator_isoa_anticollision_cascade_1(pPN512);
}

void pn512_initiator_isoa_anticollision_cascade_1(pn512_t *pPN512)
{
    ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
    ac_buffer_builder_reset(pDataOutBldr);

    //SEL
    ac_buffer_builder_write_nu8(pDataOutBldr, SEL(pPN512->anticollision.iso_a.cascade_level - 1)); //SEL: Cascade level
    ac_buffer_builder_write_nu8(pDataOutBldr, NVB(pPN512->anticollision.iso_a.valid_bits)); //First NVB: Bytecount = 2, Bitcount = 0, then adapt if collision detected

    NFC_DBG("SEL - cascade level %u,  %u valid bits - NVB %u", pPN512->anticollision.iso_a.cascade_level, pPN512->anticollision.iso_a.valid_bits, NVB(pPN512->anticollision.iso_a.valid_bits));

    if (pPN512->anticollision.iso_a.valid_bits > 0) {
        // Transmit first part of uid
        ac_buffer_builder_write_n_bytes(pDataOutBldr, pPN512->anticollision.iso_a.cln, (pPN512->anticollision.iso_a.valid_bits >> 3) + ((pPN512->anticollision.iso_a.valid_bits & 0x7) ? 1 : 0));
        pn512_set_last_byte_length((nfc_transceiver_t *)pPN512, pPN512->anticollision.iso_a.valid_bits & 0x7);
        pn512_set_first_byte_align((nfc_transceiver_t *)pPN512, pPN512->anticollision.iso_a.valid_bits & 0x7);
    }

    // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x00); // Set MSB to 0, to accept collisions
    pn512_set_crc((nfc_transceiver_t *)pPN512, false, false);
    pn512_set_timeout((nfc_transceiver_t *)pPN512, 30);
    pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));


    pn512_register_write(pPN512, PN512_REG_COMIRQ, 2);
    NFC_DBG("IRQ status %04X", ((pn512_register_read(pPN512, PN512_REG_COMIRQ))
                                | ((pn512_register_read(pPN512, PN512_REG_DIVIRQ)) << 8)));

    pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_cascade_2);
}

void pn512_initiator_isoa_anticollision_cascade_2(pn512_t *pPN512, nfc_err_t ret)
{
    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    // Load & clear collisions register
    // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x80); // Set MSB to 1, to treat collisions as errors

    if (ret && (ret != NFC_ERR_COLLISION)) { // There might be collisions here
        NFC_WARN("Did not receive response: error %d", ret);
        pn512_anticollision_callback(pPN512, ret);
        return;
    }

    // We should receive 5 bytes back minus all the CLn bits that we have transmitted; we ignore all bits from the first collision
    size_t expected_resp_bits = (5 << 3) - pPN512->anticollision.iso_a.valid_bits;

    // Check for collision
    uint8_t valid_bits = expected_resp_bits;
    if (ret == NFC_ERR_COLLISION) {
        uint8_t coll_reg = pn512_register_read(pPN512, PN512_REG_COLL);

        // FIXME - PN512 error
        //if( !(coll_reg & 0x20) ) // bit 5 is CollPosNotValidSet
        {
            valid_bits = (coll_reg & 0x1f);

            if (valid_bits == 0) {
                valid_bits = 32;
            }

            valid_bits--;

            NFC_DBG("Collision detected, %u valid bits", valid_bits);
            if (valid_bits < expected_resp_bits) {
                // Collision detected
                pPN512->anticollision.iso_a.more_targets = true;
            } else {
                valid_bits = expected_resp_bits;
            }
        }
    }

    size_t resp_sz = (valid_bits >> 3) + ((valid_bits & 0x7) ? 1 : 0);
    if (ac_buffer_reader_readable(pResp) < resp_sz) {
        (void) pn512_register_read(pPN512, PN512_REG_COLL);

        NFC_WARN("Wrong length (%u instead of %u - valid bits %u)", ac_buffer_reader_readable(pResp), resp_sz, valid_bits);
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    // Read bytes
    uint8_t bufIn[5] = {0};
    ac_buffer_read_n_bytes(pResp, bufIn + (pPN512->anticollision.iso_a.valid_bits >> 3), resp_sz);

    // Mask out valid bits that are already known
    bufIn[pPN512->anticollision.iso_a.valid_bits >> 3] &= 0xff << (pPN512->anticollision.iso_a.valid_bits & 0x7);

    // Update number of valid bits
    pPN512->anticollision.iso_a.valid_bits += valid_bits;

    // Mask out bits past valid bits in last byte
    bufIn[pPN512->anticollision.iso_a.valid_bits >> 3] &= 0xff >> ((8 - pPN512->anticollision.iso_a.valid_bits) & 0x7);

    // Now remember bits before collision
    for (size_t p = 0; p < 5; p++) {
        pPN512->anticollision.iso_a.cln[p] |= bufIn[p];
    }

    // If we have all bits, then check BCC, go to next step
    if (pPN512->anticollision.iso_a.valid_bits < 5 * 8) { // Collision, add a 1 at the end of known bits to resolve collision
        pPN512->anticollision.iso_a.cln[pPN512->anticollision.iso_a.valid_bits >> 3] |= (1 << (pPN512->anticollision.iso_a.valid_bits & 0x7));
        pPN512->anticollision.iso_a.valid_bits++;

        // Restart first step with more valid bits
        pn512_initiator_isoa_anticollision_cascade_1(pPN512);
        return;
    }

    //Check BCC if all bits are valid
    if (pPN512->anticollision.iso_a.cln[4] != (pPN512->anticollision.iso_a.cln[0] ^ pPN512->anticollision.iso_a.cln[1] ^ pPN512->anticollision.iso_a.cln[2] ^ pPN512->anticollision.iso_a.cln[3])) {
        NFC_WARN("Wrong BCC %02X != %02X", bufIn[4], bufIn[0] ^ bufIn[1] ^ bufIn[2] ^ bufIn[3]);
        pn512_anticollision_callback(pPN512, NFC_ERR_COLLISION);
        return; //TODO handle this properly
    }

    if (pPN512->anticollision.iso_a.cln[0] == CT) {
        //Not the last cascade level
        if (pPN512->anticollision.iso_a.cascade_level == 3) { // not allowed
            NFC_WARN("Cascade tag present in cascade level 3");
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }
        memcpy(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uid[(pPN512->anticollision.iso_a.cascade_level - 1) * 3], &pPN512->anticollision.iso_a.cln[1], 3);
        pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength += 3;
    } else {
        //Last cascade level
        memcpy(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uid[(pPN512->anticollision.iso_a.cascade_level - 1) * 3], &pPN512->anticollision.iso_a.cln[0], 4);
        pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength += 4;
    }

    ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
    ac_buffer_builder_reset(pDataOutBldr);

    //Select and get SAK
    ac_buffer_builder_write_nu8(pDataOutBldr, SEL(pPN512->anticollision.iso_a.cascade_level - 1));  //SEL: Cascade level
    ac_buffer_builder_write_nu8(pDataOutBldr, NVB(40));  //NVB: 40 valid bits = 5 bytes

    //Transmit last 4 transmitted UID bytes + BCC (including cascade byte if relevant)
    ac_buffer_builder_write_n_bytes(pDataOutBldr, pPN512->anticollision.iso_a.cln, 5);

    NFC_DBG("Selecting target");

    //This time compute & check CRC
    pn512_set_crc((nfc_transceiver_t *)pPN512, true, true);
    pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));
    pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_cascade_3);
}

static void pn512_initiator_isoa_anticollision_cascade_3(pn512_t *pPN512, nfc_err_t ret)
{
    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    if (ret) {
        NFC_WARN("Did not receive response: error %d", ret);
        pn512_anticollision_callback(pPN512, ret);
        return;
    }

    if (ac_buffer_reader_readable(pResp) != 1) {
        NFC_WARN("Wrong length");
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak = ac_buffer_read_nu8(pResp);
    NFC_DBG("Got SAK %02X", pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak);

    //Check SAK
    if (pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x04) {
        //Continue anticollision
        pPN512->anticollision.iso_a.cascade_level++;
        pPN512->anticollision.iso_a.valid_bits = 0;
        memset(pPN512->anticollision.iso_a.cln, 0, 5);
        pn512_initiator_isoa_anticollision_cascade_1(pPN512);
    } else {
        //Anticollision complete

        NFC_DBG("Found one target- SAK = %02X", pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak);

        //Analyze SAK
        memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t));

        if (pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x40) { //NFC-IP1 compliant
            pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_a = true;
        }
        if ((pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x20)
                && pPN512->config.initiators.nfc_iso_dep_a) { //ISO-14443A-4 compliant
            pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_a = true;
        }
        if (!(pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x60)
                && pPN512->config.initiators.nfc_type2) { //Potentially NFC Type 2 (or Mifare, etc)
            pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type2 = true;
        }

        // Unknown target
        if (!pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_a
                && !pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_a
                && !pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type2
           ) {
            pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER);
            return;
        }

        // Valid target detected
        pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type;
        pPN512->transceiver.remote_targets_count++;

        if (!pPN512->config.options.bail_at_first_target
                && pPN512->anticollision.iso_a.more_targets
                && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS)) {
            // Halt target and continue with others
            ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
            ac_buffer_builder_reset(pDataOutBldr);

            //HALTA
            ac_buffer_builder_write_nu8(pDataOutBldr, HALTA1);
            ac_buffer_builder_write_nu8(pDataOutBldr, HALTA2);

            pn512_set_crc((nfc_transceiver_t *)pPN512, true, false);
            pn512_set_timeout((nfc_transceiver_t *)pPN512, 30);
            pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));
            pn512_transceive_hw(pPN512, pn512_transceive_mode_transmit, pn512_initiator_isoa_anticollision_cascade_4);
            return;
        }

        // Leave it activated and finish!
        pn512_initiator_isoa_anticollision_complete(pPN512);
    }
}

static void pn512_initiator_isoa_anticollision_cascade_4(pn512_t *pPN512, nfc_err_t ret)
{
    if (ret) {
        NFC_WARN("Could not halt device: error %d", ret);
        pn512_anticollision_callback(pPN512, ret);
        return;
    }

    // Start again
    pn512_initiator_isoa_anticollision_reqa(pPN512);
}

void pn512_initiator_isoa_anticollision_complete(pn512_t *pPN512)
{
    pn512_anticollision_callback(pPN512, NFC_OK);
}

// ISO B
static void pn512_initiator_isob_anticollision(pn512_t *pPN512, pn512_cb_t cb);
static void pn512_initiator_isob_anticollision_reqb(pn512_t *pPN512);
static void pn512_initiator_isob_anticollision_atqb(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isob_anticollision_next_slot(pn512_t *pPN512);
static void pn512_initiator_isob_anticollision_haltb_resp(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_isob_anticollision_complete(pn512_t *pPN512);

#define REQB 0x05
#define HALTB 0x50

void pn512_initiator_isob_anticollision(pn512_t *pPN512, pn512_cb_t cb)
{
    pPN512->anticollision.cb = cb;
    pPN512->anticollision.iso_b.slots_num_exponent = 0; // Start with one slot
    pPN512->anticollision.iso_b.slot_number = 0;
    pPN512->anticollision.iso_b.found_one = false;

    // Reset transceive mode
    pPN512->transceive.mode = pn512_transceive_mode_idle;

    pn512_initiator_isob_anticollision_reqb(pPN512);
}

void pn512_initiator_isob_anticollision_reqb(pn512_t *pPN512)
{
    ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
    ac_buffer_builder_reset(pDataOutBldr);

    if (pPN512->anticollision.iso_b.slot_number == 0) {
        // Send REQB/WUPB
        pPN512->anticollision.iso_b.more_targets = false;

        ac_buffer_builder_write_nu8(pDataOutBldr, REQB);
        ac_buffer_builder_write_nu8(pDataOutBldr, 0x00); // AFI: All card types should respond
        uint8_t wup = 0;
        if ((pPN512->anticollision.iso_b.slots_num_exponent == 0)) { //&& (pPN512->anticollision.iso_b.slot_number == 0))
            wup |= 0x8; // Send Wake-Up command on first iteration
        }
        ac_buffer_builder_write_nu8(pDataOutBldr, wup | (pPN512->anticollision.iso_b.slots_num_exponent & 0x7)); // Param: number of slots
    } else {
        // Just send slot marker
        ac_buffer_builder_write_nu8(pDataOutBldr, REQB | ((pPN512->anticollision.iso_b.slot_number & 0xf) << 4));
    }

    pn512_set_crc((nfc_transceiver_t *)pPN512, true, true);
    pn512_set_timeout((nfc_transceiver_t *)pPN512, 18);
    pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));
    pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isob_anticollision_atqb);
}

void pn512_initiator_isob_anticollision_atqb(pn512_t *pPN512, nfc_err_t ret)
{
    // Three cases:
    // - 1 response --> store response, halt PICC and check next slot number
    // - No response --> check next slot number
    // - Collision --> check next slot number but we will have to increment number of slots
    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    if (ret && (ret != NFC_ERR_COLLISION) && (ret != NFC_ERR_WRONG_COMM) && (ret != NFC_ERR_TIMEOUT)) {
        NFC_WARN("Did not receive ATQB: error %u", ret);
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    // Increment slot number
    pPN512->anticollision.iso_b.slot_number++;

    if ((ret == NFC_ERR_COLLISION) || (ret == NFC_ERR_WRONG_COMM)) {
        pPN512->anticollision.iso_b.more_targets = true;
    } else if (!ret) {
        pPN512->anticollision.iso_b.found_one = true;

        // Decode ATQB
        if (ac_buffer_reader_readable(pResp) != 12) {
            NFC_WARN("Wrong length (%u bytes)", ac_buffer_reader_readable(pResp));
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }

        NFC_DBG("Got ATQB:");
        NFC_DBG_BLOCK(ac_buffer_dump(pResp);)

        // Check first byte
        uint8_t atqb0 = ac_buffer_read_nu8(pResp);
        if (atqb0 != 0x50) {
            NFC_WARN("Wrong first byte for ATQB: %02X", atqb0);
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }

        memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t));
        pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_b = true;

        // Save PUPI, application data & protocol info
        ac_buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.pupi, 4);
        ac_buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.application_data, 4);
        ac_buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.protocol_info, 3);

        // Valid target detected
        pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type;
        pPN512->transceiver.remote_targets_count++;

        if (!pPN512->config.options.bail_at_first_target
                && (pPN512->anticollision.iso_b.more_targets || (pPN512->anticollision.iso_b.slot_number < (1 << pPN512->anticollision.iso_b.slots_num_exponent)))
                && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS)) {
            // Halt target and continue with others
            ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
            ac_buffer_builder_reset(pDataOutBldr);

            // Halt PICC and move on to the next slot

            //HALTB
            ac_buffer_builder_write_nu8(pDataOutBldr, HALTB);
            ac_buffer_builder_write_n_bytes(pDataOutBldr, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count - 1].nfcB.pupi, 4);

            pn512_set_crc((nfc_transceiver_t *)pPN512, true, true);
            pn512_set_timeout((nfc_transceiver_t *)pPN512, 30);
            pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));
            pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isob_anticollision_haltb_resp);
            return;
        } else {
            pn512_initiator_isob_anticollision_complete(pPN512);
            return;
        }
    }

    // Move on to the next slot
    pn512_initiator_isob_anticollision_next_slot(pPN512);
}

void pn512_initiator_isob_anticollision_next_slot(pn512_t *pPN512)
{
    if (pPN512->anticollision.iso_b.slot_number >= (1 << pPN512->anticollision.iso_b.slots_num_exponent)) {
        if (!pPN512->anticollision.iso_b.more_targets) {
            // No further collisions to resolve
            pn512_initiator_isob_anticollision_complete(pPN512);
            return;
        }
        if (pPN512->anticollision.iso_b.slots_num_exponent >= 4) {
            // Cannot handle more than 16 slots
            pn512_initiator_isob_anticollision_complete(pPN512);
            return;
        }
        pPN512->anticollision.iso_b.slots_num_exponent++;
        pPN512->anticollision.iso_b.slot_number = 0;
    }
    pn512_initiator_isob_anticollision_reqb(pPN512);
}

void pn512_initiator_isob_anticollision_haltb_resp(pn512_t *pPN512, nfc_err_t ret)
{
    // Check for response
    if (ret) {
        NFC_WARN("Did not receive HALTB response: error %u", ret);
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    if (ac_buffer_reader_readable(pResp) != 1) {
        NFC_WARN("Wrong length (%u bytes)", ac_buffer_reader_readable(pResp));
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    NFC_DBG("Got HALTB response:");
    NFC_DBG_BLOCK(ac_buffer_dump(pResp);)

    // Check byte
    uint8_t haltbr = ac_buffer_read_nu8(pResp);
    if (haltbr != 0x00) {
        NFC_WARN("Wrong byte for HALTB response: %02X", haltbr);
        pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
        return;
    }

    // PICC halted, move to next slot
    pn512_initiator_isob_anticollision_next_slot(pPN512);
}

void pn512_initiator_isob_anticollision_complete(pn512_t *pPN512)
{
    if (pPN512->anticollision.iso_b.found_one) {
        pn512_anticollision_callback(pPN512, NFC_OK);
    } else {
        pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER);
    }
}

// Felica
static void pn512_initiator_felica_anticollision(pn512_t *pPN512, pn512_cb_t cb);
static void pn512_initiator_felica_anticollision_reqc(pn512_t *pPN512);
static void pn512_initiator_felica_anticollision_atqc(pn512_t *pPN512, nfc_err_t ret);
static void pn512_initiator_felica_anticollision_complete(pn512_t *pPN512);

#define REQC 0x00

void pn512_initiator_felica_anticollision(pn512_t *pPN512, pn512_cb_t cb)
{
    pPN512->anticollision.cb = cb;

    // Reset transceive mode
    pPN512->transceive.mode = pn512_transceive_mode_idle;

    pn512_initiator_felica_anticollision_reqc(pPN512);
}

void pn512_initiator_felica_anticollision_reqc(pn512_t *pPN512)
{
    ac_buffer_builder_t *pDataOutBldr = &pPN512->readBufBldr;
    ac_buffer_builder_reset(pDataOutBldr);

    ac_buffer_builder_write_nu8(pDataOutBldr, 6); //Length
    ac_buffer_builder_write_nu8(pDataOutBldr, REQC);
    ac_buffer_builder_write_nu16(pDataOutBldr, 0xFFFF); //Any system code
    ac_buffer_builder_write_nu8(pDataOutBldr, 0x00); // Padding
    ac_buffer_builder_write_nu8(pDataOutBldr, 0x07); // 8 time slots, more would overflow the rx buffer if many cards responded

    pn512_set_crc((nfc_transceiver_t *)pPN512, true, true);
    pn512_set_timeout((nfc_transceiver_t *)pPN512, 13); //8 timeslots at 212kbps
    pn512_set_write((nfc_transceiver_t *)pPN512, ac_buffer_builder_buffer(pDataOutBldr));

    pn512_framing_rx_multiple_enable(pPN512); // Set RxMultiple bit

    pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_felica_anticollision_atqc);
}

void pn512_initiator_felica_anticollision_atqc(pn512_t *pPN512, nfc_err_t ret)
{
    ac_buffer_t *pResp = pn512_get_read((nfc_transceiver_t *)pPN512);

    if (ret || (ac_buffer_reader_readable(pResp) == 0)) {
        NFC_WARN("Did not receive ATQC: error %d", ret);
        pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER);
        return;
    }

    // We might have multiple responses
    NFC_DBG("Got ATQC:");
    NFC_DBG_BLOCK(ac_buffer_dump(pResp);)

    while (ac_buffer_reader_readable(pResp) > 0) {
        if (ac_buffer_reader_readable(pResp) != 18 + 1) { // ATQC is 18 bytes, 1 byte for errors added by PN512
            NFC_WARN("Wrong length (%d bytes)", ac_buffer_reader_readable(pResp));
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }

        // First byte is length, check that it's correct
        uint8_t frame_length = ac_buffer_read_nu8(pResp);
        uint8_t atqc0 = ac_buffer_read_nu8(pResp);
        if ((frame_length != 18) || (atqc0 != 0x01)) {
            NFC_WARN("Wrong ATQC frame");
            pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL);
            return;
        }

        // Read NFCID2
        ac_buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2, 8);

        // Discard padding bytes
        ac_buffer_read_n_skip(pResp, 8);

        // Read error register
        uint8_t err_reg = ac_buffer_read_nu8(pResp);
        if (err_reg & 0x1f) {
            // Error within this time slot, skip
            continue;
        }

        //Populate tech accordingly
        memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t));
        if (
            (pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2[0] == 0x01)
            && (pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2[1] == 0xFE)
        ) {
            // NFC-DEP supported
            pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_f_212 = 1;
        } else {
            // Type 3 supported
            pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type3 = 1;
        }
        pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type;
        pPN512->transceiver.remote_targets_count++;

        // Only continue if we can have more targets
        if (!pPN512->config.options.bail_at_first_target
                && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS)) {
            continue;
        }
        break;
    }

    pn512_initiator_felica_anticollision_complete(pPN512);
}

void pn512_initiator_felica_anticollision_complete(pn512_t *pPN512)
{
    pn512_anticollision_callback(pPN512, NFC_OK);
}

void pn512_poll_setup(pn512_t *pPN512)
{
    bool target = pPN512->config.targets.nfc_type2
                  || pPN512->config.targets.nfc_type3
                  || pPN512->config.targets.nfc_iso_dep_a
                  || pPN512->config.targets.nfc_nfc_dep_a
                  || pPN512->config.targets.nfc_nfc_dep_f_212
                  || pPN512->config.targets.nfc_nfc_dep_f_424;

// No need for initiator-specific configuration at this stage, but keep this just in case
//  bool initiator = pPN512->config.initiators.nfc_type1
//      || pPN512->config.initiators.nfc_type2
//      || pPN512->config.initiators.nfc_type3
//      || pPN512->config.initiators.nfc_iso_dep_a
//      || pPN512->config.initiators.nfc_nfc_dep_a
//      || pPN512->config.initiators.nfc_nfc_dep_f_212
//      || pPN512->config.initiators.nfc_nfc_dep_f_424;

    if (target) {
        NFC_DBG("Configure anticoll/polling response");
        //Setup ATQA, SAK and Felica polling response
        pn512_fifo_clear(pPN512);

        ac_buffer_builder_t *pDataCfgBldr = &pPN512->readBufBldr;
        ac_buffer_builder_reset(pDataCfgBldr);

        //Write ATQA
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x04);
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);

        //Write NFCID1 (0s as it will be randomly generated) - first byte will be set to 0x08 by HW according to NFC-IP1
        for (int i = 0; i < 3; i++) {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        }

        //Write SAK
        uint8_t sak = 0x00; //Default SAK (UID complete)
        if (pPN512->config.targets.nfc_iso_dep_a) {
            sak |= 0x20; //This target is ISO-14443A-4 compliant
        }
        if (pPN512->config.targets.nfc_nfc_dep_a) {
            sak |= 0x40; //This target is NFC-IP1 compliant
        }
        ac_buffer_builder_write_nu8(pDataCfgBldr, sak);

        //Write NFCID2 (xx 0xfe according to NFC-IP1 and 0s as 6 bytes will be randomly generated)
        if (pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424) {
            //Byte 1 is 0x01 if supporting NFC-IP1
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x01);
        } else if (pPN512->config.targets.nfc_type3) { //NFC-IP will have priority over type 3 tag emulation
            //Byte 1 is 0x02 if supporting Type 3 tag platform
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x02);
        } else {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        }
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFE);

        for (int i = 0; i < 6; i++) {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        }

        //2 PAD0 bytes (Felica spec) - this would code the manufacturer + IC code (0xFFFF for NFC-IP1 tags)
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFF);
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFF);

        //3 PAD1 bytes
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);

        if (!(pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424)
                && pPN512->config.targets.nfc_type3) {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x01); //MRTI Check
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x01); //MRTI Update
        } else {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);
        }

        //1 PAD2 byte
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);

        //2 system code bytes
        if (!(pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424)
                && pPN512->config.targets.nfc_type3) {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0x12);
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFC);
        } else {
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFF); //Wildcard system code
            ac_buffer_builder_write_nu8(pDataCfgBldr, 0xFF);
        }

        //Write NFCID3
        ac_buffer_builder_write_nu8(pDataCfgBldr, 0x00);

        pn512_fifo_write(pPN512, ac_buffer_builder_buffer(pDataCfgBldr));

        pn512_cmd_exec(pPN512, PN512_CMD_CONFIG);
        pn512_cmd_wait_idle(pPN512, -1);

        NFC_DBG("Overwrite NFCIDs with random numbers");

        //Overwrite NFCIDs with random numbers
        pn512_cmd_exec(pPN512, PN512_CMD_RNDIDG);
        pn512_cmd_wait_idle(pPN512, -1);
    }
}

static void pn512_poll_iteration(pn512_t *pPN512, nfc_err_t ret);
static void pn512_poll_delay_complete(uint32_t events, void *pUserData)
{
    pn512_t *pPN512 = (pn512_t *) pUserData;

    if (events & EVENT_ABORTED) {
        pn512_poll_callback(pPN512, NFC_ERR_ABORTED);
        return;
    }

    pn512_poll_iteration(pPN512, NFC_OK);
}

static void pn512_poll_delay(pn512_t *pPN512, uint32_t timeout)
{
    task_init(&pPN512->transceiver.task, EVENT_TIMEOUT, timeout, pn512_poll_delay_complete, pPN512);
    nfc_scheduler_queue_task(&pPN512->transceiver.scheduler, &pPN512->transceiver.task);
}

void pn512_poll_iteration(pn512_t *pPN512, nfc_err_t ret)
{
    do {
        if (pPN512->poll.state == pn512_polling_state_start_listening) {
            NFC_DBG("pn512_polling_state_start_listening");
            // Start with listening
            if (!pn512_config_target(pPN512)) {
                // Otherwise switch to next step
                pPN512->poll.state = pn512_polling_state_start_polling;
                continue;
            }

            pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field;

            // Shortcut if target is halted (means RF field is still here)
            if (pn512_register_read(pPN512, PN512_REG_MIFNFC) & 0x04) {
                continue;
            }

            // Fix for PN512 bug that sometimes detects its own RF field
            //  if(pPN512->rf_on)
            {
                //Switch RF field off
                pn512_rf_field_switch_off(pPN512);
                NFC_DBG("RF field switched off");
            }

            NFC_DBG("Target anticollision");

            bool iso14443a = pPN512->config.targets.nfc_type2 || pPN512->config.targets.nfc_iso_dep_a || pPN512->config.targets.nfc_nfc_dep_a;
            bool felica = pPN512->config.targets.nfc_type3 || pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424;

            NFC_DBG("Switch in target mode and set framing: ISO A: %s; Felica: %s", iso14443a ? "Yes" : "No", felica ? "Yes" : "No");

            //Switch in target mode
            if (iso14443a && felica) {
                pn512_framing_set(pPN512, nfc_framing_target_mode_detector);
            } else if (iso14443a) {
                pn512_framing_set(pPN512, nfc_framing_target_a_106);
            } else if (felica) {
                pn512_framing_set(pPN512, nfc_framing_target_f_212);
            }

            pn512_rf_field_wait_for_external(pPN512, pPN512->config.options.listen_for, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_listen_wait_for_remote_field) {
            NFC_DBG("pn512_polling_state_listen_wait_for_remote_field");

            if (!pn512_config_target(pPN512)) {
                // Otherwise switch to next step
                pPN512->poll.state = pn512_polling_state_start_listening;
                continue;
            }

            if (ret == NFC_ERR_TIMEOUT) {
                // Continue polling
                pPN512->poll.state = pn512_polling_state_start_polling;
                continue;
            }

            if (ret) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            pPN512->poll.state = pn512_polling_state_listen_anticollision;

            pn512_target_anticollision(pPN512, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_listen_anticollision) {
            NFC_DBG("pn512_polling_state_listen_anticollision");

            if (ret == NFC_ERR_FIELD) {
                // This means that a remote field was dropped - give it another chance
                pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field;
                pn512_rf_field_switch_off(pPN512);
                pn512_rf_field_wait_for_external(pPN512, pPN512->config.options.listen_for, pn512_poll_iteration);
                return;
            }

            if (ret) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            // Be safe, not sure what the framing is
            pPN512->framing = nfc_framing_unknown;

            pn512_poll_callback(pPN512, NFC_OK);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_start_polling) {
            NFC_DBG("pn512_polling_state_start_polling");

            if (!pn512_config_initiator(pPN512)) {
                // Otherwise switch to next step
                pPN512->poll.state = pn512_polling_state_start_listening;
                continue;
            }

            // Try to activate RF field
            pPN512->poll.state = pn512_polling_state_rf_collision_avoidance;

            pn512_rf_field_nfcip1_rf_collision_avoidance(pPN512, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_rf_collision_avoidance) {
            NFC_DBG("pn512_polling_state_rf_collision_avoidance");

            if (ret) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            NFC_DBG("Own RF field is %s", pPN512->rf_on ? "on" : "off");

            if (!pPN512->rf_on) {
                // Go back to listening, target framing is still valid so no need to reset it
                pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field;
                continue;
            }

            pPN512->poll.state = pn512_polling_state_polling_nfc_a_start;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_a_start) {
            NFC_DBG("pn512_polling_state_polling_nfc_a_start");

            //Check if ISO A is needed
            bool nfc_a = pPN512->config.initiators.nfc_type2 || pPN512->config.initiators.nfc_iso_dep_a || pPN512->config.initiators.nfc_nfc_dep_a; //We do not support type 1 card emulation so irrelevant
            if (!nfc_a) {
                // Continue with NFC B
                pPN512->poll.state = pn512_polling_state_polling_nfc_b_start;
                continue;
            }

            pPN512->transceiver.initiator_ntarget = true;
            pn512_framing_set(pPN512, nfc_framing_initiator_a_106);

            pPN512->poll.state = pn512_polling_state_polling_nfc_a_gt;
            pn512_poll_delay(pPN512, 10 + 5); // Guard time for ISO A is 5 ms
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_a_gt) {
            NFC_DBG("pn512_polling_state_polling_nfc_a_gt");

            // Start anticollision
            pPN512->poll.state = pn512_polling_state_polling_nfc_a_anticollision;
            pn512_initiator_isoa_anticollision(pPN512, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_a_anticollision) {
            NFC_DBG("pn512_polling_state_polling_nfc_a_anticollision");

            if (ret == NFC_ERR_NOPEER) {
                NFC_DBG("Not found");
                // Continue with NFC B
                pPN512->poll.state = pn512_polling_state_polling_nfc_b_start;
                continue;
            }

            if ((ret == NFC_OK) && (pPN512->config.options.bail_at_first_tech || (pPN512->transceiver.remote_targets_count == MUNFC_MAX_REMOTE_TARGETS))) {
                // At least one target found, exit polling loop
                pn512_poll_callback(pPN512, NFC_OK);
                return;
            }

            if (ret == NFC_ERR_ABORTED) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            // Continue with NFC B
            pPN512->poll.state = pn512_polling_state_polling_nfc_b_start;
            continue;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_b_start) {
            NFC_DBG("pn512_polling_state_polling_nfc_b_start");

            //Check if ISO B is needed
            bool nfc_b = pPN512->config.initiators.nfc_iso_dep_b;
            if (!nfc_b) {
                // Continue with NFC F
                pPN512->poll.state = pn512_polling_state_polling_nfc_f_start;
                continue;
            }

            pPN512->transceiver.initiator_ntarget = true;
            pn512_framing_set(pPN512, nfc_framing_initiator_b_106);

            pPN512->poll.state = pn512_polling_state_polling_nfc_b_gt;
            pn512_poll_delay(pPN512, 10 + 5); // Guard time for ISO B is 5 ms
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_b_gt) {
            NFC_DBG("pn512_polling_state_polling_nfc_b_gt");

            // Start anticollision
            pPN512->poll.state = pn512_polling_state_polling_nfc_b_anticollision;
            pn512_initiator_isob_anticollision(pPN512, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_b_anticollision) {
            NFC_DBG("pn512_polling_state_polling_nfc_b_anticollision");

            if (ret == NFC_ERR_NOPEER) {
                NFC_DBG("Not found");
                // Continue with NFC F
                pPN512->poll.state = pn512_polling_state_polling_nfc_f_start;
                continue;
            }

            if ((ret == NFC_OK)
                    && (pPN512->config.options.bail_at_first_tech
                        || (pPN512->transceiver.remote_targets_count
                            == MUNFC_MAX_REMOTE_TARGETS))) {
                // At least one target found, exit polling loop
                pn512_poll_callback(pPN512, NFC_OK);
                return;
            }

            if (ret == NFC_ERR_ABORTED) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            // Continue with NFC F
            pPN512->poll.state = pn512_polling_state_polling_nfc_f_start;
            continue;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_f_start) {
            NFC_DBG("pn512_polling_state_polling_nfc_f_start");

            //Check if Felica is needed
            bool nfc_f = pPN512->config.initiators.nfc_type3 || pPN512->config.initiators.nfc_nfc_dep_f_212;
            if (!nfc_f) {
                // Wrap up
                pPN512->poll.state = pn512_polling_state_finish_polling;
                continue;
            }

            pPN512->transceiver.initiator_ntarget = true;
            pn512_framing_set(pPN512, nfc_framing_initiator_f_212);

            pPN512->poll.state = pn512_polling_state_polling_nfc_f_gt;
            pn512_poll_delay(pPN512, 20); // Guard time for Felica is 20 ms
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_f_gt) {
            NFC_DBG("pn512_polling_state_polling_nfc_f_gt");

            // Start anticollision
            pPN512->poll.state = pn512_polling_state_polling_nfc_f_anticollision;
            pn512_initiator_felica_anticollision(pPN512, pn512_poll_iteration);
            return;
        }

        if (pPN512->poll.state == pn512_polling_state_polling_nfc_f_anticollision) {
            NFC_DBG("pn512_polling_state_polling_nfc_f_anticollision");

            if (ret == NFC_ERR_NOPEER) {
                NFC_DBG("Not found");
                // Resolve polling
                pPN512->poll.state = pn512_polling_state_finish_polling;
                continue;
            }

            if ((ret == NFC_OK) && (pPN512->config.options.bail_at_first_tech || (pPN512->transceiver.remote_targets_count == MUNFC_MAX_REMOTE_TARGETS))) {
                // At least one target found, exit polling loop
                pn512_poll_callback(pPN512, NFC_OK);
                return;
            }

            if (ret == NFC_ERR_ABORTED) {
                pn512_poll_callback(pPN512, ret);
                return;
            }

            // Resolve polling
            pPN512->poll.state = pn512_polling_state_finish_polling;
            continue;
        }

        if (pPN512->poll.state == pn512_polling_state_finish_polling) {
            if (pPN512->transceiver.remote_targets_count > 0) {
                pn512_poll_callback(pPN512, NFC_OK);
            } else {
                pn512_poll_callback(pPN512, NFC_ERR_NOPEER);
            }
            return;
        }

    } while (true);
}

// Main polling loop function
void pn512_poll_hw(pn512_t *pPN512, pn512_cb_t cb)
{
    nfc_transceiver_t *pTransceiver = (nfc_transceiver_t *)pPN512;
    pPN512->poll.cb = cb;

    //Reset active state
    pTransceiver->initiator_ntarget = false;
    memset(&pTransceiver->active_tech, 0, sizeof(nfc_tech_t));

    //Reset discovered targets
    pTransceiver->remote_targets_count = 0;

    //Initialize state machine
    pPN512->poll.state = pn512_polling_state_start_listening;

    if (!pn512_config_target(pPN512) && !pn512_config_initiator(pPN512)) {
        pn512_poll_callback(pPN512, NFC_ERR_PARAMS);
        return;
    }

    //First iteration
    pn512_poll_iteration(pPN512, NFC_OK);
}
