/*****************************************************************
/
/ File   :   ifdhandler.c
/ Author :   David Corcoran <corcoran@linuxnet.com>
/ Date   :   June 15, 2000
/ Purpose:   This provides reader specific low-level calls.
/            A lot of the functionality listed in the specs is
/            not done.  I've done a minimum to get started.
/            See http://www.linuxnet.com for more information.
/ License:   See file LICENSE
/
******************************************************************/

#ifndef TARGET_OSX
#include "config.h"
#endif

#include "pcscdefines.h"
#include "ifdhandler.h"
#include "AdmHndlr.h"
#include "T0Hndlr.h"
#include "T1Hndlr.h"
#include "common.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "MCU_ATR.h"

// Slot
typedef struct _SLOT {
    UCHAR atr[64];
    DWORD atrLen;
} SLOT, *PSLOT;

// Reader
typedef struct _READER {
    SLOT slots[MAX_SLOTS];
    DWORD slotCount;
} READER, *PREADER;

// Array of readers
static READER g_Readers[MAX_READERS];

RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
{
    ULONG rv = STATUS_SUCCESS;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

#ifdef PCSC_DEBUG
    printf("IFDHCreateChannel: Enter, Lun = 0x%08X, Channel = 0x%08X\n", Lun, Channel);
#endif

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    // Reset ATR
    memset(g_Readers[readerIndex].slots[slotIndex].atr, 0, sizeof(g_Readers[readerIndex].slots[slotIndex].atr));
    g_Readers[readerIndex].slots[slotIndex].atrLen = 0;

    // If first slot
    if (slotIndex == 0)
    {
        // Initialize slot count
        g_Readers[readerIndex].slotCount = 1;

        // Initialize reader
        rv = Adm_Initialize("usb", readerIndex, Channel);
    }
    else
    {
        // Increment slot count
        g_Readers[readerIndex].slotCount++;
    }

    if (rv == STATUS_SUCCESS)
    {
#ifdef PCSC_DEBUG
        printf("IFDHCreateChannel: Exit(IFD_SUCCESS)\n");
#endif
        return IFD_SUCCESS;
    }
    else
    {
#ifdef PCSC_DEBUG
        printf("IFDHCreateChannel: Exit(IFD_COMMUNICATION_ERROR)\n");
#endif
        return IFD_COMMUNICATION_ERROR;
    }
}

RESPONSECODE IFDHCloseChannel(DWORD Lun)
{
    ULONG rv = STATUS_SUCCESS;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    // Power off card
    Adm_UnPowerICC(readerIndex, slotIndex);

    // Decrement slot count
    g_Readers[readerIndex].slotCount--;
    if (g_Readers[readerIndex].slotCount == 0)
        rv = Adm_UnInitialize(readerIndex);

    if (rv == STATUS_SUCCESS)
    {
        return IFD_SUCCESS;
    }
    else
    {
        return IFD_COMMUNICATION_ERROR;
    }
}

RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
{
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    switch ( Tag )
    {
    case TAG_IFD_ATR:
        if (*Length >= g_Readers[readerIndex].slots[slotIndex].atrLen)
        {
            *Length = g_Readers[readerIndex].slots[slotIndex].atrLen;
            memcpy(Value, g_Readers[readerIndex].slots[slotIndex].atr, *Length);
        }
        break;

    case TAG_IFD_SIMULTANEOUS_ACCESS:
        if (*Length >= 1)
        {
            *Length = 1;
            Value[0] = MAX_READERS;
        }
        break;

    case TAG_IFD_THREAD_SAFE:
        if (*Length >= 1)
        {
            *Length = 1;
            Value[0] = 0;
        }
        break;

    case TAG_IFD_SLOTS_NUMBER:
        if (*Length >= 1)
        {
            *Length = 1;
            Value[0] = Adm_GetNumSlots(readerIndex);
        }
        break;

    default:
        return IFD_ERROR_TAG;
    }

    return IFD_SUCCESS;
}

RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
{
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    return IFD_NOT_SUPPORTED;
}

RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags,
    UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
{
    UCHAR           fl;
    UCHAR           dl;
    UCHAR           pps[100];
    UCHAR           rdrInfo[100];
    BOOL            bOldFw = FALSE;
    ULONG           ppsLen;
    ULONG           rv;
    RESPONSECODE    result;

    ULONG nProtocol;

    UCHAR atr[64];
    DWORD atrLen;

    ULONG uSupPrtcl;
    UCHAR TD;
    UCHAR idx;
    UCHAR bShf;

    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

#ifdef PCSC_DEBUG
    printf("%s: IFDHSetProtocolParameters: Enter\n", __FILE__);
#endif

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    // v1.4: Simply return success when ATR 3B 00 is found.
    if (!memcmp(g_Readers[readerIndex].slots[slotIndex].atr, "\x3B\x00", 2))
    {
#ifdef PCSC_DEBUG
        printf("IFDHSetProtocolParameters: Reader emulated ATR -- Memory Card Inserted\n");
#endif
        return IFD_SUCCESS;
    }

    // v1.3: Fix protocol, flags and PTS1 error
#define SCARD_PROTOCOL_T0       0x0001
#define SCARD_PROTOCOL_T1       0x0002

    nProtocol = Protocol == SCARD_PROTOCOL_T0 ? 0 : 1;

    Flags = 0x10;   // PPS1 is always present for ACR38

    pps[0] = 0xFF;
    pps[1] = Flags | (0x0F & nProtocol);
    ppsLen = 2;

    if (PTS1 == 0x00)
    {
        // case when PCSC-LITE does not provide PTS1
        // use the default speed as reported in ATR then
        MCU_ATR_RESULT  mcuATRResult;
        MCU_ATR         mcuATR;
        UCHAR           mcuATRTA1;

#ifdef PCSC_DEBUG
        printf("%s: PCSC-LITE does not provide PTS1 -> use default\n", __FILE__);
#endif
        mcuATRResult = MCUAtrInit(&mcuATR, g_Readers[readerIndex].slots[slotIndex].atr, g_Readers[readerIndex].slots[slotIndex].atrLen);
        if (mcuATRResult != MCU_ATR_OK)
        {
#ifdef PCSC_DEBUG
            printf("%s: MCUAtrInit failed, 0x%X\n", __FILE__, mcuATRResult);
#endif
            result = STATUS_DATA_ERROR;
            return result;
        }

        mcuATRResult = MCUAtrGetInterfaceByte(&mcuATR, 1, MCU_ATR_INTERFACE_TA, &mcuATRTA1);
        if (mcuATRResult != MCU_ATR_OK)
        {
#ifdef PCSC_DEBUG
            printf("%s: MCUAtrGetInterfaceByte failed, 0x%X\n", __FILE__, mcuATRResult);
#endif
            result = STATUS_DATA_ERROR;
            MCUAtrCleanUp(&mcuATR);

            // v1.7.1: Default F/D = 0x11 if TA1 is absent
            mcuATRTA1 = 0x11;
        }

        PTS1 = mcuATRTA1;
#ifdef PCSC_DEBUG
        printf("PTS1 = 0x%02X\n",PTS1);
#endif
    }

    if ((Flags & 0x10) == 0x10)
    {
        fl = (PTS1 & 0xF0) >> 4;
        dl = (PTS1 & 0x0F);

        // v1.7.8: Get reader F/W version
        rv = Adm_GetAcrStats( readerIndex, rdrInfo);
        if (rv != STATUS_SUCCESS)
        {
#ifdef PCSC_DEBUG
            printf("IFDHSetProtocolParameters: Adm_GetAcrStats failed, %X\n", rv);
            printf("IFDHSetProtocolParameters: Assuming old FW (104)\n");
#endif
            bOldFw = TRUE;
        }
        else
        {
            int nFwVer = 0;
#ifdef PCSC_DEBUG
            UCHAR szFwVer[11];
            memset(szFwVer, 0, sizeof(szFwVer));
            memcpy(szFwVer, rdrInfo, 10);
            printf("IFDHSetProtocolParameters: F/W version = %s\n", szFwVer);
#endif
            nFwVer = (rdrInfo[6] - '0') * 100 + (rdrInfo[7] - '0') * 10 + (rdrInfo[8] - '0');
            if (nFwVer <= 104)
                bOldFw = TRUE;
        }

        pps[ppsLen] = PTS1;
        ppsLen++;
    }

    if ((Flags & 0x20) == 0x20)
    {
        pps[ppsLen] = PTS2;
        ppsLen++;
    }

    if ((Flags & 0x40) == 0x40)
    {
        pps[ppsLen] = PTS3;
        ppsLen++;
    }

    // v1.3: Add the missed PPS_CheckSum
    pps[ppsLen] = pps[0] ^ pps[1] ^ pps[2]; // PPS_CheckSum
    ppsLen++;

    // v1.7: Skip PPS when not needed
    uSupPrtcl = 0;
    TD = g_Readers[readerIndex].slots[slotIndex].atr[1];
    idx = 1;
    bShf = 0;
    do
    {
        for (bShf = 4; bShf < 8; bShf++)
        {
            if((TD >> bShf) & 0x01)
                idx++;
        }

        if((TD & 0x80) == 0)
            break;

        TD = g_Readers[readerIndex].slots[slotIndex].atr[idx];

        if ((TD & 0x0F) == 0)
            uSupPrtcl |= SCARD_PROTOCOL_T0;
        else if ((TD & 0x0F) == 1)
            uSupPrtcl |= SCARD_PROTOCOL_T1;

    }
    while (idx < 36);

#if PCSC_DEBUG
    printf("%s: This card supports %s %s protocol\n",
        __FILE__,
        uSupPrtcl & SCARD_PROTOCOL_T0 ? "T0" : " ",
        uSupPrtcl & SCARD_PROTOCOL_T1 ? "T1" : " ");
#endif

    if ((uSupPrtcl == Protocol) &&
        (uSupPrtcl == SCARD_PROTOCOL_T0 || uSupPrtcl == SCARD_PROTOCOL_T1) &&
        (fl == 0x01 && dl == 0x01))
    {
        // Necessary Conditions: Card supports 1 protocol
        //                       Fl/Dl indicates default baudrate
        //                       Selected protocol = supported protocol
        //
        // Action: Skip issuing PPS and return success to PCSCLITE
#if PCSC_DEBUG
        printf("%s: There is no need to issue PPS for this card -> Skipping PPS\n", __FILE__);
#endif
        return IFD_SUCCESS;
    }

    // v1.7.9: Fix for wrong protocol passed from pcsc-lite (Mac OS X 10.5)
    if ((uSupPrtcl & SCARD_PROTOCOL_T0) &&
        (uSupPrtcl == SCARD_PROTOCOL_T0) &&
        (uSupPrtcl != Protocol))
        return IFD_PROTOCOL_NOT_SUPPORTED;

    if ((uSupPrtcl & SCARD_PROTOCOL_T1) &&
        (uSupPrtcl == SCARD_PROTOCOL_T1) &&
        (uSupPrtcl != Protocol))
        return IFD_PROTOCOL_NOT_SUPPORTED;

    // v1.7.10: Correct the special handling of PPS for FW104 in v1.7.8
    if (bOldFw)
    {
        if (PTS1 == 0x95)
        {
            pps[2] = 0x94;
            pps[3] = pps[0] ^ pps[1] ^ pps[2];
            rv = Adm_DoPPSExchange(readerIndex, slotIndex, pps, ppsLen);
            if (rv != STATUS_SUCCESS)
            {
                pps[2] = 0x95;
                pps[3] = pps[0] ^ pps[1] ^ pps[2];
                rv = Adm_DoPPSExchange(readerIndex, slotIndex, pps, ppsLen);
                if (rv != STATUS_SUCCESS)
                {
#ifdef PCSC_DEBUG
                    printf("IFDHSetProtocolParameters: Resetting the card (PPS failed - old firmware)...\n");
#endif
                    atrLen = sizeof(atr);
                    Adm_ResetICC(readerIndex, slotIndex, atr, &atrLen);
                }
            }
        }
    }
    else
    {
        rv = Adm_DoPPSExchange(readerIndex, slotIndex, pps, ppsLen);
        if (rv != STATUS_SUCCESS)
        {
#ifdef PCSC_DEBUG
            printf("IFDHSetProtocolParameters: Resetting the card (PPS failed)...\n");
#endif
            atrLen = sizeof(atr);
            Adm_ResetICC(readerIndex, slotIndex, atr, &atrLen);
        }
    }

    return IFD_SUCCESS;
}

RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
{
    RESPONSECODE ret = IFD_SUCCESS;
    ULONG rv;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots
    UCHAR atr[MAX_ATR_SIZE];
    DWORD atrLen;

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    switch (Action)
    {
    case IFD_POWER_DOWN:
        // Reset ATR
        memset(g_Readers[readerIndex].slots[slotIndex].atr, 0, sizeof(g_Readers[readerIndex].slots[slotIndex].atr));
        g_Readers[readerIndex].slots[slotIndex].atrLen = 0;

        // Power off card
        rv = Adm_UnPowerICC(readerIndex, slotIndex);
        if (rv != STATUS_SUCCESS)
            ret = IFD_ERROR_POWER_ACTION;
        break;

    case IFD_POWER_UP:
    case IFD_RESET:
        // Warm reset
        atrLen = sizeof(atrLen);
        rv = Adm_ResetICC(readerIndex, slotIndex, atr, &atrLen);
        if (rv != STATUS_SUCCESS)
        {
            // Cold reset
            Adm_UnPowerICC(readerIndex, slotIndex);
            usleep(100 * 1000);
            atrLen = sizeof(atrLen);
            rv = Adm_ResetICC(readerIndex, slotIndex, atr, &atrLen);
        }

        if (rv != STATUS_SUCCESS)
        {
            // Reset ATR
            memset(g_Readers[readerIndex].slots[slotIndex].atr, 0, sizeof(g_Readers[readerIndex].slots[slotIndex].atr));
            g_Readers[readerIndex].slots[slotIndex].atrLen = 0;

            ret = IFD_ERROR_POWER_ACTION;
        }
        else
        {
            // Store ATR
            g_Readers[readerIndex].slots[slotIndex].atrLen = atrLen;
            memcpy(g_Readers[readerIndex].slots[slotIndex].atr, atr, atrLen);

            // Return ATR
            *AtrLength = atrLen;
            memcpy(Atr, atr, atrLen);
        }
        break;

    default:
        ret = IFD_NOT_SUPPORTED;
        break;
    }

    return ret;
}

RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
    PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength,
    PSCARD_IO_HEADER RecvPci)
{
    ULONG rv;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

#ifdef PCSC_DEBUG
    int i;
#endif

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

#ifdef PCSC_DEBUG
    printf("T=%d -> ", SendPci.Protocol);
    for (i = 0; i < TxLength; i++)
        printf("%x ", TxBuffer[i]);
    printf("\n");
#endif

    if (SendPci.Protocol == 0)
    {
#ifdef PCSC_DEBUG
    printf("Doing T0_ExchangeData\n");
#endif
        rv = T0_ExchangeData(readerIndex, slotIndex, TxBuffer, TxLength, RxBuffer, RxLength);
    }
    else if (SendPci.Protocol == 1)
    {
#ifdef PCSC_DEBUG
    printf("Doing T1_ExchangeData\n");
#endif
        rv = T1_ExchangeData(readerIndex, slotIndex, TxBuffer, TxLength, RxBuffer, RxLength);
    }
    else
    {
        return IFD_PROTOCOL_NOT_SUPPORTED;
    }

#ifdef PCSC_DEBUG
    printf("T=%d <- ", SendPci.Protocol);
    for (i = 0; i < *RxLength; i++)
        printf("%x ", RxBuffer[i]);
    printf("\n");
#endif

    if (rv == STATUS_SUCCESS)
    {
        return IFD_SUCCESS;
    }
    else
    {
        return IFD_COMMUNICATION_ERROR;
    }
}

RESPONSECODE IFDHControl(DWORD Lun, PUCHAR TxBuffer, DWORD TxLength,
    PUCHAR RxBuffer, PDWORD RxLength)
{
    /* This function performs a data exchange with the reader
     * (not the card) specified by Lun. Here XXXX will only be used.
     * It is responsible for abstracting functionality such as PIN
     * pads, biometrics, LCD panels, etc.  You should follow the MCT,
     * CTBCS specifications for a list of accepted commands to
     * implement.
     * TxBuffer - Transmit data
     * TxLength - Length of this buffer.
     * RxBuffer - Receive data
     * RxLength - Length of the received data.
     * This function will be passed the length of the buffer RxBuffer
     * and it must set this to the length of the received data.
     *
     * Notes:
     * RxLength should be zero on error.
     */

    ULONG rv;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    rv = Adm_Control(readerIndex, TxBuffer, TxLength, RxBuffer, RxLength);
    if (rv == STATUS_SUCCESS)
    {
        return IFD_SUCCESS;
    }
    else
    {
        return IFD_COMMUNICATION_ERROR;
    }
}

RESPONSECODE IFDHICCPresence(DWORD Lun)
{
    ULONG rv;
    DWORD readerIndex = READER_INDEX(Lun);  // v1.7.9:  Support multiple readers
    DWORD slotIndex = SLOT_INDEX(Lun);      // v1.7.11: Support multiple slots

    // Check reader and slot index
    if (!IsValidReaderIndex(readerIndex) || !IsValidSlotIndex(slotIndex))
        return IFD_COMMUNICATION_ERROR;

    if (slotIndex == 0)
        rv = Adm_IsICCPresent(readerIndex);
    else
        rv = STATUS_SUCCESS;    // SAM slot always card present

    if (rv == STATUS_SUCCESS)
    {
        return IFD_ICC_PRESENT;
    }
    else if (rv == STATUS_UNSUCCESSFUL)
    {
        // Reset ATR
        memset(g_Readers[readerIndex].slots[slotIndex].atr, 0, sizeof(g_Readers[readerIndex].slots[slotIndex].atr));
        g_Readers[readerIndex].slots[slotIndex].atrLen = 0;

        return IFD_ICC_NOT_PRESENT;
    }
    else
    {
        return IFD_COMMUNICATION_ERROR;
    }
}
