//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// Low frequency ZX8211 funtions
//-----------------------------------------------------------------------------
#include "lfzx.h"
#include "zx8211.h"

#include "BigBuf.h"
#include "crc.h"        // CRC-8 / Hitag1 / ZX8211
#include "fpgaloader.h"
#include "dbprint.h"
#include "lfops.h"      // turn_read_lf_on / off
#include "lfadc.h"
#include "lfsampling.h" // getSamplingConfig
#include "pm3_cmd.h"    // struct
#include "ticks.h"

/*
ZX8211

RWE to tag
----------
ASK w 100% modulation index
When power field is:
   off, its considered LOW field
   on,  its considered HIGH field


Binary Pulse Length Coding (BPLC)

ZERO = 8 off,  8 on  (14-22)
ONE  = 8 off, 28 on  (26-32)
EOF  = 8 off, 30 on  (38 or more)

Protection
----------
32bit read password
32bit write password

Config bits
-------------

Timings
-------

Tx = 8us = 1fc
*/

#define ZX_START_GAP        170
#define ZX_WAIT_GAP         90
#define ZX_GAP              8    // 4 - 10
#define ZX_T0               18
#define ZX_T0_MIN           14
#define ZX_T0_MAX           22
#define ZX_T1               28
#define ZX_T1_MIN           26
#define ZX_T1_MAX           32
#define ZX_TEOF             38
#define ZX_RESET_GAP        35000 // 35ms
#define ZX_RESPONSE_GAP     208

#define ZX_PROG             716
#define ZX_PROG_CT          4470

// TTF switch to RTF
#define ZX_SWITCH_RTF 350

// ZX commands
#define LF_ZX_GET_UID  0b00110
#define LF_ZX_READ
#define LF_ZX_WRITE


static void zx8211_setup_read(void) {

    FpgaDownloadAndGo(FPGA_BITSTREAM_LF);

    // Make sure the tag is reset
    FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);

    // use lf config settings
    sample_config *sc = getSamplingConfig();
    LFSetupFPGAForADC(sc->divisor, true);

    FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);

    // 50ms for the resonant antenna to settle.
    WaitMS(50);

    // Now set up the SSC to get the ADC samples that are now streaming at us.
    FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER);

    FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125);

    // Connect the A/D to the peak-detected low-frequency path.
    SetAdcMuxFor(GPIO_MUXSEL_LOPKD);

    // Start the timer
    StartTicks();

    // Watchdog hit
    WDT_HIT();
}

static void zx_send(const uint8_t *cmd, uint8_t clen) {

    if (clen == 0)
        return;

    turn_read_lf_on(ZX_START_GAP);

    // now start writing with bitbanging the antenna.
    while (clen-- > 0) {

        turn_read_lf_off(ZX_GAP * 8);

        if (((*cmd++) & 1) == 1) {
            turn_read_lf_on(ZX_T1 * 8);
        } else {
            turn_read_lf_on(ZX_T0 * 8);
        }
    }

    // send eof
    turn_read_lf_off(ZX_GAP * 8);
    turn_read_lf_on(ZX_TEOF * 8);
}

static void zx_get(bool ledcontrol) {

    while (BUTTON_PRESS() == false) {

        WDT_HIT();

        if (ledcontrol && (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) {
            LED_D_ON();
        }

        if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {
            volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
            (void)sample;

            // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope
            if (ledcontrol) LED_D_OFF();

        }
    }
}

int zx8211_read(zx8211_data_t *zxd, bool ledcontrol) {
    zx8211_setup_read();

    // clear buffer now so it does not interfere with timing later
    BigBuf_Clear_ext(false);

    if (ledcontrol) LED_A_ON();

    // send GET_UID
    zx_send(NULL, 0);

    FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);

    zx_get(ledcontrol);

    //uint32_t cs = CRC8Hitag1(uint8_t *buff, size_t size);

    if (ledcontrol) LEDsoff();

    StopTicks();
    lf_finalize(ledcontrol);

    reply_ng(CMD_LF_ZX_READ, PM3_SUCCESS, NULL, 0);
    return PM3_SUCCESS;
}

int zx8211_write(zx8211_data_t *zxd, bool ledcontrol) {
    zx8211_setup_read();

    StopTicks();
    lf_finalize(ledcontrol);
    //reply_ng(CMD_LF_ZX_WRITE, status, tag.data, sizeof(tag.data));
    return PM3_SUCCESS;
}
