/******************************************************************

            Title  : T1Hndlr.c
            Package: ACS Cybermouse Linux Driver
            Author : David Corcoran
            Date   : 10/15/99
            Purpose: This handles the T=1 protocol.
            LICENSE: See LICENSE

********************************************************************/

/*
    Versione modificata
*/

#include <memory.h>
#include "pcscdefines.h"
#include "AdmHndlr.h"
#include "T1Hndlr.h"
#ifndef TARGET_OSX
#include "config.h"
#endif

#ifdef T1_DEBUG
#include <stdio.h>
#endif

/* This can actually be read from the ATR but since many
   smartcard manufacturers still can't figure out what it
   means to be ISO compliant I will just rely on a small
   number.  ( Most cards have a 32 byte IFSD_SIZE )
*/


#define MAX_IFSD_SIZE  20
#define BWT_MULTIPLIER  3

#define T1_SBLOCK_SUCCESS      0x200
#define T1_SBLOCK_WTXREQUEST   0x201
#define T1_RBLOCK_SUCCESS      0x210
#define T1_IBLOCK_SUCCESS      0x220
#define T1_ERROR_PARITY        0x230
#define T1_ERROR_OTHER         0x240
#define T1_INVALID_BLOCK       0x250

ULONG T1_ACSTransaction( DWORD, DWORD, PUCHAR, ULONG, PUCHAR, PULONG );
ULONG T1_GetResponseType( PUCHAR, DWORD );
ULONG T1_WTXResponse( DWORD, DWORD, UCHAR, PUCHAR );

UCHAR T1CalculateLRC( PUCHAR pucBuffer, DWORD dwLength) {

  UCHAR ucXSum;
  short i;

  ucXSum = 0;
  for ( i=0; i<dwLength; i++ ) {
    ucXSum ^= pucBuffer[i];
  }
  return ucXSum;
}

ULONG T1_GetResponseType( PUCHAR pucRBuffer, DWORD dwLength ) {
  /* Checks to see what type of block it is */

  if ( (pucRBuffer[1] & 0x80)&&(pucRBuffer[1] & 0x40) ) {
    /* S Block Found */
#ifdef T1_DEBUG
    printf("S Block Found\n");
#endif

    if ( pucRBuffer[1] & 0x03 ) {
#ifdef T1_DEBUG
    printf("WTX Request Made\n");
#endif
    return T1_SBLOCK_WTXREQUEST;
    } else if ( pucRBuffer[1] & 0x04 ) {
#ifdef T1_DEBUG
    printf("Vpp Error Response Made\n");
#endif
    } else if ( pucRBuffer[1] & 0x02 ) {
#ifdef T1_DEBUG
    printf("ABORT Request Made\n");
#endif
    } else if ( pucRBuffer[1] & 0x01 ) {
#ifdef T1_DEBUG
    printf("IFS Request Made\n");
#endif
    } else if ( pucRBuffer[1] == 0xC0 ) {
#ifdef T1_DEBUG
    printf("RESYNCH Request Made\n");
#endif
    } else {
#ifdef T1_DEBUG
    printf("Unknown Request Made\n");
#endif
    }

    return T1_SBLOCK_SUCCESS;

  } else if ( pucRBuffer[1] & 0x80) {
    /* R Block Found */
#ifdef T1_DEBUG
    printf("R Block Found\n");
#endif

	/* ---- 1.5 fix (R-Block error) starts here ---- */
#if 0
    if ( pucRBuffer[1] & 0x10 ) {
	
      if ( pucRBuffer[1] & 0x01 ) {
	return T1_ERROR_PARITY;
      }

      if ( pucRBuffer[1] & 0x02 ) {
	return T1_ERROR_OTHER;
      }

    } else {
      return T1_RBLOCK_SUCCESS;
    }
#endif

	if ( pucRBuffer[1] & 0x01 ) {
	
		return T1_ERROR_PARITY;
	}

	if ( pucRBuffer[1] & 0x02 ) {
	
		return T1_ERROR_OTHER;
	}

	return T1_RBLOCK_SUCCESS;
	/* ---- 1.5 fix (R-Block error) ends here ---- */	
	
  } else if ( (pucRBuffer[1] & 0x80) == 0 ) {
    /* I Block Found */
#ifdef T1_DEBUG
    printf("I Block Found\n");
#endif
    return T1_IBLOCK_SUCCESS;

  } else {
    return T1_INVALID_BLOCK;
  }
  return T1_INVALID_BLOCK;	/* Unknown error... [KK] */
}

