/*
 *  GCCmds.c
 *  $Id: GCCmds.c,v 1.43 2012/04/08 18:38:19 rousseau Exp $
 *  ifd-GemPC
 *
 *  Created by Jean-Luc Giraud on Sat Oct 20 2001.
 *  Updated by Ludovic Rousseau, Oct 2001.
 *
 *  Copyright (c) 2001-2004 Jean-Luc Giraud, Ludovic Rousseau
 *
 *  License: this code is under a double licence COPYING.BSD and COPYING.GPL
 *
 */

#include <stdio.h>
#include <string.h>

#include "gempc_ifdhandler.h"

#include "Config.h"
#include "GemCore.h"
#include "GCdebug.h"
#include "GCUtils.h"
#include "GCCmds.h"
#include "GCTransport.h"

#if GEMPC==410
#include "../GemPC410/gbpserial.h"
#endif

#if GEMPC==430
#include "../GemPC430/usbserial.h"
#endif

#ifndef GEMPC
#error "GEMPC not defined: set it to 410 or 430"
#endif

/* p. 18 Set Mode */
#define IFD_CMD_MODE_SET1       0x01
#define IFD_CMD_MODE_SET2       0x00

/* p. 17 Configure SIO line */
#define IFD_CMD_SIO_SET         0x0A

/* p. 21 Restart, p. 22 Restart and Run Specified Application */
#define IFD_CMD_RESTART         0x0C

/* p. 24 Set Reader in Halt Mode */
#define IDF_CMD_HALT            0x0E

/* p. 26 Power Down */
#define IFD_CMD_ICC_POWER_DOWN  0x11

/* p. 34 Power Up - Asynchronous Cards, p. 36 Change Card Communication
 * Parameters - Asynchronous Cards, p. 45 Power UP - EMV Compliant,
 * p. 51 Change Transparent Mode Parameters, p. 53 Power Up - Transparent
 * Mode,
 * p. 57 Power Up - Synchronous Cards */
#define IFD_CMD_ICC_POWER_UP    0x12

/* options for IFD_CMD_ICC_POWER_UP command, p. 34
 * Class A: Vcc for card is 5V */
#define IFD_CMD_ICC_POWER_UP_OPT_5V 0x01
/* Class B: Vcc for card is 3V */
#define IFD_CMD_ICC_POWER_UP_OPT_3V 0x02
/* Class AB: Vcc for card is 5V or 3V */
#define IFD_CMD_ICC_POWER_UP_OPT_5V_3V (IFD_CMD_ICC_POWER_UP_OPT_5V|IFD_CMD_ICC_POWER_UP_OPT_3V)
/* Reset and no PPS management. The reader stays at 9600 baud if the card is
 * in negotiable mode */
#define IFD_CMD_ICC_POWER_UP_OPT_NO_PPS_MNGT 0x10

/* Reset and no PPS management. Reader switches to maximum speed and T=1 if
 * card in negotiable mode */
#define IFD_CMD_ICC_POWER_UP_OPT_AUTO_PPS_MNGT 0x20

/* p. 38 ISO Output - Asynchronous Card,
 * p. 58 Read Data From Synchronous Card (ISO Out) */
#define IFD_CMD_ICC_ISO_OUT     0x13

/* p. 39 ISO Input - Asynchronous Card,
 * p. 59 Send Data to Synchronous Card (ISO In) */
#define IFD_CMD_ICC_ISO_IN      0x14

/* p. 40 Exchange APDU - Asynchronous Card,
 * p. 46 Exchange APDU - EMV Compliant,
 * p. 54 Exchange Block - Transparent Mode,
 * p. 60 Exchange with Synchronous Card (ADPU) */
#define IFD_CMD_ICC_APDU        0x15

#define IFD_CMD_ICC_SYNCHRONE   0x16

#define IFD_CMD_ICC_DISPATCHER  "\x17\x01"

/* p. 27 Define Main Card Type and Card Presence Detection */
#define IFD_CMD_ICC_DEFINE_TYPE 0x17

