//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
#include "felica.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "util.h"
#include "protocols.h"
#include "crc16.h"
#include "fpgaloader.h"
#include "string.h"
#include "commonutil.h"
#include "dbprint.h"
#include "ticks.h"
#include "iso18.h"

// FeliCa timings
// minimum time between the start bits of consecutive transfers from reader to tag: 6800 carrier (13.56MHz) cycles
#ifndef FELICA_REQUEST_GUARD_TIME
//# define FELICA_REQUEST_GUARD_TIME (6800 / 16 + 1) // 426
# define FELICA_REQUEST_GUARD_TIME ((512 + 0 * 256) * 64 / 16 + 1)
#endif
// FRAME DELAY TIME 2672 carrier cycles
#ifndef FELICA_FRAME_DELAY_TIME
# define FELICA_FRAME_DELAY_TIME (2672/16 + 1) // 168
#endif
#ifndef DELAY_AIR2ARM_AS_READER
#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) // 91
#endif
#ifndef DELAY_ARM2AIR_AS_READER
#define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) // 209
#endif
#define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len))

static uint32_t felica_timeout;
static uint32_t felica_nexttransfertime;
static uint32_t felica_lasttime_prox2air_start;

static void iso18092_setup(uint8_t fpga_minor_mode);
static uint8_t felica_select_card(felica_card_select_t *card);
static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed);
static bool WaitForFelicaReply(uint16_t maxbytes);

static void iso18092_set_timeout(uint32_t timeout) {
    felica_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / (16 * 8) + 2;
}

static uint32_t iso18092_get_timeout(void) {
    return felica_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / (16 * 8) - 2;
}

#ifndef FELICA_MAX_FRAME_SIZE
#define FELICA_MAX_FRAME_SIZE 260
#endif






//structure to hold outgoing NFC frame
static uint8_t frameSpace[FELICA_MAX_FRAME_SIZE + 4];

//structure to hold incoming NFC frame, used for ISO/IEC 18092-compatible frames
static struct {
    enum {
        STATE_UNSYNCD,
        STATE_TRYING_SYNC,
        STATE_GET_LENGTH,
        STATE_GET_DATA,
        STATE_GET_CRC,
        STATE_FULL
    } state;

    uint16_t  shiftReg; //for synchronization and offset calculation
    int       posCnt;
    bool      crc_ok;
    int       rem_len;
    uint16_t  len;
    uint8_t   byte_offset;
    uint8_t   *framebytes;
//should be enough. maxlen is 255, 254 for data, 2 for sync, 2 for crc
// 0,1 -> SYNC, 2 - len,  3-(len+1)->data, then crc
} FelicaFrame;

//b2 4d is SYNC, 45645 in 16-bit notation, 10110010 01001101 binary. Frame will not start filling until this is shifted in
//bit order in byte -reverse, I guess?  [((bt>>0)&1),((bt>>1)&1),((bt>>2)&1),((bt>>3)&1),((bt>>4)&1),((bt>>5)&1),((bt>>6)&1),((bt>>7)&1)] -at least in the mode that I read those in
#ifndef SYNC_16BIT
# define SYNC_16BIT 0xB24D
#endif

static void FelicaFrameReset(void) {
    FelicaFrame.state = STATE_UNSYNCD;
    FelicaFrame.posCnt = 0;
    FelicaFrame.crc_ok = false;
    FelicaFrame.byte_offset = 0;
}
static void FelicaFrameinit(uint8_t *data) {
    FelicaFrame.framebytes = data;
    FelicaFrameReset();
}

//shift byte into frame, reversing it at the same time
static void shiftInByte(uint8_t bt) {
    uint8_t j;
    for (j = 0; j < FelicaFrame.byte_offset; j++) {
        FelicaFrame.framebytes[FelicaFrame.posCnt] = (FelicaFrame.framebytes[FelicaFrame.posCnt] << 1) + (bt & 1);
        bt >>= 1;
    }
    FelicaFrame.posCnt++;
    FelicaFrame.rem_len--;
    for (j = FelicaFrame.byte_offset; j < 8; j++) {
        FelicaFrame.framebytes[FelicaFrame.posCnt] = (FelicaFrame.framebytes[FelicaFrame.posCnt] << 1) + (bt & 1);
        bt >>= 1;
    }
}

