/*****************************************************************
/
/ File   :   AdmHndlr.c
/ Author :   David Corcoran
/ Date   :   October 15, 1999
/ Purpose:   This handles administrative functions like reset/power.
/            See http://www.linuxnet.com for more information.
/ License:   See file LICENSE
/
******************************************************************/

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

#include "pcscdefines.h"
#include "AdmHndlr.h"
#include "MCU_ATR.h"
#ifdef	TARGET_OSX
#include "usbserial_macosx.h"
#else
#include "usbserial_linux.h"
#endif
#include "common.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ACR38IOCtrl.h"

#define READER_FREQUENCY		(3680000)
#define READER_MAX_SUPPORT_FI	(1)
#define READER_MAX_SUPPORT_DI	(8)

#define NUM_ACR38_CONTEXT	(100)
typedef struct _ACR38_CONTEXT
{
	int						bUsed;
	DWORD					Lun;
	ACR38_READER_CARD_TYPE	type;

} ACR38_CONTEXT, *PACR38_CONTEXT;

static ACR38_CONTEXT			gACR38Context[NUM_ACR38_CONTEXT];
static int				bACR38ContextInit	= 0;
static int				bACR38PPS_94Fix		= 0;

static double
Adm_PPSBitRate(	UCHAR	fl,
				UCHAR	dl);

static ULONG
Adm_Control_SetCardType(PACR38_CONTEXT	pContext,
						PUCHAR			TxBuffer,
						DWORD			TxLength,
						PUCHAR			RxBuffer,
						PDWORD			RxLength);

static PACR38_CONTEXT
Adm_GetContext(DWORD	Lun);

ULONG
Adm_Initialize(	char*	pcPort,
				DWORD	Lun,
				DWORD	Channel )
{
	int	i;

#ifdef PCSC_DEBUG
	printf("Adm_Initialize: Enter\n");
#endif

	if (bACR38ContextInit == 0)
	{
		for (i = 0; i < NUM_ACR38_CONTEXT; i++)
		{
			gACR38Context[i].bUsed	= 0;
		}
		bACR38ContextInit = 1;
	}

	if (OpenUSB(Lun, Channel) == STATUS_SUCCESS)
	{
		for (i = 0; i < NUM_ACR38_CONTEXT; i++)
		{
			if (gACR38Context[i].bUsed == 0)
			{
				gACR38Context[i].bUsed	= 1;
				gACR38Context[i].Lun	= Lun;
				gACR38Context[i].type	= READER_CARD_TYPE_MCU;
				break;
			}
		}
		if (i == NUM_ACR38_CONTEXT)
		{	/* No free context room... */
			return STATUS_UNSUCCESSFUL;
		}
		return STATUS_SUCCESS;
	}
	return STATUS_UNSUCCESSFUL;
}

ULONG
Adm_UnInitialize(DWORD	Lun)
{
	PACR38_CONTEXT	pContext;

#ifdef PCSC_DEBUG
printf("Adm_UnInitialize: Enter\n");
#endif

	CloseUSB(Lun);
	pContext = Adm_GetContext(Lun);
	if (pContext != NULL)
	{
		pContext->bUsed = 0;
	}
	return STATUS_SUCCESS;
}

ULONG Adm_PowerICC( DWORD Lun, DWORD slotIndex, PUCHAR pucAtr, PULONG pulAtrLen ) {
  return Adm_ResetICC( Lun, slotIndex, pucAtr, pulAtrLen );
}