/* p. 43 Card Status - Asynchronous Card,
 * p. 49 Card Status - EMV Compliant,
 * p. 55 Card Status - Transparent Mode,
 * p. 61 Card Status - Synchronous Card */
#define IFD_CMD_ICC_STATUS      0x17

/* p. 31 Directory,
 * p. 32 Set Operating Mode */
#define IFD_CMD_DIR1            0x17
#define IFD_CMD_DIR2            0x00
#define IFD_EMV_MODE			0x45
#define IFD_GENERIC_MODE		0x47

#define IFD_CMD_MOD_POWER_DOWN  0x19

/* p. 34 Power Up - Asynchronous Cards,
 * p. 36 Change Card Communication Parameters - Asynchronous Cards,
 * p. 45 Power UP - EMV Compliant,
 * p. 51 Change Transparent Mode Parameters,
 * p. 53 Power Up - Transparent Mode,
 * p. 57 Power Up - Synchronous Cards */
#define IFD_CMD_MOD_POWER_UP    0x1A

/* p. 38 ISO Output - Asynchronous Card,
 * p. 58 Read Data From Synchronous Card (ISO Out) */
#define IFD_CMD_MOD_ISO_OUT     0x1B

/* p. 39 ISO Input - Asynchronous Card,
 * p. 59 Send Data to Synchronous Card (ISO In) */
#define IFD_CMD_MOD_ISO_IN      0x1C

/* p. 40 Exchange APDU - Asynchronous Card,
 * p. 46 Exchange APDU - EMV Compliant,
 * p. 54 Exchange Block - Transparent Mode,
 * p. 60 Exchange with Synchronous Card (ADPU) */
#define IFD_CMD_MOD_APDU        0x1D

#define IFD_CMD_MOD_SYNCHRONE   0x1E

/* p. Define Type and Select Auxiliary Card */
#define IFD_CMD_MOD_DEFINE_TYPE 0x1F

#define IFD_CMD_MOD_DISPATCHER  "\x1F\x01"

/* p. 43 Card Status - Asynchronous Card,
 * p. 49 Card Status - EMV Compliant,
 * p. 55 Card Status - Transparent Mode,
 * p. 61 Card Status - Synchronous Card */
#define IFD_CMD_MOD_STATUS      0x1F

/* p. 20 Read Firmware Version,
 * p. 63 Read Memory */
#define IFD_CMD_MEM_RD          0x22

/* p. 19 Set Delay,
 * p. 64 Write Memory */
#define IFD_CMD_MEM_WR          0x23

/* p. 69 Read CPU Port */
#define IFD_CMD_CPU_RD          0x24

/* p. 70 Write CPU Port */
#define IFD_CMD_CPU_WR          0x25

/* p. 66 Erase Flash Memory */
#define IFD_CMD_MEM_ERASE       0x26

/* p. 68 Select External Memory Page */
#define IFD_CMD_MEM_SELECT      0x27

#define IFD_CMD_LCD_OFF         0x29

/* p. 72 Init the LCD */
#define IFD_CMD_LCD_ON          0x2A

/* p. 73 Display Character String */
#define IFD_CMD_LCD_STRING      0x2B

/* p. 74 Display Character */
#define IFD_CMD_LCD_CHAR        0x2C

/* p. 75 Send LCD Command */
#define IFD_CMD_LCD_CMD         0x2D

/* p. 77 Set Key Press Timeout */
#define IFD_CMD_KEY_TIMEOUT     0x32

/* p. 78 Sound Buzzer */
#define IFD_CMD_SOUND_BUZZER    0x33

/* p. 80 Read Date and Time */
#define IFD_CMD_RTC_RD          0x3A

/* p. 81 Update Date and Time */
#define IFD_CMD_RTC_WR          0x3B

#define IFD_CMD_IO_RD           0x42

#define IFD_CMD_IO_WR           0x43

#define IFD_CMD_IO_BIT_WR       0x44

#define IFD_TYP_VERSION 0x05
#define IFD_ADD_VERSION_HIGH 0x3F
#define IFD_ADD_VERSION_LOW 0xE0