static void Process18092Byte(uint8_t bt) {

    switch (FelicaFrame.state) {

        case STATE_UNSYNCD: {
            // almost any nonzero byte can be start of SYNC. SYNC should be preceded by zeros, but that is not always the case
            if (bt > 0) {
                FelicaFrame.shiftReg = reflect8(bt);
                FelicaFrame.state = STATE_TRYING_SYNC;
            }
            break;
        }

        case STATE_TRYING_SYNC: {

            if (bt == 0) {
                // desync
                FelicaFrame.shiftReg = bt;
                FelicaFrame.state = STATE_UNSYNCD;
            } else {

                for (uint8_t i = 0; i < 8; i++) {

                    if (FelicaFrame.shiftReg == SYNC_16BIT) {
                        // SYNC done!
                        FelicaFrame.state = STATE_GET_LENGTH;
                        FelicaFrame.framebytes[0] = 0xb2;
                        FelicaFrame.framebytes[1] = 0x4d;
                        FelicaFrame.byte_offset = i;

                        // shift in remaining byte, slowly...
                        for (uint8_t j = i; j < 8; j++) {
                            FelicaFrame.framebytes[2] = (FelicaFrame.framebytes[2] << 1) + (bt & 1);
                            bt >>= 1;
                        }

                        FelicaFrame.posCnt = 2;
                        if (i == 0) {
                            break;
                        }
                    }
                    FelicaFrame.shiftReg = (FelicaFrame.shiftReg << 1) + (bt & 1);
                    bt >>= 1;
                }

                //that byte was last byte of sync
                if (FelicaFrame.shiftReg == SYNC_16BIT) {
                    //Force SYNC on next byte
                    FelicaFrame.state = STATE_GET_LENGTH;
                    FelicaFrame.framebytes[0] = 0xb2;
                    FelicaFrame.framebytes[1] = 0x4d;
                    FelicaFrame.byte_offset = 0;
                    FelicaFrame.posCnt = 1;
                }
            }
            break;
        }
        case STATE_GET_LENGTH: {
            shiftInByte(bt);
            FelicaFrame.rem_len = FelicaFrame.framebytes[2] - 1;
            FelicaFrame.len = FelicaFrame.framebytes[2] + 4; //with crc and sync
            FelicaFrame.state = STATE_GET_DATA;
            break;
        }
        case STATE_GET_DATA: {
            shiftInByte(bt);
            if (FelicaFrame.rem_len <= 0) {
                FelicaFrame.state = STATE_GET_CRC;
                FelicaFrame.rem_len = 2;
            }
            break;
        }
        case STATE_GET_CRC: {
            shiftInByte(bt);
            if (FelicaFrame.rem_len <= 0) {
                FelicaFrame.rem_len = 0;
                // skip sync 2bytes. IF ok, residue should be 0x0000
                FelicaFrame.crc_ok = check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2);
                FelicaFrame.state = STATE_FULL;
            }
            break;
        }
        case STATE_FULL:  //ignore byte. Don't forget to clear frame to receive next one...
        default:
            break;
    }
}

/* Perform FeliCa polling card
 * Currently does NOT do any collision handling.
 * It expects 0-1 cards in the device's range.
 * return 0 if selection was successful
 */