ULONG
Adm_ResetICC(	DWORD	Lun,
				DWORD	slotIndex,
				PUCHAR	pucAtr,
				PULONG	pulAtrLen)
{
	ULONG			rv;
	UCHAR			emvOption[5];
	UCHAR			selectCardType[20];
	UCHAR			powerReset[4];
	UCHAR			resetCardType[20];
	UCHAR			ucResponse[MAX_BUFFER_SIZE];
	ULONG			ulSize;
	PACR38_CONTEXT	pContext;
#ifdef PCSC_DEBUG
printf("Adm_ResetICC: Enter\n");
#endif
	*pulAtrLen = 0;

	pContext = Adm_GetContext(Lun);
	if (pContext == NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}

	// Card slot
	if (slotIndex == 0)
	{
		if (pContext->type == READER_CARD_TYPE_MCU)
		{	/* MCU Card */
			/* Setup the EMV Option [No EMV + No Mem Card] */
			emvOption[0]	= 0x01;
			emvOption[1]	= 0x07;
			emvOption[2]	= 0x00;
			emvOption[3]	= 0x01;
			emvOption[4]	= 0x00;
			rv = Adm_Transmit(	Lun,
								emvOption,
								5,
								ucResponse,
								&ulSize);
			if (rv != STATUS_SUCCESS)
			{
				return rv;
			}

			/* Select Card Type [Auto Select] */
			selectCardType[0] = 0x01;
			selectCardType[1] = 0x02;
			selectCardType[2] = 0x00;
			selectCardType[3] = 0x01;
			selectCardType[4] = 0x00;
			rv = Adm_Transmit(	Lun,
								selectCardType,
								5,
								ucResponse,
								&ulSize);
			if (rv != STATUS_SUCCESS)
			{
				return rv;
			}

			/* Power up the card */
			powerReset[0] = 0x01;
			powerReset[1] = 0x80;
			powerReset[2] = 0x00;
			powerReset[3] = 0x00;
			rv = Adm_Transmit(Lun, powerReset, 4, pucAtr, pulAtrLen);
		}
		else
		{	/* Memory Card */
			/* Setup the EMV Option [No EMV + Mem Card] */
			emvOption[0]	= 0x01;
			emvOption[1]	= 0x07;
			emvOption[2]	= 0x00;
			emvOption[3]	= 0x01;
			emvOption[4]	= 0x20;
			rv = Adm_Transmit(	Lun,
								emvOption,
								5,
								ucResponse,
								&ulSize);
			if (rv != STATUS_SUCCESS)
			{
				return rv;
			}

			selectCardType[0] = 0x01;
			selectCardType[1] = 0x02;
			selectCardType[2] = 0x00;
			selectCardType[3] = 0x01;
			selectCardType[4] = pContext->type;
			rv = Adm_Transmit(	Lun,
								selectCardType,
								5,
								ucResponse,
								&ulSize);
			if (rv != STATUS_SUCCESS)
			{
				return rv;
			}

			powerReset[0] = 0x01;
			powerReset[1] = 0x80;
			powerReset[2] = 0x00;
			powerReset[3] = 0x00;
			rv = Adm_Transmit(Lun, powerReset, 4, pucAtr, pulAtrLen);
			if (rv != STATUS_SUCCESS)
			{
				return rv;
			}

			resetCardType[0] = 0x01;
			resetCardType[1] = 0xA0;
			resetCardType[2] = 0x00;
			resetCardType[3] = 0x06;
			resetCardType[4] = 0xFF;
			resetCardType[5] = 0xA4;
			resetCardType[6] = 0x00;
			resetCardType[7] = 0x00;
			resetCardType[8] = 0x01;
			resetCardType[9] = pContext->type;
			rv = Adm_Transmit(Lun, resetCardType, 10, ucResponse, &ulSize);
		}
	}
	else    // SAM card slot
	{
		// Power up SAM card
		powerReset[0] = 0x01;
		powerReset[1] = 0x90;
		powerReset[2] = 0x00;
		powerReset[3] = 0x00;
		rv = Adm_Transmit(Lun, powerReset, 4, pucAtr, pulAtrLen);
	}

	/* Turn off card status notification */
	/* Adm_SetNotification( ADM_NOTIFY_FALSE ); */

	return rv;
}

ULONG Adm_SetNotification( DWORD Lun, UCHAR ucDisposition ) {

  ULONG rv;
  UCHAR ucSet[5];
  UCHAR pucRx[MAX_BUFFER_SIZE];
  ULONG ulRxLen;
#ifdef PCSC_DEBUG
printf("Adm_SetNotification: Enter\n");
#endif
  ucSet[0] = 0x01;
  ucSet[1] = 0x06;
  ucSet[2] = 0x01;
  ucSet[3] = ucDisposition;
  //ucSet[4] = Adm_CheckSum( ucSet, 4);

  rv = Adm_Transmit( Lun, ucSet, 5, pucRx, &ulRxLen );

  return rv;
}

