/*
    main.c: main function used for IFDH debug
    Copyright (C) 2001-2004   Ludovic Rousseau

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

/*
 * $Id: main.c,v 1.21 2012/04/08 19:09:42 rousseau Exp $
 */

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

#include "gempc_ifdhandler.h"
#include "Config.h"
#include "GCdebug.h"

/* CHANNELID:
 /  0x000001 - /dev/pcsc/1
 /  0x000002 - /dev/pcsc/2
 /  0x000003 - /dev/pcsc/3
 /  0x000004 - /dev/pcsc/4 */

/* 1 -> GCR400
 / 2 -> GemPC410 */
#define SERIAL_PORT 2
#define SERIAL_NAME "/dev/pcsc/2"

/* for the GemPC430 use something like usb:08e6/0430 */

#define LUN 0

void pcsc_error(int rv);

int main(int argc, char *argv[])
{
	int rv, i, len_i, len_o;
	UCHAR atr[MAX_ATR_SIZE];
	DWORD atrlength;
	UCHAR s[MAX_BUFFER_SIZE], r[MAX_BUFFER_SIZE];
	DWORD dwSendLength, dwRecvLength;
	SCARD_IO_HEADER SendPci, RecvPci;

	if (argc > 0)
		rv = IFDHCreateChannelByName(LUN, argv[1]);
	else
		rv = IFDHCreateChannelByName(LUN, SERIAL_NAME);
	if (rv != IFD_SUCCESS)
	{
		printf("IFDHCreateChannel: %d\n", rv);
		return 1;
	}
#if 0
	rv = IFDHCreateChannel(LUN, SERIAL_PORT);
	if (rv != IFD_SUCCESS)
	{
		printf("IFDHCreateChannel: %d\n", rv);
		return 1;
	}
#endif

	rv = IFDHICCPresence(LUN);
	pcsc_error(rv);

	rv = IFDHPowerICC(LUN, IFD_RESET, atr, &atrlength);
	if (rv != IFD_SUCCESS)
	{
		printf("IFDHPowerICC: %d\n", rv);

		goto end;
	}

	DEBUG_XXD("ATR: ", atr, atrlength);

	rv = IFDHICCPresence(LUN);
	pcsc_error(rv);

	memset(&SendPci, 0, sizeof(SendPci));
	memset(&RecvPci, 0, sizeof(RecvPci));

	/* select applet */
	s[0] = 0x00;
	s[1] = 0xA4;
	s[2] = 0x04;
	s[3] = 0x00;
	s[4] = 0x06;
	s[5] = 0xA0;
	s[6] = 0x00;
	s[7] = 0x00;
	s[8] = 0x00;
	s[9] = 0x18;
	s[10] = 0xFF;

	dwSendLength = 11;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Select applet: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

	/* Case 1 */
	s[0] = 0x80;
	s[1] = 0x21;
	s[2] = 0x00;
	s[3] = 0x00;
	s[4] = 0x00;

	dwSendLength = 5;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Case 1: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

	/* Case 2 */
	/*
	 * 248 (0xF8) is max size for one USB or GBP paquet
	 * 255 (0xFF) maximum, 1 minimum
	 */
	len_i = 255;

	s[0] = 0x80;
	s[1] = 0x22;
	s[2] = 0x00;
	s[3] = 0x00;
	s[4] = len_i;

	for (i=0; i<len_i; i++)
		s[5+i] = i;

	dwSendLength = len_i + 5;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Case 2: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

	/* Case 3 */
	/*
	 * 252  (0xFC) is max size for one USB or GBP paquet
	 * 256 (0x100) maximum, 1 minimum
	 */
	len_o = 256;

	s[0] = 0x80;
	s[1] = 0x23;
	if (len_o > 255)
	{
		s[2] = 0x01;
		s[3] = len_o-256;
	}
	else
	{
		s[2] = 0x00;
		s[3] = len_o;
	}
	s[4] = len_o;

	dwSendLength = 5;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Case 3: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

	/* Case 4 */
	/*
	 * len_i
	 * 248 (0xF8) is max size for one USB or GBP paquet
	 * 255 (0xFF) maximum, 1 minimum
	 *
	 * len_o
	 * 252  (0xFC) is max size for one USB or GBP paquet
	 * 256 (0x100) maximum, 1 minimum
	 */
	len_i = 255;
	len_o = 256;

	s[0] = 0x80;
	s[1] = 0x24;
	if (len_o > 255)
	{
		s[2] = 0x01;
		s[3] = len_o-256;
	}
	else
	{
		s[2] = 0x00;
		s[3] = len_o;
	}
	s[4] = len_i;

	for (i=0; i<len_i; i++)
		s[5+i] = i;

	dwSendLength = len_i + 5;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Case 4: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

	/* Get response */
	s[0] = 0x00;
	s[1] = 0xC0;
	s[2] = 0x00;
	s[3] = 0x00;
	s[4] = r[1]; /* SW2 of previous command */

	dwSendLength = 5;
	dwRecvLength = sizeof(r);

	DEBUG_XXD("Get response: ", s, dwSendLength);
	rv = IFDHTransmitToICC(LUN, SendPci, s, dwSendLength, r, &dwRecvLength,
		&RecvPci);

	DEBUG_XXD("Received: ", r, dwRecvLength);
	if (rv)
		pcsc_error(rv);

end:
	/* Close */
	rv = IFDHCloseChannel(LUN);
	if (rv != IFD_SUCCESS)
	{
		printf("IFDHCloseChannel: %d\n", rv);
		return 1;
	}

	return 0;
} /* main */

void pcsc_error(int rv)
{
	switch (rv)
	{
		case IFD_ICC_PRESENT:
			DEBUG_INFO("IFD: card present");
			break;

		case IFD_ICC_NOT_PRESENT:
			DEBUG_INFO("IFD: card _NOT_ present");
			break;

		case IFD_COMMUNICATION_ERROR:
			DEBUG_INFO("IFD: communication error");
			break;

		case IFD_PROTOCOL_NOT_SUPPORTED:
			DEBUG_INFO("IFD: protocol not supported");
			break;

		case IFD_RESPONSE_TIMEOUT:
			DEBUG_INFO("IFD: response timeout");
			break;

		default:
			DEBUG_INFO2("IFD: undocumented error: %d", rv);
	}
} /* pcsc_error */

