/*
 * GCGBPTransport.c: send a command to a GemCore serial reader 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: GCGBPTransport.c,v 1.10 2012/04/08 19:09:42 rousseau Exp $
 *
 */

#include <string.h>

#include "Config.h"
#include "GCdebug.h"
#include "gempc_ifdhandler.h"
#include "gbpserial.h"
#include "GemCore.h"
#include "GCTransport.h"

/*
 * Transmission without errors:
 * Host              Reader
 * I(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *      <----------- I(1)
 *
 * Transmission with errors
 *
 *        Case 1:
 * Host              Reader
 * I(0) -----/----->
 *      <----------- R(0)
 * I(0) ----------->
 *      <----------- I(0)
 *
 *        Case 2:
 * Host              Reader
 * I(0) ----------->
 *      <----\------ I(0)
 * R(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *
 *        Case 3:
 * Host              Reader
 * I(0) -----/----->
 *      <----\------ R(0)
 * R(0) ----------->
 *      <----------- R(0)
 * I(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *
 *        Case 4:
 * Host              Reader
 * I(0) ----------->
 *      <----\------ I(0)
 * R(0) -----/----->
 *      <----------- R(1)
 * R(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *
 *        Case 5:
 * Host              Reader
 * I(0) ----------->
 *      <----\------ I(0)
 * R(0) -----/----->
 *      <----\------ R(1)
 * R(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *
 *        Case 6:
 * Host              Reader
 * I(0) ----------->
 *      <----\------ I(0)
 * R(0) -----/----->
 *      <----\------ R(1)
 * R(0) -----/----->
 *      <----------- R(1)
 * R(0) ----------->
 *      <----------- I(0)
 * I(1) ----------->
 *
 */

status_t GCSendCommand(DWORD Lun, DWORD nLengthIn,
	const UCHAR pcBufferCmd[], PDWORD pnLengthOut, PUCHAR pcBufferOut)
{
	UCHAR pctr_to_card_buffer[GC_TR_BUF_SIZE];
	RESPONSECODE creturn_value;
	DWORD nlength, rv;

	creturn_value = STATUS_SUCCESS;

	if (GC_TR_BUF_SIZE <= nLengthIn)
	{
		/* Buffer is too small (should not happen) */
		creturn_value = STATUS_DEVICE_PROTOCOL_ERROR;
		goto finally;
	}

again:
	memcpy(pctr_to_card_buffer + 1, pcBufferCmd, nLengthIn);
	pctr_to_card_buffer[TR_OFFSET_LNG] = nLengthIn;
	if (WriteGBP(Lun, nLengthIn + 1, pctr_to_card_buffer) != STATUS_SUCCESS)
	{
		creturn_value = STATUS_DEVICE_PROTOCOL_ERROR;
		goto finally;
	}

	nlength = sizeof(pctr_to_card_buffer);
	if ((rv = ReadGBP(Lun, &nlength, pctr_to_card_buffer)) != STATUS_SUCCESS)
	{
		/* The reader reports an error. */

		/* Unknown NAD, retry the command */
		if (rv == STATUS_NAD_UNKNOWN)
		{
			DEBUG_COMM("wrong NAD, resent sequence");
			goto again;
		}

		/* resent sequence 0 */
		if (rv == STATUS_PCB_REP_0)
		{
			DEBUG_COMM("resent sequence n°0");
			SetGBPSeqNumber(Lun, 0);
			goto again;
		}

		/* resent sequence 1 */
		if (rv == STATUS_PCB_REP_1)
		{
			DEBUG_COMM("resent sequence n°1");
			SetGBPSeqNumber(Lun, 1);
			goto again;
		}

		if (rv == STATUS_PCB_SYNC_REQ || rv == STATUS_PCB_SYNC_RESP)
		{
			DEBUG_COMM("Resynch unsupported");
		}

		creturn_value = STATUS_DEVICE_PROTOCOL_ERROR;
		goto finally;
	}
	if (nlength < 1)
	{
		/* length byte not received */
		creturn_value = STATUS_DEVICE_PROTOCOL_ERROR;
		goto finally;
	}
	nlength--;
	*pnLengthOut = (*pnLengthOut < nlength) ? *pnLengthOut : nlength;
	memcpy(pcBufferOut, pctr_to_card_buffer + 1, *pnLengthOut);

finally:
	if (creturn_value != STATUS_SUCCESS)
		*pnLengthOut = 0;

	/* Clear buffer */
	bzero(pctr_to_card_buffer, sizeof(pctr_to_card_buffer));

	return creturn_value;
} /* GCSendCommand */