// v1.7.10: response buffer rely on pucRxBuffer
ULONG Adm_Transmit( DWORD Lun, PUCHAR pucTxBuffer, ULONG ulTxLength,
                    PUCHAR pucRxBuffer, PULONG pulRxLength )
{
    ULONG ret = STATUS_SUCCESS;

    BYTE epBuffer[64];
    DWORD epBufferLen;

    BOOL responseReceived;
    BOOL headerReceived;
    DWORD bufferLen;
    DWORD readLen;
    DWORD dataLen;
    DWORD tempLen;

    //ULONG i;
/*
#ifdef PCSC_DEBUG
    int i;
#endif
*/
    if (bACR38PPS_94Fix == 1)
        usleep(1);
/*
//#ifdef PCSC_DEBUG
    printf("Adm_Transmit: pucTxBuffer: ");
    for (i = 0; i < ulTxLength; i++)
        printf("%02X ", pucTxBuffer[i]);
    printf("\n");
//#endif
*/

    if (WriteUSB(Lun, ulTxLength, pucTxBuffer) != STATUS_SUCCESS)
    {
        //printf("Adm_Transmit ERROR : WriteUSB() != STATUS_SUCCESS \n");
        return STATUS_DATA_ERROR;
    }

    responseReceived = FALSE;
    headerReceived = FALSE;

    //printf("Adm_Transmit: *pulRxLength 1: %d\n", *pulRxLength);

    bufferLen = *pulRxLength;
    readLen = 0;
    dataLen = 0;
    tempLen = 0;

    *pulRxLength = 0;

    while (!responseReceived)
    {
        epBufferLen = sizeof(epBuffer);
        if (ReadUSB(Lun, &epBufferLen, epBuffer) != STATUS_SUCCESS)
        {
            //printf("Adm_Transmit ERROR : ReadUSB() != STATUS_SUCCESS \n");
            ret = STATUS_DATA_ERROR;
            break;
        }

        if (epBufferLen == 0)
        {
            //printf("Adm_Transmit ERROR : epBufferLen == 0 \n");
            break;
        }
/*
        printf("Adm_Transmit: epBuffer: ");
        for (i = 0; i < epBufferLen; i++)
            printf("%02X ", epBuffer[i]);
        printf("\n");
*/
        if (headerReceived)
        {
            if (dataLen > epBufferLen)
                tempLen = epBufferLen;
            else
                tempLen = dataLen;

            memcpy(pucRxBuffer + readLen, epBuffer, tempLen);

            readLen += tempLen;
            dataLen -= tempLen;
            if (dataLen <= 0)
            {
                responseReceived = TRUE;
                *pulRxLength = readLen;
            }
        }
        else
        {
            // Header
            if (epBuffer[0] == 0x01)
            {
                headerReceived = TRUE;

                // Check status
                if (epBuffer[1] != 0)
                {
                    //printf("Adm_Transmit ERROR : epBuffer[1] != 0\n");
                    ret = STATUS_DATA_ERROR;
                }

                // Get data length
                dataLen = ((epBuffer[2] << 8) | epBuffer[3]);

                tempLen = epBufferLen - 4;
                memcpy(pucRxBuffer, epBuffer + 4, tempLen);

                readLen += tempLen;
                dataLen -= tempLen;
                if (dataLen <= 0)
                {
                    responseReceived = TRUE;
                    *pulRxLength = readLen;
                }
            }
        }
        //printf("Adm_Transmit: dataLen=%d  readLen=%d  tempLen=%d\n", dataLen, readLen, tempLen);
    }

/*
//#ifdef PCSC_DEBUG
    printf("Adm_Transmit: *pulRxLength 2: %d\n", *pulRxLength);
    printf("Adm_Transmit: pucRxBuffer: ");
    for (i = 0; i < *pulRxLength; i++)
        printf("%02X ", pucRxBuffer[i]);
    printf("\n");
//#endif
*/
    if (!responseReceived)
    {
        //printf("Adm_Transmit ERROR : !responseReceived \n");
        ret = STATUS_DATA_ERROR;
    }

    //printf("Adm_Transmit: ret = 0x%08X    SUCCESS=0x%08X\n", ret, STATUS_SUCCESS);
    return ret;
}

/*
ULONG Adm_Transmit( DWORD Lun, PUCHAR pucTxBuffer, ULONG ulTxLength, 
                    PUCHAR pucRxBuffer, PULONG pulRxLength ) {

  UCHAR ucResponse[MAX_BUFFER_SIZE];
  DWORD readLength;
  DWORD totalBytesToRead = 0;
  int byteCounter, retFlag, i;

  byteCounter = 0; retFlag = 0;

  if (bACR38PPS_94Fix == 1)
  {
	  usleep(1);
  }

  if ( WriteUSB( Lun, ulTxLength, pucTxBuffer ) != STATUS_SUCCESS ) {
    return STATUS_DATA_ERROR;
  }
 
  // IO_UpdateReturnBlock( 4 ); // Wait up to 4 seconds until error
  
  byteCounter = 0;

  readLength = ADM_BULK_IN_LEN;  // read header bytes first
  if ( ReadUSB( Lun, &readLength, &ucResponse[byteCounter] ) != STATUS_SUCCESS) {
    return STATUS_DATA_ERROR;
  } else {
    totalBytesToRead = ucResponse[2]*256 + ucResponse[3];
  }

  if (ucResponse[1] != 0x00)
  {
	  return STATUS_DATA_ERROR;
  }  // status byte

  if (totalBytesToRead <= ADM_BULK_IN_LEN - 4) {
    // buffer satisfies
    *pulRxLength = totalBytesToRead;
    memcpy(pucRxBuffer, &ucResponse[4], *pulRxLength);
    return STATUS_SUCCESS;
  } else {
    byteCounter += ADM_BULK_IN_LEN;
  }

  // otherwise we must chain
  for (i=0; i < (totalBytesToRead - (ADM_BULK_IN_LEN - 4)) / ADM_BULK_IN_LEN; i++) {  

    readLength = ADM_BULK_IN_LEN;
    if ( ReadUSB( Lun, &readLength, &ucResponse[byteCounter] ) != STATUS_SUCCESS) {
      return STATUS_DATA_ERROR;
    }

    byteCounter += readLength;
  }

  if (((totalBytesToRead - (ADM_BULK_IN_LEN - 4)) % ADM_BULK_IN_LEN) != 0) {

    readLength = ADM_BULK_IN_LEN;
    if ( ReadUSB( Lun, &readLength, &ucResponse[byteCounter] ) != STATUS_SUCCESS) {
      return STATUS_DATA_ERROR;
    }

    byteCounter += readLength;
  }
  
  *pulRxLength = totalBytesToRead;
  memcpy(pucRxBuffer, &ucResponse[4], *pulRxLength);
  return STATUS_SUCCESS;
}
*/