ULONG T1_WTXResponse( DWORD Lun, DWORD slotIndex, UCHAR ucWtx, PUCHAR ucRBuffer) {

  ULONG rv;
  DWORD dwRBufferLen;
  UCHAR ucTBuffer[MAX_BUFFER_SIZE];

  ucTBuffer[0] = 0x00;          /* NAD - Addressing not used */
  ucTBuffer[1] = 0xE3;          /* PCB - WTX Response        */
  ucTBuffer[2] = 1;             /* LEN - 1                   */
  ucTBuffer[3] = ucWtx;         /* WTX Value                 */
  ucTBuffer[4] = T1CalculateLRC( ucTBuffer, 4 );
  dwRBufferLen = MAX_BUFFER_SIZE;

  Adm_SetWWT( Lun, ucWtx*BWT_MULTIPLIER );

  rv = T1_ACSTransaction( Lun, slotIndex, ucTBuffer, 5,
			    ucRBuffer, &dwRBufferLen );
  return rv;
}

ULONG T1_ExchangeData(  DWORD Lun, DWORD slotIndex, PUCHAR pucTBuffer, DWORD dwTLength,
                        PUCHAR pucRBuffer, DWORD* dwRLength ) {

  ULONG rv, tv;
  UCHAR ucTBuffer[MAX_BUFFER_SIZE];
  UCHAR ucRBuffer[MAX_BUFFER_SIZE];
  UCHAR ucRTemp[MAX_BUFFER_SIZE];
//  UCHAR ucRHeader[5];
  DWORD dwCycles, dwRemaining, dwOffset;
  DWORD dwRBufferLen, dwRCount;
  int i;
  static UCHAR ucSChainNum  = 0;
  static UCHAR ucRChainNum  = 1;
	int iPrevSequence;

    /* Problem: Command may have a return that exceeds the
       IFSD Length.

       Solution: Wait for ICC's I-block transmission with
       the MORE bit set and send appropriate R-blocks.
    */
	#ifdef T1_DEBUG
	printf("***************** T1_ExchangeData:\n");
	printf("<--\n");
	for (i=0; i<dwTLength; i++) {
		if ((i>0)&&(i%16)==0) printf("\n");
		printf("%02x ",pucTBuffer[i]);
	}
	printf("\n");
	#endif

	dwRCount=0;

  if ( dwTLength + 4 < MAX_IFSD_SIZE )
  {

	#ifdef T1_DEBUG
		printf("CASE 1: dwTLength + 4 = %ld < MAX_IFSD_SIZE\n", dwTLength + 4);
	#endif

  retryblocka:
    ucTBuffer[0] = 0x00;          /* NAD - Addressing not used */
    ucTBuffer[1] = 0x00;          /* PCB - No chaining yet     */
    ucTBuffer[2] = dwTLength;     /* LEN - Size of the APDU    */
    ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00;  /* N(R)          */
    memcpy( &ucTBuffer[3], pucTBuffer, dwTLength );
    ucTBuffer[3 + dwTLength] = T1CalculateLRC( ucTBuffer, dwTLength + 3 );
    dwRBufferLen             = sizeof(ucRBuffer);
    dwRBufferLen = MAX_BUFFER_SIZE;

    rv = T1_ACSTransaction( Lun, slotIndex, ucTBuffer, dwTLength+4,
			    ucRBuffer, &dwRBufferLen );

	do {	/* Modified by ACS */

    tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );

	#ifdef T1_DEBUG
	printf("T1 Response type = %lx\n",tv);
	#endif
	
    switch( tv ) {
      UCHAR ucWtx;

      case T1_SBLOCK_WTXREQUEST:
	#ifdef T1_DEBUG
	printf("T1_SBLOCK_WTXREQUEST\n");
	#endif
        ucWtx = ucRBuffer[3];
        T1_WTXResponse( Lun, slotIndex, ucWtx, ucRBuffer );
        break;
      case T1_ERROR_OTHER:
	#ifdef T1_DEBUG
	printf("T1_ERROR_OTHER, retrying...\n");
	#endif
        ucSChainNum += 1;
        goto retryblocka;
        break;
      default:
        break;
    }

	}while(tv==T1_SBLOCK_WTXREQUEST);	/* Modified by ACS */

	iPrevSequence = ucRBuffer[1]&0x40;
	#ifdef T1_DEBUG
	printf("CASE 1: more data: %s\n", ucRBuffer[1]&0x20? "AVAILABLE":"NOT AVAILABLE");
	printf("CASE 1: current sequence number = 0x%02X \n", iPrevSequence);
	printf("CASE 1: transmission completed, now receiveing (block 1 should be already be received, size=%d)\n", ucRBuffer[2]);
	#endif

	if (rv != STATUS_SUCCESS) {
		return STATUS_DEVICE_PROTOCOL_ERROR;
	}

    /* Copy the response from the DATA field */
     if ( ucRBuffer[2] > 0 && rv == STATUS_SUCCESS ) {
      memcpy( ucRTemp, &ucRBuffer[3], ucRBuffer[2] );
      dwRCount = ucRBuffer[2];
    }
     ucSChainNum += 1;
    /* Start of loop which checks the More Data bit after every transfer
       to determine whether or not another R-block must be sent
    */
    if ( ucRBuffer[1] & 0x20 )      /* PCB More Data Bit */
    {


#ifdef T1_DEBUG
	printf("MORE data bit set, starting receive cycle...\n");
#endif

      do {
	/* Create the R-block */

	/* ---- 1.6 fix (I-Block Chaining) starts here ---- */
#if 0
	/* ---- the following is the original code ---- */
	ucTBuffer[0] = 0x00;                            /* DF Nad   */
	ucTBuffer[1] = (ucRChainNum%2)?0x90:0x80;        /* N(R)     */
	ucTBuffer[2] = 0x00;                            /* Len  = 0 */
	ucTBuffer[3] = T1CalculateLRC( ucTBuffer, 3 );  /* Lrc      */
#endif

	/* ---- new code starts here ---- */
	/* R-Block Seq #, NR, should not be the same as I-Block Seq #, NS */
	unsigned char NR	= (~ucRBuffer[1]>>6)&0x01;

	ucTBuffer[0] = 0x00;                            /* DF Nad   */
	ucTBuffer[1] = NR?0x90:0x80;        /* N(R)     */
	ucTBuffer[2] = 0x00;                            /* Len  = 0 */
	ucTBuffer[3] = T1CalculateLRC( ucTBuffer, 3 );  /* Lrc      */
	/* ---- 1.6 fix (I-Block Chaining) ends here ---- */

	dwRBufferLen = MAX_BUFFER_SIZE;
        rv = T1_ACSTransaction( Lun, slotIndex, ucTBuffer,4,ucRBuffer,&dwRBufferLen );
	tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
	#ifdef T1_DEBUG
	printf(">>>>>>>>>>>>>>>>>>>>> %02x %lx\n", ucRBuffer[1], tv);
	#endif
	switch( tv ) {
		UCHAR ucWtx;
		case T1_SBLOCK_WTXREQUEST:
			#ifdef T1_DEBUG
			printf("T1_SBLOCK_WTXREQUEST\n");
			#endif
			ucWtx = ucRBuffer[3];
			T1_WTXResponse(Lun, slotIndex, ucWtx,ucRBuffer);
			break;
		case T1_ERROR_OTHER:
			#ifdef T1_DEBUG
			printf("T1_ERROR_OTHER, retrying from retryblocka\n");
			#endif
			ucSChainNum += 1;
			goto retryblocka;
			break;
		default:
			break;
	}

	if (rv != STATUS_SUCCESS) {
	  return STATUS_DEVICE_PROTOCOL_ERROR;
	}

	if(iPrevSequence != (ucRBuffer[1]&0x40) )
	{
		#ifdef T1_DEBUG
		printf("The block was copyed in the output buffer\n");
		#endif
		memcpy( &ucRTemp[dwRCount], &ucRBuffer[3], ucRBuffer[2] );
		dwRCount   += ucRBuffer[2];
		iPrevSequence = ucRBuffer[1]&0x40;
	}
	ucRChainNum += 1;
      } while ( ucRBuffer[1] & 0x20 );
	#ifdef T1_DEBUG
	printf("Receive cycle terminated\n");
	#endif
    }
#ifdef PCSC_DEBUG
    printf("Full T=1 Response Data APDU: ");
    for (i=0; i < dwRCount; i++) {
      printf("%x ", ucRTemp[i]);
    } printf("\n");
#endif

	#ifdef T1_DEBUG
	printf("***************** Full T=1 Response Data APDU: %ld\n", dwRCount);
	printf("-->\n");
	for (i=0; i < dwRCount; i++) {
		if ((i>0)&&(i%16)==0) printf("\n");
		printf("%02x ", ucRTemp[i]);
	} printf("\n");
	#endif

}
else
{

    /* Problem: Command is larger than the IFSD Length
       This is a large outgoing command which
       will have a return of 2 status bytes.

       Solution: Use I-block chaining and wait for ICC
       R-block responses
    */

	#ifdef T1_DEBUG
	printf("CASE 2: dwTLength + 4 = %ld > MAX_IFSD_SIZE\n", dwTLength + 4);
	#endif

    dwCycles     = dwTLength / MAX_IFSD_SIZE;
    dwRemaining  = dwTLength % MAX_IFSD_SIZE;
    dwRBufferLen = sizeof(ucRBuffer);
    for ( i=0; i < dwCycles; i++ )
    {
	#ifdef T1_DEBUG
	printf("Transission Cycle %d of %ld\n", i+1, dwCycles);
	#endif

    retryblockb:
      ucTBuffer[0]  = 0x00;          /* NAD - Addressing not used */
      ucTBuffer[1]  = 0x20;          /* PCB - Chaining used       */
      ucTBuffer[2]  = MAX_IFSD_SIZE; /* LEN - Size of the APDU    */
      ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00;  /* N(R)          */
      dwOffset      = MAX_IFSD_SIZE*i;            /* Buffer Offset */
      memcpy( &ucTBuffer[3], &pucTBuffer[dwOffset], MAX_IFSD_SIZE );
      ucTBuffer[3 + MAX_IFSD_SIZE] = T1CalculateLRC( ucTBuffer,
						     MAX_IFSD_SIZE + 3 );

      dwRBufferLen = MAX_BUFFER_SIZE;
      T1_ACSTransaction( Lun, slotIndex, ucTBuffer, 4+MAX_IFSD_SIZE,
			 ucRBuffer, &dwRBufferLen );
      tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
	switch( tv ) {
		UCHAR ucWtx;
		case T1_SBLOCK_WTXREQUEST:
			ucWtx = ucRBuffer[3];
			T1_WTXResponse( Lun, slotIndex, ucWtx, ucRBuffer );
			break;
		case T1_ERROR_OTHER:
			#ifdef T1_DEBUG
			printf("T1_ERROR_OTHER, retrying \n");
			#endif
			ucSChainNum += 1;
			goto retryblockb;
			break;
		default:
			break;
	}

	ucSChainNum += 1;
    }

	#ifdef T1_DEBUG
	printf("CASE 2: transmission cycle terminated, sending remaining %ld data...\n", dwRemaining);
	#endif

    ucTBuffer[0]  = 0x00;          /* NAD - Addressing not used */
    ucTBuffer[1]  = 0x00;          /* PCB - Chaining done       */
    ucTBuffer[2]  = dwRemaining;   /* LEN - Size of the APDU    */
    ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00;  /* N(R)          */
    dwOffset      = MAX_IFSD_SIZE*i;           /* Buffer Offset */
    memcpy( &ucTBuffer[3], &pucTBuffer[dwOffset], dwRemaining );
    ucTBuffer[3 + dwRemaining] = T1CalculateLRC( ucTBuffer,
						   dwRemaining + 3 );

    dwRBufferLen = MAX_BUFFER_SIZE;
    rv = T1_ACSTransaction(Lun, slotIndex, ucTBuffer, dwRemaining+4, ucRBuffer, &dwRBufferLen );


		tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
		#ifdef T1_DEBUG
		printf(">>>>>>>>>>>>>>>>>>>>> %02x %lx\n", ucRBuffer[1], tv);
		#endif
		switch( tv ) {
			UCHAR ucWtx;

			case T1_SBLOCK_WTXREQUEST:
				#ifdef T1_DEBUG
				printf("T1_SBLOCK_WTXREQUEST\n");
				#endif
				ucWtx = ucRBuffer[3];
				T1_WTXResponse( Lun, slotIndex, ucWtx, ucRBuffer );
				break;
			case T1_ERROR_OTHER:
				#ifdef T1_DEBUG
				printf("WARNING !!! T1_ERROR_OTHER, retring from retryblockb!!!\n");
				#endif
				ucSChainNum += 1;
				goto retryblockb; // ?????
				break;
			default:
				break;
		}

		iPrevSequence = ucRBuffer[1] & 0x40;
		#ifdef T1_DEBUG
		printf("CASE 2: more data: %s\n", ucRBuffer[1]&0x20? "AVAILABLE":"NOT AVAILABLE");
		printf("CASE 2: current sequence number = 0x%02X \n", iPrevSequence);

		printf("CASE 2: transmission completed, now receiveing (block 1 should be already be received, size=%d)\n", ucRBuffer[2]);
		#endif

		if ( ucRBuffer[2] > 0 && rv == STATUS_SUCCESS ) {
			#ifdef T1_DEBUG
			printf("CASE 2: first received block was copyed in out buffer because ucRBuffer[2]=%d >0\n", ucRBuffer[2]);
			#endif
			memcpy( ucRTemp, &ucRBuffer[3], ucRBuffer[2] );
			dwRCount = ucRBuffer[2];
		}
		else
		{
			#ifdef T1_DEBUG
			printf("CASE 2: first received block was discarded ucRBuffer[2]=%d <= 0\n", ucRBuffer[2]);
			#endif
		}


		//????????????????????????????????????????????????????????????????????????????????????????????????
		ucSChainNum += 1;
		/* Start of loop which checks the More Data bit after every transfer
		to determine whether or not another R-block must be sent
		*/
		if ( ucRBuffer[1] & 0x20 )  /* PCB More Data Bit */
		{
			#ifdef T1_DEBUG
			printf("CASE 2: MORE data bit set, starting recv loop\n");
			#endif

			do {
				/* Create the R-block */
				ucTBuffer[0] = 0x00;                            /* DF Nad   */
				ucTBuffer[1] = (ucRChainNum%2)?0x90:0x80;        /* N(R)     */
				ucTBuffer[2] = 0x00;                            /* Len  = 0 */
				ucTBuffer[3] = T1CalculateLRC( ucTBuffer, 3 );  /* Lrc      */
				dwRBufferLen = MAX_BUFFER_SIZE;
				rv = T1_ACSTransaction( Lun, slotIndex, ucTBuffer,4,ucRBuffer,&dwRBufferLen );
				tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
				#ifdef T1_DEBUG
				printf(">>>>>>>>>>>>>>>>>>>>> %02x %lx\n", ucRBuffer[1], tv);
				#endif
				switch( tv ) {
					UCHAR ucWtx;

					case T1_SBLOCK_WTXREQUEST:
					#ifdef T1_DEBUG
						printf("T1_SBLOCK_WTXREQUEST\n");
					#endif
						ucWtx = ucRBuffer[3];
						T1_WTXResponse( Lun, slotIndex, ucWtx, ucRBuffer );
						break;
					case T1_ERROR_OTHER:
						#ifdef T1_DEBUG
						printf("WARNING !!! T1_ERROR_OTHER, retring from retryblockb!!!\n");
						#endif
						ucSChainNum += 1;
						goto retryblockb;
						break;
					default:
						break;
				}

				if (rv != STATUS_SUCCESS) {
					#ifdef T1_DEBUG
					printf("STATUS_DEVICE_PROTOCOL_ERROR\n");
					#endif
					return STATUS_DEVICE_PROTOCOL_ERROR;
				}

				#ifdef T1_DEBUG
				printf("CASE 2: current sequence number = 0x%02X, previous = 0x%02X\n", ucRBuffer[1]&0x40, iPrevSequence);
				#endif

				if(iPrevSequence != (ucRBuffer[1]&0x40) ) {

					memcpy( &ucRTemp[dwRCount], &ucRBuffer[3], ucRBuffer[2] );
					dwRCount   += ucRBuffer[2];
					iPrevSequence = ucRBuffer[1]&0x40;
					#ifdef T1_DEBUG
					printf("CASE 2: received block was copyed in out buffer; current sequence number is: 0x%02X\n", iPrevSequence);
					#endif
				}
				else
				{
					#ifdef T1_DEBUG
					printf("CASE 2: received block was discarded; current sequence number is: 0x%02X\n", iPrevSequence);
					#endif
				}

				ucRChainNum += 1;

			} while ( ucRBuffer[1] & 0x20 );

			#ifdef T1_DEBUG
			printf("CASE 2: receive cycle completed\n");
			#endif

		}
		#ifdef PCSC_DEBUG
		printf("Full T=1 Response Data APDU: ");
		for (i=0; i < dwRCount; i++) {
			printf("%x ", ucRTemp[i]);
		} printf("\n");
		#endif

		#ifdef T1_DEBUG
		printf("***************** Full T=1 Response Data APDU: \n");
		printf("-->\n");
		for (i=0; i < dwRCount; i++) {
			if ((i>0)&&(i%16)==0) printf("\n");
			printf("%02x ", ucRTemp[i]);
		} printf("\n");
		#endif

		//????????????????????????????????????????????????????????????????????????????????????????????????


	}

	*dwRLength = dwRCount;
	memcpy( pucRBuffer, ucRTemp, dwRCount );

	return rv;
}