ifd_t GCCmdPowerDown(const DWORD lun)
{
	UCHAR cmd[] = { IFD_CMD_ICC_POWER_DOWN };
	ifd_t rv;
	gcore_t gc_rv;

	DEBUG_INFO("");

	/* PowerDown the card */
	rv = GCMakeCommand(lun, sizeof(cmd), cmd, NULL, NULL, &gc_rv);
	GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

	if (rv != IFD_SUCCESS)
		return rv;

	if (gc_rv != GCORE_OK)
		/* There is a problem in power down */
		return IFD_ERROR_POWER_ACTION;

	return IFD_SUCCESS;
} /* GCCmdPowerDown */


ifd_t GCCmdPowerUp(const DWORD lun, PDWORD nlength, UCHAR buffer[])
{
#ifdef AUTOMATIC_PPS
	UCHAR cmd_auto_pps[] = { IFD_CMD_ICC_POWER_UP,
		IFD_CMD_ICC_POWER_UP_OPT_5V_3V | IFD_CMD_ICC_POWER_UP_OPT_AUTO_PPS_MNGT
		};
#endif
	UCHAR cmd_no_pps[] = { IFD_CMD_ICC_POWER_UP,
		IFD_CMD_ICC_POWER_UP_OPT_5V_3V | IFD_CMD_ICC_POWER_UP_OPT_NO_PPS_MNGT };
	UCHAR cmd_emv[] = { IFD_CMD_ICC_POWER_UP };
	UCHAR cmd_set_operating_mode[] = { IFD_CMD_DIR1, IFD_CMD_DIR2,
		IFD_GENERIC_MODE };

	ifd_t rv;
	gcore_t gc_rv;

	DEBUG_INFO("");

#ifdef AUTOMATIC_PPS
	/* PowerUp the card with automatic PPS negociation */
	rv = GCMakeCommand(lun, sizeof(cmd_auto_pps), cmd_auto_pps, nlength,
		buffer, &gc_rv);
	GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

	if (rv != IFD_SUCCESS)
		return rv;

	/* There is a problem in power up: try with NO PPS */
	if ((gc_rv != GCORE_OK) && (gc_rv != GCORE_WRONG_TCK))
#endif
	{
		rv = GCMakeCommand(lun, sizeof(cmd_no_pps), cmd_no_pps, nlength,
			buffer, &gc_rv);
		GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

		if (rv != IFD_SUCCESS)
			return rv;

		/* There is a problem in power up: try to use EMV mode */
		if ((gc_rv != GCORE_OK) && (gc_rv != GCORE_WRONG_TCK))
		{
			/* power up: will return a 0xA0 error or EMV ok */
			rv = GCMakeCommand(lun, sizeof(cmd_emv), cmd_emv, nlength,
				buffer, &gc_rv);
			GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

			/* If the card is NOT EMV we continue */
			if (gc_rv != GCORE_OK)
			{
				/* set mode to ISO */
				rv = GCMakeCommand(lun, sizeof(cmd_set_operating_mode),
					cmd_set_operating_mode, nlength, buffer, &gc_rv);
				GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

				/* new power up */
				rv = GCMakeCommand(lun, sizeof(cmd_no_pps), cmd_no_pps, nlength,
					buffer, &gc_rv);
				GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);
			}
			else
				/* the card is EMV */
				IFDSetEmv(lun);

			if (rv != IFD_SUCCESS)
				return rv;
		}

		/* There is a problem in power up */
		if ((gc_rv != GCORE_OK) && (gc_rv != GCORE_WRONG_TCK))
			return IFD_ERROR_POWER_ACTION;
	}

	return IFD_SUCCESS;
} /* GCCmdPowerUp */