ULONG Adm_SetWWT( DWORD Lun, ULONG ulWWT ) {

  return STATUS_SUCCESS;
}

ULONG Adm_GetAcrStats( DWORD Lun, PUCHAR pucStatus ) {

  ULONG rv;
  ULONG ulSize = 0;
  UCHAR ucStatus[5];
  UCHAR ucResponse[MAX_BUFFER_SIZE];

  ucStatus[0] = 0x01;
  ucStatus[1] = 0x01;
  ucStatus[2] = 0x00;
  ucStatus[3] = 0x00;

  rv = Adm_Transmit( Lun, ucStatus, 4, ucResponse, &ulSize );

  memcpy( pucStatus, ucResponse, ulSize );
  return rv;
}

ULONG Adm_IsICCPresent(DWORD Lun) {

  ULONG rv;
  UCHAR ucStatus[MAX_BUFFER_SIZE];

  rv = Adm_GetAcrStats( Lun, ucStatus );

  if ( rv == STATUS_SUCCESS ) {
  
    if ( ucStatus[15] == 0x01 || ucStatus[15] == 0x03 ) {
      return STATUS_SUCCESS; /* Card is inserted */
    } else {
      return STATUS_UNSUCCESSFUL;
    }
  }

  return STATUS_DATA_ERROR;

}

ULONG Adm_SelectCard( DWORD Lun, ULONG ulCardType ) {

  ULONG rv;
  ULONG ulSize = 0;
  UCHAR ucType[5];
  UCHAR ucResponse[MAX_BUFFER_SIZE];
#ifdef PCSC_DEBUG
printf("Adm_SelectCard: Enter\n");
#endif
  ucType[0] = 0x01;
  ucType[1] = 0x02;
  ucType[2] = 0x00;
  ucType[3] = 0x01;
  ucType[4] = (UCHAR) ulCardType;

  rv = Adm_Transmit( Lun, ucType, 5, ucResponse, &ulSize );

  return rv;
}

ULONG Adm_Select442818( DWORD Lun, PUCHAR pucAtr, PULONG pulAtrLen ) {

  ULONG rv;
  ULONG ulSize = 0;
  UCHAR selectCardType[20];
  UCHAR	powerReset[4];
  UCHAR uc4428[20];
  UCHAR ucResponse[MAX_BUFFER_SIZE];
#ifdef PCSC_DEBUG
printf("Adm_Select442818: Enter\n");
#endif
  selectCardType[0] = 0x01;
  selectCardType[1] = 0x02;
  selectCardType[2] = 0x00;
  selectCardType[3] = 0x01;
  selectCardType[4] = 0x05;
  rv = Adm_Transmit( Lun, selectCardType, 5, ucResponse, &ulSize );
  if (rv != STATUS_SUCCESS) {
    return rv;
  }

  powerReset[0] = 0x01;
  powerReset[1] = 0x80;
  powerReset[2] = 0x00;
  powerReset[3] = 0x00;
  rv = Adm_Transmit( Lun, powerReset, 4, ucResponse, &ulSize );
  if (rv != STATUS_SUCCESS) {
    return rv;
  }

  uc4428[0] = 0x01;
  uc4428[1] = 0xA0;
  uc4428[2] = 0x00;
  uc4428[3] = 0x06;
  uc4428[4] = 0xFF;
  uc4428[5] = 0xA4;
  uc4428[6] = 0x00;
  uc4428[7] = 0x00;
  uc4428[8] = 0x01;
  uc4428[9] = 0x05;

  rv = Adm_Transmit( Lun, uc4428, 10, ucResponse, &ulSize );

  if (rv != STATUS_SUCCESS) {
    return rv;
  }
    
  *pulAtrLen = ulSize - 2;  
  memcpy(pucAtr, &ucResponse, *pulAtrLen);  

  return rv;

}