static uint8_t felica_select_card(felica_card_select_t *card) {

    // POLL command
    // 0xB2 0x4B = sync code
    // 0x06 = len
    // 0x00 = rfu
    // 0xff = system code service
    // 0xff = system code service
    // 0x00  = request code
    // b7    = automatic switching of data rate
    // b6-b2 = reserved
    // b1    = fc/32 (414kbps)
    // b0    = fc/64 (212kbps)
    // 0x00 = timeslot
    // 0x09 0x21 = crc
    static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21};


    // We try 10 times, or if answer was received.
    int len = 25;
    do {
        // end-of-reception response packet data, wait approx. 501μs
        // end-of-transmission command packet data, wait approx. 197μs
        // polling card
        TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);

        // polling card, break if success
        if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) {
            break;
        }

        WDT_HIT();

    } while (--len);

    // 1. timed-out
    if (len == 0) {
        return 1;
    }

    // 2. wrong answer
    if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) {
        return 2;
    }

    // 3. wrong crc.  residue is 0, hence if crc is a value it failed.
    if (check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2) == false) {

        if (g_dbglevel >= DBG_DEBUG) {
            Dbprintf("Error: CRC check failed!");
            Dbhexdump(FelicaFrame.len - 2, FelicaFrame.framebytes + 2, 0);
        }
        return 3;
    }

    // copy UID
    // idm 8
    if (card) {
        memcpy(card->IDm, FelicaFrame.framebytes + 4,     8);
        memcpy(card->PMm, FelicaFrame.framebytes + 4 + 8, 8);
        // memcpy(card->servicecode, FelicaFrame.framebytes + 4 + 8 + 8, 2);
        memcpy(card->code,   card->IDm,     2);
        memcpy(card->uid,    card->IDm + 2, 6);
        memcpy(card->iccode, card->PMm,     2);
        memcpy(card->mrt,    card->PMm + 2, 6);
        if (g_dbglevel >= DBG_DEBUG) {
            Dbprintf("Received Frame: ");
            Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
        }
    }
    // 0. OK
    return 0;
}

// poll-0: 0xb2,0x4d,0x06,0x00,0xff,0xff,0x00,0x00,0x09,0x21,
// resp:  0xb2,0x4d,0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80,0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f,
// poll-1 (reply with available system codes - NFC Tag3 specs, IIRC): 0xb2,0x4d,0x06,0x00,0xff,0xff,0x01,0x00,0x3a,0x10
// resp: 0xb2,0x4d,0x14,0x01,  0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,  0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,  0x88,0xb4,0x0c,0xe2,
// page-req:  0xb2,0x4d,0x10,0x06,  0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,  0x01,  0x0b,0x00,  0x01,  0x80,0x00,  0x2e,0xb3,
// page-req: 0x06, IDm(8), ServiceNum(1),Slist(2*num) BLocknum (1) BLockids(2-3*num)
// page-resp: 0xb2,0x4d,0x1d,0x07,  0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,  0x00,  0x00,  0x01,  0x10,0x04,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x23,   0xcb,0x6e,

// builds a readblock frame for felica lite(s).  Using SERVICE:  SERVICE_FELICA_LITE_READONLY
// Felica standard has a different file system, AFAIK,
// 8-byte IDm, number of blocks, blocks numbers
// number of blocks limited to 4 for FelicaLite(S)
static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t *blocks) {

    if (blocknum > 4 || blocknum == 0) {
        Dbprintf("Invalid number of blocks, %d != 4", blocknum);
    }

    uint8_t c = 0, i = 0;

    // Sync bytes
    frameSpace[c++] = 0xb2;
    frameSpace[c++] = 0x4d;

    c++; // set length later

    frameSpace[c++] = FELICA_RDBLK_REQ; // command number

    // card IDm, from poll
    frameSpace[c++] = idm[0];
    frameSpace[c++] = idm[1];
    frameSpace[c++] = idm[2];
    frameSpace[c++] = idm[3];
    frameSpace[c++] = idm[4];
    frameSpace[c++] = idm[5];
    frameSpace[c++] = idm[6];
    frameSpace[c++] = idm[7];

    // number of services
    frameSpace[c++] = 0x01;

    // service code
    frameSpace[c++] = (SERVICE_FELICA_LITE_READONLY >> 8);
    frameSpace[c++] = SERVICE_FELICA_LITE_READONLY & 0xFF;

    // number of blocks
    frameSpace[c++] = blocknum;

    for (i = 0; i < blocknum; i++) {

        // 3-byte block
        if (blocks[i] >= 256) {
            frameSpace[c++] = 0x00;
            frameSpace[c++] = (blocks[i] >> 8); // block number, little endian....
            frameSpace[c++] = (blocks[i] & 0xff);
        } else {
            frameSpace[c++] = 0x80;
            frameSpace[c++] = blocks[i];
        }
    }

    // set length
    frameSpace[2] = c - 2;
    // Add CRC
    AddCrc(frameSpace + 2, c - 2);
}