ifd_t GCCmdGetOSVersion(const DWORD lun, PDWORD length, UCHAR buffer[])
{
 /*
	 * GCR 400      (OROS): 'OROS-R2.24RM    ' (without ')
	 *           (GemCore): incorrect number of arguments (error 03)
	 *
	 * GCR 410      (OROS): OROS-R2.99-R1.10
	 *  v1.10    (GemCore): GemCore-R1.10-0M
	 *
	 * GCR 410P     (OROS): OROS-R2.99-R1.11
	 *  v1.118   (GemCore): 'GemCore-R1.11-8 ' (without ')
	 *
	 * GemPC 410    (OROS): OROS-R2.99-R1.21
	 *  v1.21    (GemCore): GemCore-R1.21-GM
	 *
	 * GemPC 413    (OROS): OROS-R2.99-R1.32
	 *  v1.32    (GemCore): GemCore-R1.32-GK
	 */

	UCHAR cmd[] = { IFD_CMD_MEM_RD, IFD_TYP_VERSION, IFD_ADD_VERSION_HIGH,
		IFD_ADD_VERSION_LOW, IFD_LEN_VERSION };
	gcore_t gc_rv;

	DEBUG_INFO("");

	if (*length < IFD_LEN_VERSION)
	{
		DEBUG_CRITICAL("buffer too small");
		return IFD_COMMUNICATION_ERROR;
	}

	/* clear the buffer to be sure it is \0 terminated */
	memset(buffer, 0, *length);
	/* decrease length by 1 to be sure their is room for the final \0 */
	(*length)--;
	GCMakeCommand(lun, sizeof(cmd), cmd, length, buffer, &gc_rv);
	GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

	if (gc_rv != GCORE_OK)
		return IFD_ERROR_POWER_ACTION;

	return IFD_SUCCESS;
} /* GCCmdGetOSVersion */


ifd_t GCCmdConfigureSIOLine(const DWORD lun, const int baudrate)
{
	UCHAR cmd[] = { IFD_CMD_SIO_SET, 0x00 };
	ifd_t rv;
	gcore_t gc_rv;

	DEBUG_INFO("");

	switch (baudrate)
	{
		case 9600:
			cmd[1] = 0x04;
			break;

		case 38400:
			cmd[1] = 0x02;
			break;

		default:
			DEBUG_CRITICAL2("wrong baudrate %d", baudrate);
			return IFD_COMMUNICATION_ERROR;
	}

	rv = GCMakeCommand(lun, sizeof(cmd), cmd, NULL, NULL, &gc_rv);
	GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

	if (rv != IFD_SUCCESS || gc_rv != GCORE_OK)
		return IFD_COMMUNICATION_ERROR;

	return rv;
} /* GCCmdConfigureSIOLine */


ifd_t GCCmdCardStatus(const DWORD lun, UCHAR response[], PDWORD length)
{
	UCHAR cmd[] = { IFD_CMD_ICC_STATUS };
	ifd_t rv;
	gcore_t gc_rv;

	DEBUG_PERIODIC("");

	rv = GCMakeCommand(lun, sizeof(cmd), cmd, length, response, &gc_rv);
	GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

	if (rv != IFD_SUCCESS || gc_rv != GCORE_OK)
		return IFD_COMMUNICATION_ERROR;

	return rv;
} /* GCCmdCardStatus */


ifd_t GCCmdSetMode(const DWORD lun, const int mode)
{
	UCHAR cmd[] = { IFD_CMD_MODE_SET1, IFD_CMD_MODE_SET2, mode};
	ifd_t rv;
	gcore_t gc_rv;

	DEBUG_INFO("");

	/* success by default */
	rv = IFD_SUCCESS;

	switch (mode)
	{
		case IFD_MODE_ROSNOTLP:
			rv = GCMakeCommand(lun, sizeof(cmd), cmd, NULL, NULL, &gc_rv);
			GCGemCoreError(gc_rv, __FILE__, __LINE__, __FUNCTION__);

			/* The command in unknown if ROS is disabled */
			if ((gc_rv != GCORE_UNKNOWN_CMD) && (gc_rv != GCORE_OK))
				rv = IFD_COMMUNICATION_ERROR;
			break;

		default:
			DEBUG_CRITICAL2("Unknown mode: %02X", mode);
			break;
	}

	return rv;
} /* GCCmdSetMode */