ULONG Adm_UnPowerICC(DWORD Lun, DWORD slotIndex) {

  ULONG rv;
  ULONG ulSize;
  UCHAR ucPowerDown[5];
  UCHAR ucResponse[MAX_BUFFER_SIZE];
#ifdef PCSC_DEBUG
printf("Adm_UnPowerICC: Enter\n");
#endif
  ulSize = 0;

  ucPowerDown[0] = 0x01;
  ucPowerDown[1] = slotIndex == 0 ? 0x81 : 0x91;
  ucPowerDown[2] = 0x00;
  ucPowerDown[3] = 0x00;

  rv = Adm_Transmit( Lun, ucPowerDown, 4, ucResponse, &ulSize );

  return rv;
}

ULONG Adm_ReadData( DWORD Lun, ULONG ulAddress, PUCHAR pucData, 
                    ULONG ulDataLength ) {

  ULONG rv;
  UCHAR ucRead[9];
  ULONG readLen;
#ifdef PCSC_DEBUG
printf("Adm_ReadData: Enter\n");
#endif
  ucRead[0] = 0x01;
  ucRead[1] = 0xA0;
  ucRead[2] = 0x00;
  ucRead[3] = 0x05;
  ucRead[4] = 0xFF;
  ucRead[5] = 0xB2;
  ucRead[6] = ulAddress / 256;
  ucRead[7] = ulAddress % 256;
  ucRead[8] = (UCHAR) ulDataLength;

  readLen = 0;

  rv = Adm_Transmit( Lun, ucRead, 9, pucData, &readLen );
  
  return rv;
}

ULONG Adm_WriteData( DWORD Lun, ULONG ulAddress, PUCHAR pucData, 
                     ULONG ulDataLength ) {

  ULONG rv;
  UCHAR ucWrite[1024];
  UCHAR ucRead[1024];
  ULONG writeLen;
  ULONG readLen;
#ifdef PCSC_DEBUG
printf("Adm_WriteData: Enter\n");
#endif
  if (ulDataLength > sizeof(ucWrite)) {
    return STATUS_DATA_ERROR;
  }

  writeLen = ulDataLength + 5;


  ucWrite[0] = 0x01;
  ucWrite[1] = 0xA0;
  ucWrite[2] = writeLen / 256;
  ucWrite[3] = writeLen % 256;
  ucWrite[4] = 0xFF;
  ucWrite[5] = 0xD0;
  ucWrite[6] = ulAddress / 256;
  ucWrite[7] = ulAddress % 256;
  ucWrite[8] = ulDataLength;

  memcpy(&ucWrite[9], pucData, ulDataLength);

  readLen = 0;

  rv = Adm_Transmit( Lun, ucWrite, ulDataLength + 9, ucRead, &readLen );
  
  return rv;

}

ULONG Adm_WriteProtection( DWORD Lun, ULONG ulAddress, PUCHAR pucData, 
			   ULONG ulDataLength ) {

  return STATUS_SUCCESS;
}


ULONG Adm_PresentCode( DWORD Lun, PUCHAR pucCode ) {

  ULONG rv;
  ULONG ulSize;
  UCHAR ucCode[20];
  UCHAR ucResponse[20];

  ulSize = 0;

  ucCode[0] = 0x01;
  ucCode[1] = 0xA0;
  ucCode[2] = 0x00;
  ucCode[3] = 0x07;
  ucCode[4] = 0xFF;
  ucCode[5] = 0x20;
  ucCode[6] = 0x00;
  ucCode[7] = 0x00;
  ucCode[8] = 0x02;
  ucCode[9] = pucCode[0];
  ucCode[10] = pucCode[1];

  rv = Adm_Transmit( Lun, ucCode, 11, ucResponse, &ulSize );

  return rv;

}