static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed) {

    if (NYI_timing_NYI != NULL) {
        DbpString("Error: TransmitFor18092_AsReader does not check or set parameter NYI_timing_NYI");
        return;
    }

    uint16_t flags = FPGA_MAJOR_MODE_HF_ISO18092;

    if (power) {
        flags |= FPGA_HF_ISO18092_FLAG_READER;
    }

    if (highspeed) {
        flags |= FPGA_HF_ISO18092_FLAG_424K;
    }

    FpgaWriteConfWord(flags);

    uint32_t curr_transfer_time = ((MAX(felica_nexttransfertime, GetCountSspClk()) & 0xfffffff8) + 8);

    while (GetCountSspClk() < curr_transfer_time) {};

    felica_lasttime_prox2air_start = curr_transfer_time;

    // preamble
    // sending 0x00 0x00 0x00 0x00 0x00 0x00
    uint16_t c = 0;
    while (c < 6) {
        // keep tx buffer in a defined state anyway.
        if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
            AT91C_BASE_SSC->SSC_THR = 0x00;
            c++;
        }
    }
    // sending data with sync bytes
    c = 0;

    while (c < len) {
        // Put byte into tx holding register as soon as it is ready
        if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
            AT91C_BASE_SSC->SSC_THR = frame[c++];
        }
    }

    /**/
    while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {};
    AT91C_BASE_SSC->SSC_THR = 0x00; //minimum delay

    while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {};
    AT91C_BASE_SSC->SSC_THR = 0x00; //spin
    /**/

    // log
    LogTrace(
        frame,
        len,
        (felica_lasttime_prox2air_start << 4) + DELAY_ARM2AIR_AS_READER,
        ((felica_lasttime_prox2air_start + felica_lasttime_prox2air_start) << 4) + DELAY_ARM2AIR_AS_READER,
        NULL,
        true
    );

    felica_nexttransfertime = MAX(felica_nexttransfertime, felica_lasttime_prox2air_start + FELICA_REQUEST_GUARD_TIME);
}

// Wait for tag reply
// stop when button is pressed
// or return TRUE when command is captured
bool WaitForFelicaReply(uint16_t maxbytes) {

//    if (g_dbglevel >= DBG_DEBUG) { Dbprintf("WaitForFelicaReply Start"); }

    uint32_t c = 0;

    // power, no modulation
    FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
    FelicaFrameReset();

    // clear RXRDY:
    uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
    (void)b;

    uint32_t timeout = iso18092_get_timeout();

    for (;;) {

        WDT_HIT();

        if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {

            b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);

            Process18092Byte(b);

            if (FelicaFrame.state == STATE_FULL) {

                felica_nexttransfertime = MAX(
                                              felica_nexttransfertime,
                                              (GetCountSspClk() & 0xfffffff8) - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FELICA_FRAME_DELAY_TIME);

                LogTrace(
                    FelicaFrame.framebytes,
                    FelicaFrame.len,
                    ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER - timeout,
                    ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER,
                    NULL,
                    false
                );
                return true;

            } else if (c++ > timeout && (FelicaFrame.state == STATE_UNSYNCD || FelicaFrame.state == STATE_TRYING_SYNC)) {

//                if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Timeout! STATE_UNSYNCD");

                return false;
            }
        }
    }
}

// Set up FeliCa communication (similar to iso14443a_setup)
// field is setup for "Sending as Reader"
static void iso18092_setup(uint8_t fpga_minor_mode) {

    LEDsoff();
#if defined XC3
    FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
#else
    FpgaDownloadAndGo(FPGA_BITSTREAM_HF_FELICA);
#endif
    // allocate command receive buffer
    BigBuf_free();
    BigBuf_Clear_ext(false);

    // Initialize Demod and Uart structs
    // DemodInit(BigBuf_malloc(MAX_FRAME_SIZE));
    FelicaFrameinit(BigBuf_calloc(FELICA_MAX_FRAME_SIZE));

    felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER;  // 418
    // iso18092_set_timeout(2120); // 106 * 20ms  maximum start-up time of card
    iso18092_set_timeout(1060); // 106 * 10ms  maximum start-up time of card

    init_table(CRC_FELICA);

    // connect Demodulated Signal to ADC:
    SetAdcMuxFor(GPIO_MUXSEL_HIPKD);

    // Set up the synchronous serial port
    FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO18092);

    // LSB transfer.  Remember to set it back to MSB with
    AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);

    // Signal field is on with the appropriate LED
    FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | fpga_minor_mode);

    //20.4 ms generate field,  start sending polling command afterwars.
    SpinDelay(100);

    // Start the timer
    StartCountSspClk();

    LED_D_ON();
}