ifd_t GCMakeCommand(const DWORD lun, const DWORD nLengthIn,
	const UCHAR pcBufferCmd[], PDWORD pnLengthOut, UCHAR pcBufferOut[],
	gcore_t *response)
{
	UCHAR rv;
	DWORD lengthout;
	UCHAR buffer[GC_TR_BUF_SIZE];

	/* no error by default */
	*response = GCORE_OK;

	lengthout = sizeof(buffer);
	rv = GCSendCommand(lun, nLengthIn, pcBufferCmd, &lengthout, buffer);

	/* transport level error */
	if (rv != STATUS_SUCCESS)
		return IFD_COMMUNICATION_ERROR;

	/* the reader response was empty */
	if (0 == lengthout)
		return IFD_COMMUNICATION_ERROR;

	/* GemCore level error */
	*response = buffer[GC_STATUS_OFFSET];

	/* shift command anwser (remove GemCore status byte) */
	if (pcBufferOut)
		memcpy(pcBufferOut, buffer+1, lengthout-1);

	/* status byte removed */
	if (pnLengthOut)
		*pnLengthOut = lengthout-1;

	return IFD_SUCCESS;
} /* GCMakeCommand */

UCHAR GCGemCoreError(const UCHAR rv, const char *file, const int line, const char *function)
{
	const char *text = NULL;
	char log_level = PCSC_LOG_ERROR;

	/* what is the GemCore status? */
	switch (rv)
	{
		/* normal error code */
		case GCORE_OK:
			break;

		case GCORE_UNKNOWN_CMD:
			text = "Unknown GemCore command";
			break;

		case GCORE_IMPOS_OPERATION:
			text = "Operation impossible with this driver";
			break;

		case GCORE_INC_NUMBER_ARG:
			text = "Incorrect number of arguments";
			break;

		case GCORE_RESET_ERROR:
			text = "The first byte of the response (TS) is not valid";
			break;

		case GCORE_POWERED_DOWN:
			text = "Card powered down. Power up first";
			break;

		case GCORE_MORE_DATA:
			text = "Incorrect number of parameters";
			break;

		case GCORE_WRONG_TCK:
			text = "Wrong ATR TCK";
			break;

		case GCORE_ATR:
			text = "Error in card reset response";
			break;

		case GCORE_CARD_PROT_ERR:
			text = "Card protocol error";
			break;

		case GCORE_CARD_MUTE:
			text = "Card is mute";
			break;

		case GCORE_PARITY_ERROR:
			text = "Parity error during exchange";
			break;

		case GCORE_CARD_T1_ABORT:
			text = "Card has aborted chaining (T=1)";
			break;

		case GCORE_READER_T1_ABORT:
			text = "Reader has aborted chaining (T=1)";
			break;

		case GCORE_RESYNC:
			text = "RESYNCH successfully performed by GemCore";
			break;

		case GCORE_PTS:
			text = "Protocol Type Selection (PTS) error";
			break;

		case GCORE_CARD_YON:
			text = "Card and reader in EMV mode";
			break;

		case GCORE_INVALID_PROC_BYTE:
			text = "The card just sent an invalid Procedure Byte";
			break;

		/* normal error code */
		case GCORE_CARD_EXC_INT:
			text = "Card interrupted the exchange after SW1";
			log_level = PCSC_LOG_INFO;
			break;

		/* normal error code */
		case GCORE_NOT_9000:
			text = "\"Error\" returned by the card (SW is not 9000)";
			log_level = PCSC_LOG_INFO;
			break;

		case GCORE_CARD_REMOVED:
			text = "Card removed during execution of a command";
			break;

		case GCORE_CARD_MISSING:
			text = "Card missing";
			break;

		default:
			log_msg(PCSC_LOG_ERROR,
				"%s:%d %s Unknown or undocumented error: 0x%02X",
				file, line, function, rv);
			break;
	}

	if (text)
		log_msg(log_level, "%s:%d %s %s", file, line, function, text);

	return rv;
} /* GCGemCoreError */