ULONG
Adm_SetCardPPS(	DWORD	lun,
				DWORD   slotIndex,
				UCHAR	protocol,
				UCHAR	fl,
				UCHAR	dl,
				PUCHAR	pResultPPS,
				PULONG	pResultPPSLen)
{
	UCHAR	cmd[8];
	ULONG	rv;
#ifdef PCSC_DEBUG
printf("Adm_SetCardPPS: Enter\n");
#endif
	cmd[0]	= 0x01;
	cmd[1]	= slotIndex == 0 ? 0x0A : 0x0C;
	cmd[2]	= 0x00;
	cmd[3]	= 0x04;
	cmd[4]	= 0xFF;					/* PPSS */
	cmd[5]	= 0x10 | protocol;		/* PPS0 */
	cmd[6]	= fl * 0x10 + dl;		/* PPS1 */
	cmd[7]	= cmd[4]^cmd[5]^cmd[6];	/* PCK */

#ifdef PCSC_DEBUG
{
int i;
printf("Invoking SetCardPPS(): ");
for(i=0;i<8;i++) {
  printf("%02X ", cmd[i]);
}
printf("\n");
}
#endif

	rv = Adm_Transmit(lun, cmd, 8, pResultPPS, pResultPPSLen);
	if (rv != STATUS_SUCCESS)
	{
		goto Adm_TransmitFailed;
	}

	if (memcmp(&cmd[4], &pResultPPS[0], 4) != 0)
	{
		if ((cmd[4] != pResultPPS[0]) ||
			((cmd[5] & 0x0F) != (pResultPPS[1] & 0x0F)) ||
			!(pResultPPS[1] & 0x80))
		{
			rv = STATUS_UNSUCCESSFUL;
			goto PPSExchangeFailed;
		}
	}
	return rv;

/****** Error Part ******/
PPSExchangeFailed:
Adm_TransmitFailed:
	return rv;
}

ULONG
Adm_SetReaderPPS(	DWORD	lun,
					DWORD   slotIndex,
					PUCHAR	pPPS,
					ULONG	ppsLen)
{
	UCHAR	cmd[100];
	UCHAR	rspBuf[100];
	ULONG	rspBufLen;
#ifdef PCSC_DEBUG
printf("Adm_SetReaderPPS: Enter\n");
#endif
	cmd[0]	= 0x01;
	cmd[1]	= slotIndex == 0 ? 0x0B : 0x0D;
	cmd[2]	= (ppsLen >> 8) & 0x00FF;
	cmd[3]	= ppsLen & 0x00FF;
	memcpy(&cmd[4], pPPS, ppsLen);

	return Adm_Transmit(lun, cmd, ppsLen + 4, rspBuf, &rspBufLen);
}

ULONG
Adm_DoPPSExchangeATR(	DWORD	lun,
						DWORD   slotIndex,
						PUCHAR	pATR,
						ULONG	atrLen)
{
	MCU_ATR			mcuATR;
	MCU_ATR_RESULT	mcuATRResult;
	UCHAR			mcuATRTA1;
	MCU_ATR_PROTO	mcuATRProto = MCU_ATR_PROTO_T0;
	UCHAR			fl;
	UCHAR			dl;
	UCHAR			cardPPS[100];
	ULONG			cardPPSLen;
	ULONG			result;
#ifdef PCSC_DEBUG
printf("Adm_DoPPSExchangeATR: Enter\n");
printf("Adm_DoPPSExchangeATR: MCUAtrInit\n");
#endif
	mcuATRResult = MCUAtrInit(&mcuATR, pATR, atrLen);
	if (mcuATRResult != MCU_ATR_OK)
	{
		result = STATUS_DATA_ERROR;
		goto MCUAtrInitFailed;
	}

#ifdef PCSC_DEBUG
printf("Adm_DoPPSExchangeATR: MCUAtrGetInterfaceByte\n");
#endif
	mcuATRResult = MCUAtrGetInterfaceByte(	&mcuATR,
											1,
											MCU_ATR_INTERFACE_TA,
											&mcuATRTA1);
	if (mcuATRResult != MCU_ATR_OK)
	{
		result = STATUS_DATA_ERROR;
		goto MCUAtrGetInterfaceByteForTA1Failed;
	}
	if (mcuATRTA1 == 0x11)
	{	/* It's fixed => can't change */
		result = STATUS_SUCCESS;
		goto PPSFixedAlready;
	}

#ifdef PCSC_DEBUG
printf("Adm_DoPPSExchangeATR: MCUAtrGetNumProtocol\n");
#endif
	if (MCUAtrGetNumProtocol(&mcuATR) > 0)
	{
		mcuATRResult = MCUAtrGetProtocol(	&mcuATR,
											2,
											&mcuATRProto);
		if (mcuATRResult != MCU_ATR_OK)
		{
			result = STATUS_DATA_ERROR;
			goto MCUAtrGetProtocolFailed;
		}
	}

#ifdef PCSC_DEBUG
printf("Adm_DoPPSExchangeATR: MCUAtrGetIntegerValue\n");
#endif
	mcuATRResult = MCUAtrGetIntegerValue(	&mcuATR,
											MCU_ATR_INTEGER_VALUE_FI,
											&fl);
	if (mcuATRResult != MCU_ATR_OK)
	{
		result = STATUS_DATA_ERROR;
		goto MCUAtrGetIntegerValueForFI;
	}

#ifdef PCSC_DEBUG
printf("Adm_DoPPSExchangeATR: MCUAtrGetIntegerValue\n");
#endif
	mcuATRResult = MCUAtrGetIntegerValue(	&mcuATR,
											MCU_ATR_INTEGER_VALUE_DI,
											&dl);
	if (mcuATRResult != MCU_ATR_OK)
	{
		result = STATUS_DATA_ERROR;
		goto MCUAtrGetIntegerValueForDI;
	}

printf("Adm_DoPPSExchangeATR: PPS Fl(0x%X) Dl(0x%X) => %fb/s\n", fl, dl, Adm_PPSBitRate(fl, dl));
	if (Adm_SupportPPS(fl, dl) == 0)
	{	/* Reader Not support */
#if 0	/* Just try reader's max one */
		result = STATUS_SUCCESS;
		goto ReaderNotSupport;
#else
		fl = Adm_GetMaxSupportFl();
		dl = Adm_GetMaxSupportDl();
printf("Adm_DoPPSExchangeATR: System Max Support Fl(0x%X) Dl(0x%X) => %fb/s\n", fl, dl, Adm_PPSBitRate(fl, dl));
#endif
	}

	result = Adm_SetCardPPS(
				lun,
				slotIndex,
				mcuATRProto == MCU_ATR_PROTO_T0 ? 0 : 1,
				fl,
				dl,
				cardPPS,
				&cardPPSLen);
	if (result != STATUS_SUCCESS)
	{
		goto Adm_SetCardPPSFailed;
	}

	result = Adm_SetReaderPPS(lun, slotIndex, cardPPS, cardPPSLen);
	if (result != STATUS_SUCCESS)
	{
		goto Adm_SetReaderPPSFailed;
	}

	if ((fl == 0x9) && (dl == 0x4))
	{
		bACR38PPS_94Fix = 1;
	}
	return STATUS_SUCCESS;

/****** Error Part ******/
Adm_SetReaderPPSFailed:
Adm_SetCardPPSFailed:
#if 0	/* Just try reader's max one */
ReaderNotSupport:
#endif	/* Just try reader's max one */
MCUAtrGetIntegerValueForDI:
MCUAtrGetIntegerValueForFI:
MCUAtrGetProtocolFailed:
PPSFixedAlready:
MCUAtrGetInterfaceByteForTA1Failed:
	MCUAtrCleanUp(&mcuATR);

MCUAtrInitFailed:
	return result;
}