static void felica_reset_frame_mode(void) {
    switch_off();
    //Resetting Frame mode (First set in fpgaloader.c)
    AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
}


//-----------------------------------------------------------------------------
// RAW FeliCa commands. Send out commands and store answers.
//-----------------------------------------------------------------------------
// arg0 FeliCa flags
// arg1 len of commandbytes
// d.asBytes command bytes to send
void felica_sendraw(const PacketCommandNG *c) {

    felica_command_t param = c->oldarg[0];
    size_t len = c->oldarg[1] & 0xffff;
    uint32_t arg0;

    if ((param & FELICA_CONNECT) == FELICA_CONNECT) {
        clear_trace();
    }
    set_tracing(true);

    iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);

    if ((param & FELICA_CONNECT) == FELICA_CONNECT) {

        // notify client selecting status.
        // if failed selecting, turn off antenna and quite.
        if ((param & FELICA_NO_SELECT) != FELICA_NO_SELECT) {

            felica_card_select_t card;
            arg0 = felica_select_card(&card);
            reply_mix(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t));
            if (arg0) {
                felica_reset_frame_mode();
                return;
            }
        }

    }

    if ((param & FELICA_RAW) == FELICA_RAW) {

        // 2 sync, 1 len, 2crc == 5
        uint8_t *buf = BigBuf_calloc(len + 5);
        // add sync bits
        buf[0] = 0xb2;
        buf[1] = 0x4d;
        buf[2] = len;

        // copy command
        memcpy(buf + 2, c->data.asBytes, len);

        if ((param & FELICA_APPEND_CRC) == FELICA_APPEND_CRC) {
            // Don't append crc on empty bytearray...
            if (len > 0) {
                AddCrc(buf + 2, len);
            }
        }

        if (g_dbglevel >= DBG_DEBUG) {
            Dbprintf("Transmit Frame (no CRC shown):");
            Dbhexdump(len, buf, 0);
            Dbprintf("Buffer Length: %i", buf[2] + 4);
        };

        TransmitFor18092_AsReader(buf, buf[2] + 4, NULL, 1, 0);
        arg0 = WaitForFelicaReply(1024);

        if (g_dbglevel >= DBG_DEBUG) {
            Dbprintf("Received Frame Code: %d", arg0);
            Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
        };

        uint32_t result = reply_mix(CMD_ACK, FelicaFrame.len, arg0, 0, FelicaFrame.framebytes, FelicaFrame.len);
        if (result) {
            Dbprintf("Reply to Client Error Code: %i", result);
        }
    }

    if ((param & FELICA_NO_DISCONNECT) == FELICA_NO_DISCONNECT) {
        return;
    }

    felica_reset_frame_mode();
    return;
}

void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) {

    clear_trace();
    set_tracing(true);
    iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD);

    LED_D_ON();

    int retval = PM3_SUCCESS;
    int remFrames = (samplesToSkip) ? samplesToSkip : 0;
    int trigger_cnt = 0;
    uint32_t timeout = iso18092_get_timeout();
    bool isReaderFrame;

    uint8_t flip = 0;
    uint16_t checker = 0;
    for (;;) {

        WDT_HIT();

        // since simulation is a tight time critical loop,
        // we only check for user request to end at iteration 3000, 9000.
        if (flip == 3) {
            if (data_available()) {
                retval = PM3_EOPABORTED;
                break;
            }
            flip = 0;
        }

        if (checker >= 3000) {

            if (BUTTON_PRESS())  {
                retval = PM3_EOPABORTED;
                break;
            }
            flip++;
            checker = 0;
        }
        ++checker;

        if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {

            uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
            Process18092Byte(dist);

            if ((dist >= 178) && (++trigger_cnt > triggersToSkip)) {
                Dbprintf("triggers To skip kicked %d", dist);
                break;
            }
            if (FelicaFrame.state == STATE_FULL) {
                if ((FelicaFrame.framebytes[3] % 2) == 0) {
                    isReaderFrame = true; // All Reader Frames are even and all Tag frames are odd
                } else {
                    isReaderFrame = false;
                }
                remFrames--;
                if (remFrames <= 0) {
                    Dbprintf("Stop Sniffing - samples To skip reached!");
                    break;
                }
                LogTrace(FelicaFrame.framebytes,
                         FelicaFrame.len,
                         ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER - timeout,
                         ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER,
                         NULL,
                         isReaderFrame
                        );
                FelicaFrameReset();
            }
        }
    }
    switch_off();
    //reset framing
    AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);

    Dbprintf("Felica sniffing done, tracelen: %i", BigBuf_get_traceLen());
    reply_ng(CMD_HF_FELICA_SNIFF, retval, NULL, 0);
    LED_D_OFF();
}

