/*
    resetGemPC410 - try to debug the GBP comm
    Copyright (C) 2001  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: resetGemPC410.c,v 1.12 2012-02-14 21:09:14 rousseau Exp $
 */

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <termios.h>

#define DEVICE "/dev/pcsc/2"

#define BUFFER_SIZE 100
#define MAX_APDU_SIZE 16

void checksum(unsigned char cmd[], const int len);
void debug(const unsigned char cmd[], const int len);
void explain_gbp(const unsigned char buffer[], const int rv);
int multi_read(int fd, unsigned char *buf, size_t count);

int main(int argc, char *argv[])
{
	int fd;
	unsigned char cmd[MAX_APDU_SIZE], buffer[BUFFER_SIZE];
	int rv;
	struct termios current_termios;

	(void)argc;
	(void)argv;

	printf("Using device: " DEVICE "\n");
    if ((fd = open(DEVICE, O_RDWR | O_NOCTTY)) < 0)
    {
        printf("open error: %s\n", strerror(errno));
        return 1;
    }

	if (tcgetattr(fd, &current_termios) == -1)
	{
		printf("tcgetattr() function error: %s\n", strerror(errno));
		close(fd);
		fd = -1;
		return 1;
	}

	/* The serial port is not yet configured to 38400 bauds? */
	if (cfgetospeed(&current_termios) != B38400)
	{
		printf("actual baud rate: %d\n", cfgetospeed(&current_termios));

		current_termios.c_iflag = 0;
		current_termios.c_oflag = 0;	/* Raw output modes */
		current_termios.c_cflag = 0;	/* Raw output modes */

		/* Do not echo characters because if you connect
		 * to a host it or your
		 * modem will echo characters for you.
		 * Don't generate signals. */
		current_termios.c_lflag = 0;

		/* control flow - enable receiver - ignore modem
		 * control lines */
		current_termios.c_cflag = CREAD | CLOCAL;

		/* default speed is 38400 bauds */
		cfsetospeed(&current_termios, B38400);
		cfsetispeed(&current_termios, 0); /* 0 corresponds to output speed */

		current_termios.c_cflag |= CS8;

		/*case ODDPARITY:
		 *current_termios.c_cflag |= PARENB + PARODD;
		 *case EVENPARITY:
		 *current_termios.c_cflag |= PARENB;

		 * control caracteres

		 * Minimum bytes to read
		 * current_termios.c_cc[VMIN]  = 0;
		 * Time between two bytes read (VTIME x 0.10 s)
		 * current_termios.c_cc[VTIME] = 0;

		 * This is Non Canonical mode set.
		 * by setting c_cc[VMIN]=0 and c_cc[VTIME] = 10
		 * each read operation will wait for 10/10 = 1 sec
		 * If single byte is received, read function returns.
		 * if timer expires, read() returns 0.
		 * Minimum bytes to read */
		current_termios.c_cc[VMIN] = 0;
		/* Time between two bytes read (VTIME x 0.10 s) */
		current_termios.c_cc[VTIME] = 20;

		printf("Set serial port baudrate to 38400\n");
		if (tcsetattr(fd, TCSANOW, &current_termios) == -1)
		{
			printf("tcsetattr error: %s\n", strerror(errno));
			close(fd);
			fd = -1;
			return 1;
		}
	}
	else
	{
		printf("port " DEVICE " already at 38400 bauds\n");
	}

	cmd[0] = 0x42; /* NAD */
	cmd[1] = 0x00; /* PCB */
	cmd[2] = 0x05; /* LEN */
	cmd[3] = 0x22; /* Read Firmware Version */
	cmd[4] = 0x05;
	cmd[5] = 0x3F;
	cmd[6] = 0xE0;
	cmd[7] = 0x10;
	cmd[8] = 0x00; /* EDC */
	checksum(cmd, 9);

	printf("-> ");
	debug(cmd, 9);
	/* explain_gbp(cmd, 9); */

	if (write(fd, cmd, 9) < 0)
	{
        printf("write error: %s\n", strerror(errno));
	}

	if ((rv = multi_read(fd, buffer, BUFFER_SIZE)) < 0)
	{
        printf("read error: %s\n", strerror(errno));
	}
	printf("%d bytes read\n<- ", rv);
	debug(buffer, rv);

	if (rv == 0)
	{
		if (tcgetattr(fd, &current_termios) == -1)
		{
			close(fd);
			fd = -1;
			printf("tcgetattr() function error\n");
			return 1;
		}

		current_termios.c_cflag = CREAD | CLOCAL;

		/* default speed is 9600 bauds */
		current_termios.c_cflag |= B9600;
		current_termios.c_cflag |= CS8;

		printf("Set serial port baudrate to 9600\n");
		if (tcsetattr(fd, TCSANOW, &current_termios) == -1)
		{
			close(fd);
			fd = -1;
			printf("tcsetattr error");
			return 1;
		}
	}

	explain_gbp(buffer, rv);

	cmd[1] = 0x40;
	checksum(cmd, 9);

	printf("-> ");
	debug(cmd, 9);
	/* explain_gbp(cmd, 9); */

	if (write(fd, cmd, 9) < 0)
	{
        printf("write error: %s\n", strerror(errno));
	}

	if ((rv = multi_read(fd, buffer, BUFFER_SIZE)) < 0)
	{
        printf("read error: %s\n", strerror(errno));
	}
	printf("%d bytes read\n<- ", rv);
	debug(buffer, rv);

	explain_gbp(buffer, rv);

	/* reset GemCore */
	cmd[0] = 0x42; /* NAD */
	cmd[1] = 0x00; /* PCB */
	cmd[2] = 0x04; /* LEN */
	cmd[3] = 0x0C; /* Restart */
	cmd[4] = 0x00;
	cmd[5] = 0x00;
	cmd[6] = 0x00;
	cmd[7] = 0x00; /* EDC */
	checksum(cmd, 8);

	printf("-> ");
	debug(cmd, 8);

	if (write(fd, cmd, 8) < 0)
	{
        printf("write error: %s\n", strerror(errno));
	}

	if ((rv = multi_read(fd, buffer, BUFFER_SIZE)) < 0)
	{
        printf("read error: %s\n", strerror(errno));
	}
	printf("%d bytes read\n<- ", rv);
	debug(buffer, rv);

	explain_gbp(buffer, rv);

    close(fd);

	return 0;
} /* main */