ULONG
Adm_DoPPSExchange(	DWORD	lun,
					DWORD   slotIndex,
					PUCHAR	pPPS,
					ULONG	ppsLen)
{
	UCHAR	cmd[100];
	UCHAR	cardPPS[100];
	ULONG	cardPPSLen;
	ULONG	rv;
#if 0
	UCHAR	cbProtocol	= pPPS[1]&0x0F;
	UCHAR	cbFl		= pPPS[2]>>4;
	UCHAR	cbDl		= pPPS[2]&0x0F;
#endif
	/* This function should be completed disabled */
#ifdef PCSC_DEBUG
ULONG i;
printf("Adm_DoPPSExchange: Enter\n");
printf("Adm_DoPPSExchange: PPS = ");
for(i=0;i<ppsLen;i++) {
  printf("%02X ", pPPS[i]);
}
printf("\n");
#endif

//	return STATUS_SUCCESS;

	cmd[0]	= 0x01;
	cmd[1]	= slotIndex == 0 ? 0x0A : 0x0C;
	cmd[2]	= (ppsLen >> 8) & 0x00FF;
	cmd[3]	= ppsLen & 0x00FF;
	memcpy(&cmd[4], pPPS, ppsLen);
	rv = Adm_Transmit(lun, cmd, ppsLen + 4, cardPPS, &cardPPSLen);
	if (rv != STATUS_SUCCESS)
	{
		goto Adm_TransmitFailed;
	}

	if (memcmp(&pPPS[0], &cardPPS[0], 4) != 0)
	{
		if ((pPPS[0] != cardPPS[0]) ||
			((pPPS[1] & 0x0F) != (cardPPS[1] & 0x0F)) ||
			!(cardPPS[1] & 0x80))
		{
			rv = STATUS_UNSUCCESSFUL;
			goto PPSExchangeFailed;
		}
	}
/*
	rv = Adm_SetCardPPS(
				lun,
				cbProtocol,
				cbFl,
				cbDl,
				cardPPS,
				&cardPPSLen);

#ifdef PCSC_DEBUG
	printf("Adm_DoPPSExchange: SetCardPPS rv = %x\n",rv);
#endif
*/
	rv = Adm_SetReaderPPS(lun, slotIndex, cardPPS, cardPPSLen);
	if (rv != STATUS_SUCCESS)
	{
		goto Adm_SetReaderPPSFailed;
	}

	return STATUS_SUCCESS;

/****** Error Part ******/
Adm_SetReaderPPSFailed:
PPSExchangeFailed:
Adm_TransmitFailed:
	return rv;
}