#define R_POLL0_LEN    0x16
#define R_POLL1_LEN    0x18
#define R_READBLK_LEN  0x21
//simulate NFC Tag3 card - for now only poll response works
// second half (4 bytes)  of NDEF2 goes into nfcid2_0, first into nfcid2_1
void felica_sim_lite(const uint8_t *uid) {

    // prepare our 3 responses...
    uint8_t resp_poll0[R_POLL0_LEN] = { 0xb2, 0x4d, 0x12, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f};
    uint8_t resp_poll1[R_POLL1_LEN] = { 0xb2, 0x4d, 0x14, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x88, 0xb4, 0xb3, 0x7f};
    uint8_t resp_readblk[R_READBLK_LEN] = { 0xb2, 0x4d, 0x1d, FELICA_RDBLK_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x04, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x23, 0xcb, 0x6e};

    // NFC tag 3/ ISo technically. Many overlapping standards
    DbpString("Felica Lite-S simulation start");
    Dbprintf("NDEF2 UID: %02x %02x %02x %02x %02x %02x %02x %02x",
             uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]
            );

    // fill in blanks
    for (uint8_t i = 0; i < 8; i++) {
        resp_poll0[i + 4] = uid[i];
        resp_poll1[i + 4] = uid[i];
        resp_readblk[i + 4] = uid[i];
    }

    // calculate and set CRC
    AddCrc(&resp_poll0[2], resp_poll0[2]);
    AddCrc(&resp_poll1[2], resp_poll1[2]);
    AddCrc(&resp_readblk[2], resp_readblk[2]);

    iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD);

    int retval = PM3_SUCCESS;
    int curlen = 0;
    const uint8_t *curresp = NULL;
    bool listenmode = true;
    // uint32_t frtm = GetCountSspClk();

    uint8_t flip = 0;
    uint16_t checker = 0;
    for (;;) {

        WDT_HIT();

        // since simulation is a tight time critical loop,
        // we only check for user request to end at iteration 3000, 9000.
        if (flip == 3) {
            if (data_available()) {
                retval = PM3_EOPABORTED;
                break;
            }
            flip = 0;
        }

        if (checker >= 3000) {

            if (BUTTON_PRESS())  {
                retval = PM3_EOPABORTED;
                break;
            }
            flip++;
            checker = 0;
        }
        ++checker;

        WDT_HIT();

        if (listenmode) {
            // waiting for request...
            if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {

                uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
                // frtm = GetCountSspClk();
                Process18092Byte(dist);

                if (FelicaFrame.state == STATE_FULL) {

                    if (FelicaFrame.crc_ok) {

                        if (FelicaFrame.framebytes[2] == 6 && FelicaFrame.framebytes[3] == 0) {
                            static uint8_t timeslot = 0;

                            // polling... there are two types of polling we answer to
                            if (FelicaFrame.framebytes[6] == 0) {
                                curresp = resp_poll0;
                                curlen = R_POLL0_LEN;
                                listenmode = false;
                            }
                            if (FelicaFrame.framebytes[6] == 1) {
                                curresp = resp_poll1;
                                curlen = R_POLL1_LEN;
                                listenmode = false;
                            }
                            if (timeslot > FelicaFrame.framebytes[7]) {
                                // framebytes[7] contains the maximum time slot in which we are allowed to respond (#0..#15)
                                timeslot = 0;
                            }
                            // first time slot (#0) starts after 512 * 64 / fc, slot length equals 256 * 64 / fc
                            felica_nexttransfertime = GetCountSspClk() - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + (512 + timeslot * 256) * 64 / 16 + 1;
                            timeslot++; // we should use a random time slot, but responding in incremental slots should do just fine for now
                        }

                        if (FelicaFrame.framebytes[2] > 5 && FelicaFrame.framebytes[3] == 0x06) {
                            // we should rebuild it depending on page size, but...
                            // Let's see first
                            curresp = resp_readblk;
                            curlen = R_READBLK_LEN;
                            listenmode = false;
                        }
                        // clear frame
                        FelicaFrameReset();
                    } else {
                        // frame invalid, clear it out to allow for the next one
                        FelicaFrameReset();
                    }
                }
            }
        }


        if (listenmode == false) {
            // trying to answer... here to  start answering immediately.
            // this one is a bit finicky. Seems that being a bit late is better than earlier
            // TransmitFor18092_AsReader(curresp, curlen, frtm+512, 0, 0);
            TransmitFor18092_AsReader(curresp, curlen, NULL, 0, 0);

            // switch back
            FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD);

            FelicaFrameReset();
            listenmode = true;
            curlen = 0;
            curresp = NULL;
        }
    }

    switch_off();

    // reset framing
    AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);

    Dbprintf("FeliCa Lite-S emulator stopped. Trace length: %d ", BigBuf_get_traceLen());
    reply_ng(CMD_HF_FELICALITE_SIMULATE, retval, NULL, 0);
}

