/*****************************************************************
/
/    ifdhandler for Gemplus Gemcore readers
/    Copyright (C) 2001  Ludovic Rousseau <ludovic.rousseau@free.fr>
/
/    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 2 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.
/
/    You should have received a copy of the GNU General Public License
/    along with this program; if not, write to the Free Software
/    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
/
/ File   :   ifdhandler.c
/ Author :   David Corcoran <corcoran@linuxnet.com>
/              for the empty functions
/            Ludovic Rousseau <ludovic.rousseau@free.fr>
/              for debug and migration for pcsc API 1.0 to 2.0
/            Gemplus <http://www.gemplus.[fr|com]>
/              A great part of the code is extracted from the original
/              ifdhandler.c  (C) by 1991 - 2001 Gemplus
/
/ Date   :   September 2001
/ Purpose:   This provides reader specific low-level calls.
/            See http://www.linuxnet.com for more information.
/
/
******************************************************************/

#include <pcsclite.h>
#include <ifdhandler.h>

#include <string.h>
#include <stdlib.h>

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#include <errno.h>
#endif

#include "gemplus.h"
#include "gemcom.h"
#include "or3gll.h"
#include "debug.h"
#include "or3utils.h"
#include "gemgcr.h"
#include "ifd2gem.h"
#include "or3icc.h"
#include "apduspli.h"
#include "apdubuil.h"
#include "gemcard.h"
#if (defined WIN32) || (defined G_UNIX) || (defined G_OS2)
#include "gemansi.h"
#endif

/*
 * The GCR 410 (GemPC 410) returns GemCore-R1.21-GM
 * The GCR 400 returns nothing
 */
#define IFD_OS_VERSION "GemCore-R1."

/*
 * Will only use GemCore 1.21 commands if defined
 */
//#define GEMCORE_121

/*
 * Constants
 */
char *vendor_name = "Gemplus";
char *ifd_type = "Gemcore Based Reader";
char *ifd_serial = "Gemcore Serial No. Not used";

/*
 * Global variables
 */
DEVICE_CAPABILITIES Device;
ICC_STATE Icc;
PROTOCOL_OPTIONS ProtocolOptions;

/*
 Private varaibles.
 Only used in EMV management.
*/

/*EMV Behaviour Gemplus 2001*/
static unsigned char EMVSupported=FALSE ; /*We suppose that the reader doesn't have an EMV behaviour.*/
/*EMV Behaviour Gemplus 2001*/

#ifdef HAVE_PTHREAD_H
static pthread_mutex_t ifdh_status_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif




/**************************************************************************
 *
 *		 IFDHCreateChannel
 *
 *************************************************************************/
RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
{
	/*
	 * Lun - Logical Unit Number, use this for multiple card slots or
	 * multiple readers. 0xXXXXYYYY -  XXXX multiple readers, YYYY multiple
	 * slots. The resource manager will set these automatically.  By default 
	 * the resource manager loads a new instance of the driver so if your
	 * reader does not have more than one smartcard slot then ignore the Lun
	 * in all the functions. Future versions of PC/SC might support loading
	 * multiple readers through one instance of the driver in which XXXX
	 * would be important to implement if you want this. 
	 */

	/*
	 * Channel - Channel ID.  This is denoted by the following: 0x000001 -
	 * /dev/pcsc/1 0x000002 - /dev/pcsc/2 0x000003 - /dev/pcsc/3
	 * 
	 * USB readers may choose to ignore this parameter and query the bus for 
	 * the particular reader. 
	 */

	/*
	 * This function is required to open a communications channel to the
	 * port listed by Channel.  For example, the first serial reader on COM1
	 * would link to /dev/pcsc/1 which would be a sym link to /dev/ttyS0 on
	 * some machines This is used to help with intermachine independance.
	 * 
	 * Once the channel is opened the reader must be in a state in which it
	 * is possible to query IFDHICCPresence() for card status.
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_COMMUNICATION_ERROR 
	 */

	RESPONSECODE response = IFD_SUCCESS;

	int iPort = G_COM1;

	unsigned short Unibble;		// Upper 16bits of DWORD. (DDDD)
	unsigned short Lnibble;		// Lower 16bits of DWORD. (CCCC)

	WORD16 major_ver = 0, minor_ver = 0;
	char *tmp = NULL;

	WORD16 os_length = HOR3GLL_OS_STRING_SIZE;
	char os_string[os_length];

	WORD16 rlen;

	WORD8 rbuff[HOR3GLL_BUFFER_SIZE];

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	Unibble = Channel >> 16;	// Shift right 16 bits.
	Lnibble = Channel & 0xFFFF;	// Shift left and subtract.

	switch (Unibble)
	{

	case 0x01:					// Serial Port.

		switch (Lnibble)
		{

		case 0x3F8:
			iPort = G_COM1;
			break;

		case 0x2F8:
			iPort = G_COM2;
			break;

		case 0x3E8:
			iPort = G_COM3;
			break;

		case 0x2E8:
			iPort = G_COM4;
			break;

		default:
			response = IFD_NOT_SUPPORTED;
			goto IFDHCreateChannel_return;
		}

		break;

	default:
		// case 0xFy where y is vendor defined 0 - F is NOT implemented
		// 0x02: Parellel, 0x04: PS2, 0x08: SCSI, 0x10: IDE
		// 0x20: USB Port
		response = IFD_NOT_SUPPORTED;
		goto IFDHCreateChannel_return;
	}

	DEBUG_MSG("IFDHCreateChannel: iPort=%d", iPort);

	response = G_Oros3OpenComm(iPort, 9600);
	if (response < 0)
	{
		response = IFD_COMMUNICATION_ERROR;
		goto IFDHCreateChannel_return;
	}

	os_length = HOR3GLL_OS_STRING_SIZE;

	response = G_Oros3String(&os_length, os_string);
	if (response < G_OK)
	{
		G_Oros3CloseComm();
		response = IFD_COMMUNICATION_ERROR;
		goto IFDHCreateChannel_return;
	}
	os_string[os_length] = '\0';

	if (_fstrncmp((const char G_FAR *) os_string + 1, IFD_OS_VERSION,
			_fstrlen(IFD_OS_VERSION)) != G_OK)
	{
		G_Oros3CloseComm();
		response = IFD_NOT_SUPPORTED;
		goto IFDHCreateChannel_return;
	}

	tmp = (char G_FAR *) _fstrstr(os_string + 1, IFD_OS_VERSION);
	if (tmp != NULL)
	{
		major_ver = atoi(tmp + _fstrlen(IFD_OS_VERSION));
		minor_ver = atoi(tmp + _fstrlen(IFD_OS_VERSION) + 2);
	}

	/*EMV Behaviour Gemplus 2001*/
	/*Now we got the Firmware version. We test it*/
	/*We only check the minor version.We suppose that the reader is only a GemCore Reader*/
	if(major_ver>= 20) /*Only 1.20 and above manage EMV behaviour*/
	{
	 EMVSupported= TRUE ;
	}
   /*EMV Behaviour Gemplus 2001*/
		
	/**/
	memset(&Device, 0, sizeof(Device));
	memset(&Icc, 0, sizeof(Icc));

	Device.IFD_Version = major_ver << 24;
	Device.IFD_Version |= (minor_ver << 16);
	Device.IFD_Version |= 0x0001;	// Build.. to be added later.

	// Should we change baud rate of reader comm-n here..
	if (G_ChangeIFDBaudRate(iPort, 38400) != G_OK)
	{
		G_Oros3CloseComm();
		DEBUG_MSG("38400 baud setting problem. exiting");
		response = IFD_COMMUNICATION_ERROR;
		goto IFDHCreateChannel_return;
	}
	// Removes TLP Compatibility here..
	rlen = HOR3GLL_BUFFER_SIZE;
	response = G_Oros3SetMode(HOR3GLL_LOW_TIME, 0x00, &rlen, rbuff);
	if (response < G_OK)
	{
		G_Oros3CloseComm();
		response = IFD_COMMUNICATION_ERROR;
		goto IFDHCreateChannel_return;
	}

	Device.Vendor_Name = vendor_name;
	Device.IFD_Type = ifd_type;
	Device.IFD_Serial = ifd_serial;
	Device.IFD_Channel_ID = Channel;
	Device.Asynch_Supported = 0x00000003;	// Both T=0/T=1 Supported
	Device.Default_Clock = 3680;
	Device.Max_Clock = 3680;
	Device.Default_Data_Rate = 9600;
	Device.Max_Data_Rate = 115000;

	Device.Synch_Supported = 0;
	Device.Power_Mgmt = 1;		// IFD Supports powerdown if ICC present;

	DEBUG_MSG("Vendor Name = %s", Device.Vendor_Name);
	DEBUG_MSG("IFD Type = %s", Device.IFD_Type);
	DEBUG_MSG("IFD Version = 0x%04X", Device.IFD_Version);
	DEBUG_MSG("IFD Serial = %s", Device.IFD_Serial);
	DEBUG_MSG("IFD Channel ID = 0x%04x", Device.IFD_Channel_ID);

	// All Others not supported..

IFDHCreateChannel_return:
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHCreateChannel */


/**************************************************************************
 *
 *		IFDHCloseChannel
 *
 *************************************************************************/
RESPONSECODE IFDHCloseChannel(DWORD Lun)
{
	/*
	 * This function should close the reader communication channel for the
	 * particular reader.  Prior to closing the communication channel the
	 * reader should make sure the card is powered down and the terminal is
	 * also powered down.
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_COMMUNICATION_ERROR 
	 */

	RESPONSECODE response = IFD_SUCCESS;

	DEBUG_MSG("IFDHCloseChannel");

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	G_Oros3SIOConfigureNewBaudRate(9600);
	DEBUG_MSG("IFD Handler: IFDHCloseChannel called");
	response = G_Oros3CloseComm();
	if (response < 0)
	{
		DEBUG_MSG("IFDHCloseChannel: IFD_COMMUNICATION_ERROR");
		response = IFD_COMMUNICATION_ERROR;
	}

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHCloseChannel */


/**************************************************************************
 *
 *		IFDHGetCapabilities
 *
 *************************************************************************/
RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag,
	PDWORD Length, PUCHAR Value)
{
	/*
	 * This function should get the slot/card capabilities for a particular
	 * slot/card specified by Lun.  Again, if you have only 1 card slot and
	 * don't mind loading a new driver for each reader then ignore Lun.
	 * 
	 * Tag - the tag for the information requested example: TAG_IFD_ATR -
	 * return the Atr and it's size (required). these tags are defined in
	 * ifdhandler.h
	 * 
	 * Length - the length of the returned data Value  - the value of the
	 * data
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_ERROR_TAG 
	 */

	RESPONSECODE response = IFD_SUCCESS;

	DEBUG_MSG("IFDHGetCapabilities: %x", Tag);

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	switch (Tag)
	{
		case TAG_IFD_ATR:
			break;

		default:
			response = IFD_ERROR_TAG;
	}
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHGetCapabilities */


/**************************************************************************
 *
 *		IFDHSetCapabilities
 *
 *************************************************************************/
RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag,
	DWORD Length, PUCHAR Value)
{

	/*
	 * This function should set the slot/card capabilities for a particular
	 * slot/card specified by Lun.  Again, if you have only 1 card slot and
	 * don't mind loading a new driver for each reader then ignore Lun.
	 * 
	 * Tag - the tag for the information needing set
	 * 
	 * Length - the length of the returned data Value  - the value of the
	 * data
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_ERROR_TAG IFD_ERROR_SET_FAILURE
	 * IFD_ERROR_VALUE_READ_ONLY 
	 */

	DEBUG_MSG("IFDHSetCapabilities: %x", Tag);

	return IFD_NOT_SUPPORTED;
} /* IFDHSetCapabilities */


/**************************************************************************
 *
 *		IFDHSetProtocolParameters
 *
 *************************************************************************/
RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol,
	UCHAR Flags, UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
{
	/*
	 * This function should set the PTS of a particular card/slot using the
	 * three PTS parameters sent
	 * 
	 * Protocol  - 0 .... 14  T=0 .... T=14 Flags     - Logical OR of
	 * possible values: IFD_NEGOTIATE_PTS1 IFD_NEGOTIATE_PTS2
	 * IFD_NEGOTIATE_PTS3 to determine which PTS values to negotiate.
	 * PTS1,PTS2,PTS3 - PTS Values.
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_ERROR_PTS_FAILURE IFD_COMMUNICATION_ERROR
	 * IFD_PROTOCOL_NOT_SUPPORTED 
	 */

	RESPONSECODE response = IFD_SUCCESS;
	INT16 nRes;
	WORD16 rlen;
	WORD8 rbuff[HOR3GLL_BUFFER_SIZE];

	DEBUG_MSG("IFD_Set_Protocol_Parameters with Protocol Type <%X>",
		Protocol);

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	// if ((ProtocolType & 0x7FFFFFFE) != 0)
	if ((Protocol != SCARD_PROTOCOL_T0) &&
		(Protocol != SCARD_PROTOCOL_T1))
	{
		response = IFD_PROTOCOL_NOT_SUPPORTED;
		goto IFDHSetProtocolParameters_exit;
	}

	// ICC.ProtocolType = ProtocolType;
	if (Protocol == SCARD_PROTOCOL_T0)
		ProtocolOptions.Protocol_Type = 0x00;
	else
		ProtocolOptions.Protocol_Type = 0x01;

	if ((Flags & 0xF0) == 0x00)	// No PPS1 !!!!
	{
		Flags = 0x10;			// At least PPS1
		PTS1 = 0x11;			// 9600 Bauds
	}

	DEBUG_MSG("PPS Power up Selection Flag: %X, PTS1: %X, PTS2: %X, PTS3: %X",
		Flags | Protocol, PTS1, PTS2, PTS3);

	// Send Power Up to Reader in order to apply new PPS parameters
	nRes = G_Oros3IccPowerUp(HOR3GLL_DEFAULT_TIME,
		ICC_VCC_5_3V,
		// ICC_VCC_5V,
		IFD_NEGOTIATE_PTS_MANUALLY,
		// ICC.SelectionFlags,
		Flags | Protocol, PTS1, PTS2, PTS3, &rlen, rbuff);

	if (nRes >= G_OK)
	{
		nRes = GE_Translate(rbuff[0]);
	}

	if (nRes < G_OK)
	{
		DEBUG_MSG("ICC PPS Management returning error");
		response = IFD_ERROR_PTS_FAILURE;
	}
	else
		// Update strucuture with new prt
		ProtocolOptions.Protocol_Type = Protocol;

IFDHSetProtocolParameters_exit:
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHSetProtocolParameters */


/**************************************************************************
 *
 *		IFDHPowerICC
 *
 *************************************************************************/
RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action,
	PUCHAR Atr, PDWORD AtrLength)
{
	/*
	 * This function controls the power and reset signals of the smartcard
	 * reader at the particular reader/slot specified by Lun.
	 * 
	 * Action - Action to be taken on the card.
	 * 
	 * IFD_POWER_UP - Power and reset the card if not done so (store the ATR 
	 * and return it and it's length).
	 * 
	 * IFD_POWER_DOWN - Power down the card if not done already
	 * (Atr/AtrLength should be zero'd)
	 * 
	 * IFD_RESET - Perform a quick reset on the card.  If the card is not
	 * powered power up the card. (Store and return the Atr/Length)
	 * 
	 * Atr - Answer to Reset of the card.  The driver is responsible for
	 * caching this value in case IFDHGetCapabilities is called requesting
	 * the ATR and it's length.  This should not exceed MAX_ATR_SIZE.
	 * 
	 * AtrLength - Length of the Atr.  This should not exceed MAX_ATR_SIZE.
	 * 
	 * Notes:
	 * 
	 * Memory cards without an ATR should return IFD_SUCCESS on reset but the 
	 * Atr should be zero'd and the length should be zero
	 * 
	 * Reset errors should return zero for the AtrLength and return
	 * IFD_ERROR_POWER_ACTION.
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_ERROR_POWER_ACTION IFD_COMMUNICATION_ERROR
	 * IFD_NOT_SUPPORTED 
	 */

	RESPONSECODE response = IFD_SUCCESS;
	WORD16 rlen = 0;
	WORD8 rbuff[HOR3GLL_BUFFER_SIZE];

	BYTE pts0, pts1, pts2, pts3;

#ifdef DEBUG
	if (Action == IFD_POWER_UP)
		DEBUG_MSG("IFDHPowerICC: IFD_POWER_UP");
	else if (Action == IFD_POWER_DOWN)
		DEBUG_MSG("IFDHPowerICC: IFD_POWER_DOWN");
	else if (Action == IFD_RESET)
		DEBUG_MSG("IFDHPowerICC: IFD_RESET");
#endif

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	pts0 = 0;
	pts1 = 0;
	pts2 = 0;
	pts3 = 0;

	if (Action == IFD_POWER_UP)
	{
		rlen = HOR3GLL_BUFFER_SIZE;

		response = G_Oros3IccPowerDown(HOR3GLL_DEFAULT_TIME, &rlen, rbuff);

		if (response >= G_OK)
		{
			response = GE_Translate(rbuff[0]);
			DEBUG_MSG("status: %x, response: %d", rbuff[0], response);
		}

		if (response < G_OK)
		{
			DEBUG_MSG("ICC Power up-dn returing error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}

		rlen = HOR3GLL_BUFFER_SIZE;

		/*
		if (ICC.SelectionFlags !=IFD_DEFAULT_MODE)
			if (ICC.SelectionFlags !=0 )
			//--2001 Modif
			{
				ptsmode = IFD_NEGOTIATE_PTS_MANUALLY;
				pts0 = ICC.SelectionFlags;
				pts1 = ICC.PTS1;
				pts2 = ICC.PTS2;
				pts3 = ICC.PTS3;
			}
			else 
				ptsmode = pts0 = pts1 = pts2 = pts3 = 0;
		
		if (pts0 > 0x07)
			pts0 &= 0x07;
		*/
		
		/*==== EMV Behaviour Gemplus 2001*/
		
		/* Ask to the reader in what mode it is set ? */
		response= G_Oros3Exchange(HOR3GLL_DEFAULT_TIME, 3, "\x17\x00\x00", &rlen, rbuff);
		
		/* Test status*/
		if (response < G_OK)
		{
			DEBUG_MSG("ICC Get statut returning error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}
		
		rlen = HOR3GLL_BUFFER_SIZE;
		/*Parse */
		if(rbuff[1]== 0x45)/*EMV Mode ?*/
		{
	    /*EMV PowerUp*/
		response = G_Oros3Exchange(HOR3GLL_DEFAULT_TIME, 1, "\x12", &rlen, rbuff);
		}
		else/*ISO Mode*/
		{
		/*ISO Power UP with Class selection and no automatic PTS*/
		 response = G_Oros3IccPowerUp(HOR3GLL_DEFAULT_TIME,
			ICC_VCC_5_3V,
			// ICC_VCC_5V,
			IFD_DEFAULT_MODE, pts0, pts1, pts2, pts3, &rlen, rbuff);
		}
			
     	/*==== EMV Behaviour Gemplus 2001*/
		

		if (response >= G_OK)
			response = GE_Translate(rbuff[0]);

		if (response < G_OK)
		{
			DEBUG_MSG("ICC Power up returing error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}
		
		
		/* set the ATR */
		memset(Icc.ATR, 0, sizeof(Icc.ATR));
		memcpy(Icc.ATR, rbuff + 1, rlen - 1);

		*AtrLength = rlen - 1;
		memcpy(Atr, Icc.ATR, *AtrLength);

		/*
		 * l = 1; while (ICC.ATR[l] & 0x80) { offset = 0; for(k = 0x10; k >
		 * 0;  k <<=1) { if ( ICC.ATR[l] & k) { offset++; } } l += offset;
		 * Protocol.Protocol_Type = (WORD16)(ICC.ATR[l] & 0x0F); } 
		 */
		IFDHICCPresence(Lun);
	}
	else if (Action == IFD_POWER_DOWN)
	{
		rlen = HOR3GLL_BUFFER_SIZE;

		response = G_Oros3IccPowerDown(HOR3GLL_DEFAULT_TIME, &rlen, rbuff);

		if (response >= G_OK)
		{
			response = GE_Translate(rbuff[0]);
		}

		if (response < G_OK)
		{
			DEBUG_MSG("ICC Power down returing error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}
	}
	else if (Action == IFD_RESET)
	{
		rlen = HOR3GLL_BUFFER_SIZE;

		/*
		if (ICC.SelectionFlags != IFD_DEFAULT_MODE)
			if (ICC.SelectionFlags !=0 )
			{
				ptsmode = IFD_NEGOTIATE_PTS_MANUALLY;
				pts0 = ICC.SelectionFlags;
				pts1 = ICC.PTS1;
				pts2 = ICC.PTS2;
				pts3 = ICC.PTS3;
			}
			else
				ptsmode = pts0 = pts1 = pts2 = pts3 = 0;
		
		if (pts0 > 0x07)
			pts0 &= 0x07;
		*/
	 	/*==== EMV Behaviour Gemplus 2001*/
		response= G_Oros3Exchange(HOR3GLL_DEFAULT_TIME, 3, "\x17\x00\x00", &rlen, rbuff);
		
		/**/
		if (response < G_OK)
		{
			DEBUG_MSG("ICC Get statut returning error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}

		rlen = HOR3GLL_BUFFER_SIZE;
		/*Parse */
		if(rbuff[1]== 0x45)/*EMV Mode*/
		{
	
		response = G_Oros3Exchange(HOR3GLL_DEFAULT_TIME, 1, "\x12", &rlen, rbuff);
		}
		else/*ISO Mode*/
		{
		 response = G_Oros3IccPowerUp(HOR3GLL_DEFAULT_TIME,
			ICC_VCC_5_3V,
			// ICC_VCC_5V,
			IFD_DEFAULT_MODE, pts0, pts1, pts2, pts3, &rlen, rbuff);
		}
     	/*==== EMV Behaviour Gemplus 2001*/
			

		
		if (response >= G_OK)
		{
			response = GE_Translate(rbuff[0]);
		}

		if (response < G_OK)
		{
			DEBUG_MSG("ICC Power up-reset returing error");
			response = IFD_ERROR_POWER_ACTION;
			goto IFDHPowerICC_exit;
		}

		memset(Icc.ATR, 0, sizeof(Icc.ATR));
		memcpy(Icc.ATR, rbuff + 1, rlen - 1);

		*AtrLength = rlen - 1;
		memcpy(Atr, Icc.ATR, *AtrLength);

		/*
		 * l = 1; while (ICC.ATR[l] & 0x80) { offset = 0; for(k = 0x10; k >
		 * 0;  k <<=1) { if ( ICC.ATR[l] & k) { offset++; } } l += offset;
		 * Protocol.Protocol_Type = (WORD16)(ICC.ATR[l] & 0x0F); } 
		 */
		IFDHICCPresence(Lun);
		DEBUG_MSG("IFD Handler: Resetting is done");
	}
	else
	{
		response = IFD_ERROR_NOT_SUPPORTED;
		goto IFDHPowerICC_exit;
	}

	if ((Action == IFD_POWER_UP) || (Action == IFD_RESET))
	{
		// IFD_Is_ICC_Present(); // Call this fun which updates params
		ProtocolOptions.Current_Clock = 0x3680;

		GetAtrParams(Icc.ATR, &ProtocolOptions);

		rlen = HOR3GLL_BUFFER_SIZE;
		response = G_Oros3BufferSize(&rlen, rbuff);

		if (response == G_OK)
			ProtocolOptions.Current_IFSC = (WORD32) rbuff[1];

	}

IFDHPowerICC_exit:
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHPowerICC */


/**************************************************************************
 *
 *		IFDHTransmitToICC
 *
 *************************************************************************/
RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
	PUCHAR TxBuffer, DWORD TxLength,
	PUCHAR RxBuffer, PDWORD RxLength, PSCARD_IO_HEADER RecvPci)
{
	/*
	 * This function performs an APDU exchange with the card/slot specified
	 * by Lun.  The driver is responsible for performing any protocol
	 * specific exchanges such as T=0/1 ... differences.  Calling this
	 * function will abstract all protocol differences.
	 * 
	 * SendPci Protocol - 0, 1, .... 14 Length   - Not used.
	 * 
	 * TxBuffer - Transmit APDU example (0x00 0xA4 0x00 0x00 0x02 0x3F 0x00)
	 * TxLength - Length of this buffer. RxBuffer - Receive APDU example
	 * (0x61 0x14) RxLength - Length of the received APDU.  This function
	 * will be passed the size of the buffer of RxBuffer and this function is 
	 * responsible for setting this to the length of the received APDU.  This 
	 * should be ZERO on all errors.  The resource manager will take
	 * responsibility of zeroing out any temporary APDU buffers for security
	 * reasons.
	 * 
	 * RecvPci Protocol - 0, 1, .... 14 Length   - Not used.
	 * 
	 * Notes: The driver is responsible for knowing what type of card it has. 
	 * If the current slot/card contains a memory card then this command
	 * should ignore the Protocol and use the MCT style commands for support
	 * for these style cards and transmit them appropriately.  If your
	 * reader does not support memory cards or you don't want to then ignore
	 * this.
	 * 
	 * RxLength should be set to zero on error.
	 * 
	 * returns:
	 * 
	 * IFD_SUCCESS IFD_COMMUNICATION_ERROR IFD_RESPONSE_TIMEOUT
	 * IFD_ICC_NOT_PRESENT IFD_PROTOCOL_NOT_SUPPORTED 
	 */

	RESPONSECODE PCSCResponse = IFD_SUCCESS,/*Return IFD Error Code*/
	                                    TLResponse ;/*Used only in serial communication*/
	DWORD lc, Prot;
	BYTE *CmdData;
	BYTE data_in[280], data_out[280];

	G4_APDU_COMM ApduComm;
	G4_APDU_RESP ApduResp;

//	BYTE *tmp;

	DEBUG_MSG("IFDHTransmitToICC");

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	pthread_mutex_lock(&ifdh_status_mutex);
#endif

	lc = TxLength;
	Prot = SendPci.Protocol;
	CmdData = TxBuffer;
	*RxLength = 0;

	// Removed to go faster
	// IFD_Is_ICC_Present();

	DEBUG_MSG("Prot %d ProtType %d\n", Prot, ProtocolOptions.Protocol_Type);

	if (Prot != ProtocolOptions.Protocol_Type)
	{
		DEBUG_MSG("Prot not match");
		PCSCResponse = IFD_COMMUNICATION_ERROR;
		goto IFDHTransmitToICC_exit;
	}

	if (lc < 4)
	{
		DEBUG_MSG("Lc < 4");
		PCSCResponse = IFD_COMMUNICATION_ERROR;
		goto IFDHTransmitToICC_exit;
	}

	// clear structures
	memset(&ApduComm, 0, sizeof(ApduComm));
	memset(&ApduResp, 0, sizeof(ApduResp));

	// COMMAND_LEN is defined as 4 (CLA, INS, P1, P2)
	memcpy(ApduComm.Command, CmdData, COMMAND_LEN);

	ApduComm.DataIn = data_in;
	ApduResp.DataOut = data_out;

	// Case 1 CLA INS P1 P2
	// Case 2 CLA INS P1 P2 Le
	// Case 3 CLA INS P1 P2 Lc <Data>
	// Case 4 CLA INS P1 P2 Lc <Data> Le

	if (lc == 4)				// Case1
	{
		ApduComm.LengthIn = ApduComm.LengthExpected = 0;
	}
	else
		if (lc == 5)			// Case2..ie Lin=0, Le > 0
		{
			ApduComm.LengthIn = 0;
			ApduComm.LengthExpected = CmdData[COMMAND_LEN];
			if (ApduComm.LengthExpected == 0)	// Max Data
				ApduComm.LengthExpected = 256;
		}
		else						// Case3 & Case4
		{
			ApduComm.LengthIn = CmdData[COMMAND_LEN];
			DEBUG_MSG("Command Len = %d", ApduComm.LengthIn);
			ApduComm.LengthExpected = 0;	// Case3
			if (ApduComm.LengthIn > 0)
			{
				DEBUG_MSG("Copying %d bytes from CmdData to ApduComm.DataIn",
					ApduComm.LengthIn);

				memcpy(ApduComm.DataIn, CmdData + COMMAND_LEN + 1,
					ApduComm.LengthIn);
				//DEBUG_XXD(ApduComm.DataIn, ApduComm.LengthIn);
			}

			if (lc > COMMAND_LEN + 1 + ApduComm.LengthIn)	// Case4
			{
				ApduComm.LengthExpected =
					*(CmdData + COMMAND_LEN + 1 + ApduComm.LengthIn);
				
				if (ApduComm.LengthExpected == 0x00)	// Max Data
					ApduComm.LengthExpected = 256;

				DEBUG_MSG("Len expected = %d", ApduComm.LengthExpected);
			}
		}
/*
   The Reader has an EMV Behaviour. Whatever the protocol (T=0 or T=1) we always
   send smart card exchange with 15 GemCore Command. So we set the 'Prot' variable
   with 1 value.
*/
     /*EMV Behaviour Gemplus 2001*/
	if(EMVSupported== TRUE)
	{
	 Prot= 1 ;
	}
	
	if (Prot == 0)				// If T=0 protocol...
	{
		int i;

		// 2001 Modif--

		// This will prevent to execution of both IsoIn/IsoOut commands.
		// if ( (ApduComm.LengthIn > 0) && (ApduComm.LengthExpected > 0) )
		// {
		// return (IFD_COMMUNICATION_ERROR);
		// }

		// --2001 Modif
		TLResponse = ApduSpliter(HOR3GLL_DEFAULT_TIME,
			&ApduComm, &ApduResp, G_Oros3IccIsoInput, G_Oros3IccIsoOutput);

		if (TLResponse < G_OK)
		{
			DEBUG_MSG("ApduSpliter returning < G_OK\n");
			PCSCResponse = IFD_COMMUNICATION_ERROR;
			goto IFDHTransmitToICC_exit;
		}

		*RxLength = 2 + ApduResp.LengthOut;	// Added by Dave C

		for (i = 0; i < ApduResp.LengthOut; ++i)
			RxBuffer[i] = ApduResp.DataOut[i];

		RxBuffer[i] = (ApduResp.Status & 0xff00) >> 8;
		RxBuffer[i] &= 0xff;
		RxBuffer[i + 1] = (ApduResp.Status & 0x00ff);

	}
	else
		if (Prot == 1)

		// Here whatever smartcard Protocol T=0 or T=1, the APDU command is
		// sent by using 15h GemCore Command
		// This is the Firmware that manages the exchange.
		{
			WORD32 slen;
			WORD16 rlen;
			WORD8 rbuff[HOR3GLL_BUFFER_SIZE], sbuff[HOR3GLL_BUFFER_SIZE];

			if ((ApduComm.LengthExpected > (HOR3GLL_BUFFER_SIZE - 5))
				|| ((ApduComm.LengthExpected == 0)
					&& (ApduComm.LengthIn > (HOR3GLL_BUFFER_SIZE - 6)))
				|| ((ApduComm.LengthExpected != 0)
					&&		// (ApduComm.LengthIn > (HOR3GLL_BUFFER_SIZE - 7))
					(ApduComm.LengthIn > (HOR3GLL_BUFFER_SIZE - 6))))
			{
				DEBUG_MSG("Parameters not matching");
				PCSCResponse = (IFD_COMMUNICATION_ERROR);
				goto IFDHTransmitToICC_exit;
			}

			slen = HOR3GLL_BUFFER_SIZE;

			TLResponse = ApduBuilder(&ApduComm, sbuff, &slen);
			if (TLResponse < G_OK)
			{
				DEBUG_MSG("ApduBuilder returning %d", response);
				PCSCResponse = (IFD_COMMUNICATION_ERROR);
				goto IFDHTransmitToICC_exit;
			}

			rlen = HOR3GLL_BUFFER_SIZE;

			TLResponse = G_Oros3IccIsoT1(HOR3GLL_DEFAULT_TIME, slen, sbuff,
				&rlen, rbuff);
				/*This code was dead code. The TLResponse can never be > G_OK */
/*
			if (TLResponse > G_OK)
			{
				DEBUG_MSG("G_Oros3IccIsoT1 returning %d", response);
				TLResponse = GE_Translate(rbuff[0]);
			}
*/
			if ((TLResponse < G_OK) || (rlen < 3))
			{
				PCSCResponse = (IFD_COMMUNICATION_ERROR);
				goto IFDHTransmitToICC_exit;
			}

			// ApduResp.LengthOut = rlen - 3;
			ApduResp.LengthOut = rlen - 1;
			DEBUG_MSG("G_Oros3IccIsoT1 returning rlen=%d", rlen);

			// if ( ApduResp.LengthOut > 0 )
			if (ApduResp.LengthOut > 1)
			{
				DEBUG_MSG("*ResponseSize = ApduResp.LengthOut = %d",
					ApduResp.LengthOut);
				*RxLength = ApduResp.LengthOut;	/* Added by Dave C */
				memcpy(RxBuffer, rbuff + 1, ApduResp.LengthOut);
			}
			else
			{
				ApduResp.LengthOut = 0;
			}

			RxBuffer[ApduResp.LengthOut] = rbuff[rlen - 2];
			RxBuffer[ApduResp.LengthOut + 1] = rbuff[rlen - 1];
		}

IFDHTransmitToICC_exit:
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return PCSCResponse;
} /* IFDHTransmitToICC */


/**************************************************************************
 *
 *		IFDHControl
 *
 *************************************************************************/
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. 
	 */

	DEBUG_MSG("IFDHControl");

	return IFD_NOT_SUPPORTED;
} /* IFDHControl */


/**************************************************************************
 *
 *		IFDHICCPresence
 *
 *************************************************************************/
RESPONSECODE IFDHICCPresence(DWORD Lun)
{
	/*
	 * This function returns the status of the card inserted in the
	 * reader/slot specified by Lun.  It will return either:
	 * 
	 * returns: IFD_ICC_PRESENT IFD_ICC_NOT_PRESENT IFD_COMMUNICATION_ERROR 
	 */

	RESPONSECODE response = IFD_COMMUNICATION_ERROR;
	WORD16 rlen;
	WORD8 rbuff[HOR3GLL_BUFFER_SIZE];

	WORD8 cmd[2];

	DEBUG_MSG("IFDHICCPresence");
	
#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_lock", __FILE__, __LINE__);
	if (pthread_mutex_trylock(&ifdh_status_mutex) == EBUSY)
	{
		DEBUG_MSG("%s:%d mutex is locked", __FILE__, __LINE__);
		return IFD_ICC_PRESENT;
	}
#endif

	cmd[0] = HOR3GLL_IFD_CMD_ICC_STATUS;
	rlen = MAX_IFD_STRING;

	// If Exchange is running do not send command, do not change ICC
	// Structure
	// and leave with no error.
	/*if (bSemaphore == BUSY)
	{
		DEBUG_MSG("IFD_Is_ICC_Present running but Semaphore is set");
		return IFD_SUCCESS;
	}
	else
	{
		bSemaphore == FREE;
		DEBUG_MSG("IFD_Is_ICC_Present running");
	}*/

	response = G_Oros3Exchange(HOR3GLL_LOW_TIME, 1, cmd, &rlen, rbuff);

	if (response != G_OK)
		response = IFD_NOT_SUPPORTED;	// should not return this
	else
	{
		Icc.ICC_Presence = 0;
		Icc.ICC_Interface_Status = 0;

		if (rlen == 7)
		{
			if ((rbuff[1] & 0x04) == 0)	// ICC ABSENT
			{
				Icc.ICC_Presence = 0;
				// Make ATR and everything to 0s..
				// memset(&ICC, 0, sizeof(ICC));
				Icc.ICC_Presence = 0;
				Icc.ICC_Interface_Status = 0;
				memset(Icc.ATR, 0, sizeof(Icc.ATR));
				Icc.ICC_Type = 0;

				response = IFD_ICC_NOT_PRESENT;
			}
			else
				if ((rbuff[1] & 0x02) == 0)	// ICC PRESENT. POWER-DN
				{
					Icc.ICC_Presence = 2;
					Icc.ICC_Interface_Status = 0;	// Contact Inactive..

					response = IFD_ICC_PRESENT;
				}
				else
					if ((rbuff[1] & 0x08) == 0)	// ICC PRESENT. POWERUP
					{
						Icc.ICC_Presence = 2;
						Icc.ICC_Interface_Status = 1;	// Contact Active..
						ProtocolOptions.Protocol_Type = 0;
						if ((rbuff[2] != ISOCARD) && (rbuff[2] != FASTISOCARD))
							Icc.ICC_Type = 0;	// Non ISO Card
						else
							Icc.ICC_Type = 1;	// ISO Card

						response = IFD_ICC_PRESENT;
					}
					else
						if ((rbuff[2] == ISOCARD) || (rbuff[2] == FASTISOCARD))
						{
							Icc.ICC_Presence = 2;
							Icc.ICC_Interface_Status = 1;	// Contact Active..
							ProtocolOptions.Protocol_Type = 1;
							Icc.ICC_Type = 1;
							
							response = IFD_ICC_PRESENT;
						}
						else
							Icc.ICC_Type = 0;
		}
	}

#ifdef HAVE_PTHREAD_H
	DEBUG_MSG("%s:%d mutex_unlock", __FILE__, __LINE__);
	pthread_mutex_unlock(&ifdh_status_mutex);
#endif

	return response;
} /* IFDHICCPresence */