double
Adm_PPSBitRate(	UCHAR	fl,
				UCHAR	dl)
{
	double	fiValue;
	double	diValue;

	fiValue = MCUAtrDecodeFI(fl);
	diValue = MCUAtrDecodeDI(dl);
	return ((diValue / fiValue) * READER_FREQUENCY);
}

int	/* 1 -- Support, 0 -- Not Support */
Adm_SupportPPS(	UCHAR	fl,
				UCHAR	dl)
{
	long	maxBitRate;
	long	reqBitRate;

	maxBitRate = (long) Adm_PPSBitRate(Adm_GetMaxSupportFl(),
										Adm_GetMaxSupportDl());
	reqBitRate = (long) Adm_PPSBitRate(fl, dl);

#ifdef PCSC_DEBUG
	printf("%s: maxBitRate = %f, reqBitRate = %f\n",
	__FILE__,maxBitRate,reqBitRate);
#endif
	return (reqBitRate <= maxBitRate ? 1 : 0);
}

UCHAR
Adm_GetMaxSupportFl()
{
	return READER_MAX_SUPPORT_FI;
}

UCHAR
Adm_GetMaxSupportDl()
{
	return READER_MAX_SUPPORT_DI;
}

ULONG
Adm_SetOption(DWORD Lun, UCHAR option)
{
  ULONG rv;
  ULONG ulSize = 0;
  UCHAR setOption[20];
  UCHAR ucResponse[MAX_BUFFER_SIZE];
#ifdef PCSC_DEBUG
printf("Adm_SetOption: Enter\n");
#endif
  setOption[0]	= 0x01;
  setOption[1]	= 0x07;
  setOption[2]	= 0x00;
  setOption[3]	= 0x01;
  setOption[4]	= option;
  rv = Adm_Transmit( Lun, setOption, 5, ucResponse, &ulSize );
  return rv;
}

ULONG
Adm_Control(DWORD	Lun,
			PUCHAR	TxBuffer,
			DWORD	TxLength,
			PUCHAR	RxBuffer,
			PDWORD	RxLength)
{
	PACR38_CONTEXT	pContext;
	PACR38_IO_CTRL	pIOCtrl;
	ULONG			result;
#ifdef PCSC_DEBUG
printf("Adm_Control: Enter\n");
#endif
	pContext = Adm_GetContext(Lun);
	if (pContext == NULL)
	{
		result = STATUS_UNSUCCESSFUL;
		goto NoContextFound;
	}

	pIOCtrl = (PACR38_IO_CTRL)TxBuffer;
	switch (pIOCtrl->type)
	{
	case ACR38_IO_CTRL_SET_CARD_TYPE:
		result = Adm_Control_SetCardType(	pContext,
											TxBuffer,
											TxLength,
											RxBuffer,
											RxLength);
		break;

	default:
		printf(	"Adm_Control: Unknown I/O Ctrl Type(0x%X)\n",
				pIOCtrl->type);
		result = STATUS_UNSUCCESSFUL;
		goto UnknownIoCtrlType;
	}
	return result;

/****** Error Part ******/
UnknownIoCtrlType:
NoContextFound:
	return result;
}

ULONG
Adm_Control_SetCardType(PACR38_CONTEXT	pContext,
						PUCHAR			TxBuffer,
						DWORD			TxLength,
						PUCHAR			RxBuffer,
						PDWORD			RxLength)
{
	PACR38_IO_CTRL				pIOCtrl;

	pIOCtrl = (PACR38_IO_CTRL)TxBuffer;
	pContext->type = pIOCtrl->setCardTypeData.type;
	*RxLength = 0;
	return STATUS_SUCCESS;
}

PACR38_CONTEXT
Adm_GetContext(DWORD	Lun)
{
	PACR38_CONTEXT	pContext = NULL;
	int				i;

	for (i = 0; i < NUM_ACR38_CONTEXT; i++)
	{
		if ((gACR38Context[i].bUsed == 1) &&
			(gACR38Context[i].Lun == Lun))
		{
			pContext = &gACR38Context[i];
			break;
		}
	}
	return pContext;
}

// Get number of slots
UCHAR Adm_GetNumSlots(DWORD readerIndex)
{
    UCHAR numSlots = 1;

    if (GetReaderId(readerIndex) == ACS_ACR38U_SAM)
        numSlots = 2;
        
    return numSlots;
}