#define RES_SVC_LEN 11 + 3

void felica_dump_lite_s(void) {
    uint8_t ndef[8];
    uint8_t poll[10] = { 0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xff, 0xff, 0x00, 0x00, 0x09, 0x21};
    uint16_t liteblks[28] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x90, 0x91, 0x92, 0xa0};

    // setup device.
    iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);

    uint8_t blknum;
    bool isOK = false;
    uint16_t cnt = 0, cntfails = 0;
    uint8_t *dest = BigBuf_get_addr();

    while ((BUTTON_PRESS() == false) && (data_available() == false)) {
        WDT_HIT();
        // polling?
        //TransmitFor18092_AsReader(poll, 10, GetCountSspClk()+512, 1, 0);
        TransmitFor18092_AsReader(poll, 10, NULL, 1, 0);

        if (WaitForFelicaReply(512) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) {
            // copy 8bytes to ndef.
            memcpy(ndef, FelicaFrame.framebytes + 4, 8);
            // for (c=0; c < 8; c++)
            // ndef[c] = FelicaFrame.framebytes[c+4];

            for (blknum = 0; blknum < ARRAYLEN(liteblks);) {
                // block to read.
                BuildFliteRdblk(ndef, 1, &liteblks[blknum]);

                //TransmitFor18092_AsReader(frameSpace, frameSpace[2]+4, GetCountSspClk()+512, 1, 0);

                TransmitFor18092_AsReader(frameSpace, frameSpace[2] + 4, NULL, 1, 0);
                // read block
                if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_RDBLK_ACK) {

                    dest[cnt++] = liteblks[blknum];

                    const uint8_t *fb = FelicaFrame.framebytes;
                    dest[cnt++] = fb[12];
                    dest[cnt++] = fb[13];

                    //memcpy(dest+cnt, FelicaFrame.framebytes + 15, 16);
                    //cnt += 16;
                    for (uint8_t j = 0; j < 16; j++) {
                        dest[cnt++] = fb[15 + j];
                    }

                    blknum++;
                    cntfails = 0;

                    // // print raw log.
                    // Dbprintf("LEN %u | Dump bytes count %u ", FelicaFrame.len, cnt);
                    // Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes + 15, 0);
                } else {
                    cntfails++;
                    if (cntfails > 12) {
                        blknum++;
                        cntfails = 0;
                    }
                }
            }

            isOK = true;
            break;
        }
    }
    switch_off();

    // Resetting Frame mode (First set in fpgaloader.c)
    AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);

    // setting tracelen - important!  it was set by buffer overflow before
    // iceman:  is this still needed?!?
    set_tracelen(cnt);
    reply_mix(CMD_ACK, isOK, cnt, 0, 0, 0);
}