void checksum(unsigned char cmd[], const int len)
{
	int edc, i;

	/* checksum */
	edc = 0;
	for (i=0; i<len-1; i++)
		edc ^= cmd[i];

	cmd[len-1] = edc;

} /* checksum */

void debug(const unsigned char cmd[], const int len)
{
	int i;

	if (len == 0)
		return;

	for (i=0; i<len; i++)
		printf("%02X ", cmd[i]);
	printf("\n");

	for (i=0; i<len; i++)
		if (cmd[i] > 31)
			printf("%c", cmd[i]);
		else
			printf(".");
	printf("\n");

} /* debug */

void explain_gbp(const unsigned char buffer[], const int rv)
{
	if (rv < 4)
	{
		printf("GBP bloc too short: %d instead of min 5\n", rv);
		return;
	}

	switch (buffer[0])
	{
		case 0x42:
			printf("  NAD: host to gemcore\n");
			break;
		case 0x24:
			printf("  NAD: gemcore to host\n");
			break;
		default:
			printf("  NAD: UNKNOWN value %02X\n", buffer[0]);
	}

	switch (buffer[1])
	{
		case 0x00:
			printf("  PCB: I-block S=0\n");
			break;
		case 0x40:
			printf("  PCB: I-block S=1\n");
			break;
		case 0x80:
			printf("  PCB: R-block S=0, EDC error=0, another error=0\n");
			break;
		case 0x90:
			printf("  PCB: R-block S=1, EDC error=0, another error=0\n");
			break;
		case 0x81:
			printf("  PCB: R-block S=0, EDC error=1, another error=0\n");
			break;
		case 0x91:
			printf("  PCB: R-block S=1, EDC error=1, another error=0\n");
			break;
		case 0x82:
			printf("  PCB: R-block S=0, EDC error=0, another error=1\n");
			break;
		case 0x92:
			printf("  PCB: R-block S=1, EDC error=0, another error=1\n");
			break;
		case 0x83:
			printf("  PCB: R-block S=0, EDC error=1, another error=1\n");
			break;
		case 0x93:
			printf("  PCB: R-block S=1, EDC error=1, another error=1\n");
			break;
		case 0xB0:
			printf("  PCB: S-block resynch request\n");
			break;
		case 0xE0:
			printf("  PCB: S-block resynch response\n");
			break;
		default:
			printf("  PCB: UNKNOWN value %02X\n", buffer[1]);
			break;
	}

	printf("  LEN: %02X bytes\n", buffer[2]);

	switch (buffer[3])
	{
		case 0x00:
			printf("  S: no error\n");
			break;
		case 0x01:
			printf("  S: unknown driver\n");
			break;
		case 0x02:
			printf("  S: operation impossible with this driver\n");
			break;
		case 0x03:
			printf("  S: incorrect number of arguments\n");
			break;
		case 0x04:
			printf("  S: reader command unknown\n");
			break;
		case 0x05:
			printf("  S: response too long for the buffer\n");
			break;
		case 0x10:
			printf("  S: resonse error at the card reset\n");
			break;
		case 0x12:
			printf("  S: message too long\n");
			break;
		case 0x13:
			printf("  S: byte reading error returned by an asynchronous cadr\n");
			break;
		case 0x15:
			printf("  S: card powered down\n");
			break;
		case 0x1B:
			printf("  S: a command has been sent with an incorrect number of parameters\n");
			break;
		case 0x1C:
			printf("  S: overlap on writing flash memory\n");
			break;
		case 0x1D:
			printf("  S: the TCK check byte is incorrect in a micropocessor card ATR\n");
			break;
		case 0x1E:
			printf("  S: an attempt has been made to write a write-protected external memory\n");
			break;
		case 0x1F:
			printf("  S: incorrect data has been send to external memory\n");
			break;
		case 0xA0:
			printf("  S: error in the card reset response\n");
			break;
		case 0xA1:
			printf("  S: card protocol error\n");
			break;
		case 0xA2:
			printf("  S: card malfuntion\n");
			break;
		case 0xA3:
			printf("  S: parity error\n");
			break;
		case 0xA4:
			printf("  S: card has been chaining (T=1)\n");
			break;
		case 0xA5:
			printf("  S: reader has aborted chaining (T=1)\n");
			break;
		case 0xA6:
			printf("  S: resynch successfully performed by GemCore\n");
			break;
		case 0xA7:
			printf("  S: protocol type selection (PTS) error\n");
			break;
		case 0xB0:
			printf("  S: GemCP410 command not supported\n");
			break;
		case 0xCF:
			printf("  S: other key already pressed\n");
			break;
		case 0xE4:
			printf("  S: the card has just send an invalid procedure byte\n");
			break;
		case 0xE5:
			printf("  S: the card has interrupted an exchange\n");
			break;
		case 0xE7:
			printf("  S: error returned by the card\n");
			break;
		case 0xF7:
			printf("  S: card removed\n");
			break;
		case 0xF8:
			printf("  S: the cadr is consuming too much electricity\n");
			break;
		case 0xFB:
			printf("  S: card missing\n");
			break;
		default:
			printf("  S: UNKNOWN ERROR %02X\n", buffer[3]);
			break;
	}
	return;
} /* explain_gbp */

int multi_read(int fd, unsigned char *buf, size_t count)
{
	int rv, len, to_read, already_read;

	rv = read(fd, buf, count);
	if (rv < 0)
		return rv;

	/* too short */
	if (rv < 3)
	{
		printf("ReadGBP: only %d byte(s) read\n", rv);
		return rv;
	}

	/* LEN byte (+4 bytes of protocol) */
	to_read = buf[2]+4;

	/* Nb of bytes already read */
	already_read = rv;

	/* Nb of bytes until the end of the buffer */
	len = count - rv;

	while (already_read < to_read)
	{
		rv = read(fd, buf+already_read, len);
		if (rv < 0)
		{
			printf("ReadGBP: read error: %s\n", strerror(errno));
			return rv;
		}

		already_read += rv;
		len -= rv;
	}

	return to_read;
} /* multi_read */