ULONG T1_ACSTransaction( DWORD Lun, DWORD slotIndex, PUCHAR pRequest, ULONG RequestLen,
                         PUCHAR pReply, PULONG pReplyLen ) {

  ULONG rv;
  UCHAR ucTxBuffer[MAX_BUFFER_SIZE*2]; /* For byte splitting */

  ucTxBuffer[0] = 0x01;
  ucTxBuffer[1] = slotIndex == 0 ? 0xA1 : 0xB1;
  ucTxBuffer[2] = RequestLen / 256;
  ucTxBuffer[3] = RequestLen % 256;
  memcpy(&ucTxBuffer[4], pRequest, RequestLen);

  //ucTxBuffer[RequestLen+4] = Adm_CheckSum( ucTxBuffer, RequestLen+4 );

#ifdef T1_DEBUG
{
  int i;
  printf(">>>>>T1_ACSTransaction ucTxBuffer = [");
  for (i=0;i<RequestLen+4;i++) {
    if ((i>0)&&((i%16)==0)) printf("\n");
    printf("%02x ",ucTxBuffer[i]);
  }
  printf("]\n");
}
#endif
  rv = Adm_Transmit( Lun, ucTxBuffer, RequestLen+4, pReply, pReplyLen );
#ifdef T1_DEBUG
{
  int i;
  printf(">>>>>T1_ACSTransaction pReply = [\n");
  for (i=0;i<*pReplyLen;i++) {
    if ((i>0)&&((i%16)==0)) printf("\n");
    printf("%02x ",pReply[i]);
  }
  printf("\n]\n");
}
#endif
  return rv;
}

