/*
    icc_sync.c
    Synchronous ICC's handling functions

    This file is part of the Unix driver for Towitoko smartcard readers
    Copyright (C) 2000 Carlos Prados <cprados@yahoo.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "defines.h"
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "icc_sync.h"

/*
 * Not exported constants definition
 */

#define ICC_SYNC_MAX_MEMORY		8192
#define ICC_SYNC_MAX_TRANSMIT		256	/*  This is the maximum
						   to increase performance
						   without bypassing
						   address counter */
#define ICC_SYNC_I2C_MAX_RETRIES	2
#define ICC_SYNC_I2C_RETRY_TRIGGER	1
#define ICC_SYNC_BAUDRATE		115200L


/*
 * Not exported macros definition
 */

#define ICC_SYNC_NEEDS_PIN(icc)		(((icc)->type == ICC_SYNC_2W || \
					(icc)->type == ICC_SYNC_3W) && \
					(icc)->pin_needed)
#define ICC_SYNC_NEEDS_ACTIVATE(icc)	(!(icc)->active)
#define ICC_SYNC_NEEDS_DEACTIVATE(icc)	((icc)->type != ICC_SYNC_3W && \
					(icc)->active)

/*
 * Not exported functions declaration
 */

static int ICC_Sync_ProbeCardType (ICC_Sync * icc);
static int ICC_Sync_ProbeMemoryLength (ICC_Sync * icc);
static int ICC_Sync_ProbePagemode (ICC_Sync * icc);
static ATR_Sync * ICC_Sync_CreateAtr(ICC_Sync * icc);
static void ICC_Sync_Clear (ICC_Sync * icc);

/*
 * Exported functions definition
 */

ICC_Sync *
ICC_Sync_New (void)
{
  ICC_Sync *icc;

  /* Allocate memory */
  icc = (ICC_Sync *) malloc (sizeof (ICC_Sync));

  if (icc != NULL)
    ICC_Sync_Clear (icc);

  return icc;
}

void
ICC_Sync_Delete (ICC_Sync * icc)
{
  free (icc);
}

int
ICC_Sync_Init (ICC_Sync * icc, IFD * ifd)
{
#ifndef ICC_TYPE_ASYNC
  int ret;
  
  /* LED Red */
  if (IFD_Towitoko_SetLED (ifd, IFD_TOWITOKO_LED_RED) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;
  
  /* Initialize baudrate*/
  if (IFD_Towitoko_SetBaudrate (ifd, ICC_SYNC_BAUDRATE) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  /* Activate ICC */
  if (IFD_Towitoko_ActivateICC (ifd) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  /* Reset ICC */
  if (IFD_Towitoko_ResetSyncICC (ifd, &(icc->atr)) != IFD_TOWITOKO_OK)
    {
      icc->atr = NULL;
      return ICC_SYNC_IFD_ERROR;
    }

  /* Intialice member variables */
  icc->active = TRUE;
  icc->baudrate = ICC_SYNC_BAUDRATE;
  icc->ifd = ifd;
  icc->pin_ok = FALSE;
  icc->pin_needed = TRUE;

  /* Probe Card type  */
  ret = ICC_Sync_ProbeCardType (icc);
  if (ret != ICC_SYNC_OK)
    {
      ICC_Sync_Clear (icc);
      return ret;
    }

  /* Probe memory length */
  ret = ICC_Sync_ProbeMemoryLength (icc);
  if (ret != ICC_SYNC_OK)
    {
      ICC_Sync_Clear (icc);
      return ret;
    }

  /* Probe pagemode */
  ret = ICC_Sync_ProbePagemode (icc);
  if (ret != ICC_SYNC_OK)
    {
      ICC_Sync_Clear (icc);
      return ret;
    }

  /* Create fake ATR if card haven't one */
  if (icc->atr == NULL)
    icc->atr = ICC_Sync_CreateAtr (icc);

  /* LED Green */
  if (IFD_Towitoko_SetLED (ifd, IFD_TOWITOKO_LED_GREEN) != IFD_TOWITOKO_OK)
    {
      ICC_Sync_Clear (icc);
      return ICC_SYNC_IFD_ERROR;
    }

  if (ICC_SYNC_NEEDS_DEACTIVATE (icc))
    {
      if (IFD_Towitoko_DeactivateICC (icc->ifd) != IFD_TOWITOKO_OK)
	{
	  ICC_Sync_Clear (icc);
	  return ICC_SYNC_IFD_ERROR;
	}

      icc->active = FALSE;
    }

  return ICC_SYNC_OK;
#else
  return ICC_SYNC_DETECT_ERROR;
#endif
}

int
ICC_Sync_Close (ICC_Sync * icc)
{
  /* Deactivate ICC */
  if (IFD_Towitoko_DeactivateICC (icc->ifd) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  /* LED Off */
  if (IFD_Towitoko_SetLED (icc->ifd, IFD_TOWITOKO_LED_OFF) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  if (icc->atr != NULL)
    ATR_Sync_Delete (icc->atr);

  ICC_Sync_Clear (icc);

  return ICC_SYNC_OK;
}

int
ICC_Sync_Read (ICC_Sync * icc, unsigned short address, unsigned length,
	       BYTE * data)
{
  /* Re-activate card before reset address counter */
  if (ICC_SYNC_NEEDS_ACTIVATE (icc))
    {
      if (IFD_Towitoko_ActivateICC (icc->ifd) != IFD_TOWITOKO_OK)
	return ICC_SYNC_IFD_ERROR;

      icc->active = TRUE;
    }

  /* Read access */
  if (IFD_Towitoko_SetReadAddress (icc->ifd, icc->type, address) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  if (IFD_Towitoko_ReadBuffer (icc->ifd, length, data) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  if (ICC_SYNC_NEEDS_DEACTIVATE (icc))
    {
      if (IFD_Towitoko_DeactivateICC (icc->ifd) != IFD_TOWITOKO_OK)
	return ICC_SYNC_IFD_ERROR;

      icc->pin_needed = TRUE;
      icc->active = FALSE;
    }

  return ICC_SYNC_OK;
}

int
ICC_Sync_Write (ICC_Sync * icc, unsigned short address, unsigned length,
		BYTE * data)
{
  BYTE buffer[ICC_SYNC_MAX_TRANSMIT], mask;
  unsigned written, to_write, retries, max_retries, trials;
  int ret;

  if (length > ICC_SYNC_I2C_RETRY_TRIGGER)
    {
      /* At least one retry is needed for writting I2C */
      if ((icc->type == ICC_SYNC_I2C_SHORT)
	  || icc->type == (ICC_SYNC_I2C_LONG))
	max_retries = ICC_SYNC_I2C_MAX_RETRIES;
      else
	max_retries = 1;
    }
  else
    max_retries = 1;

  /* 
   * Divide data into smaller buffers to:
   *    - Don't bypass low byte of write address counter
   *    - Compare data written and read to see if memory is Read Only
   *    - Retry when first writting fails with I2C cards
   */
  for (written = 0; written < length; written += to_write)
    {
      /* See how many bytes can be written to the current page */
      mask = icc->pagemode - 0x01;
      
      to_write =
      MIN (MIN (ICC_SYNC_MAX_TRANSMIT, length - written),
     (((address + written) | mask) + 1) - (address + written));

      /* Repeat Until to_write bytes are written or max_retries are reached */
      retries = 0;
      do
	{
	  /* Re-activate card before reset address */
	  if (ICC_SYNC_NEEDS_ACTIVATE (icc))
	    {
	      if (IFD_Towitoko_ActivateICC (icc->ifd) != IFD_TOWITOKO_OK)
		return ICC_SYNC_IFD_ERROR;

	      icc->active = TRUE;

	      /* 2W cards needs to re-enter PIN */
	      if (icc->pin_ok && ICC_SYNC_NEEDS_PIN (icc))
		{
		  ret = ICC_Sync_EnterPin (icc, icc->pin, &trials);
		  if (ret != ICC_SYNC_OK)
		    return ret;
		}
	    }

	  /* Write access */
	  if (IFD_Towitoko_SetWriteAddress (icc->ifd, icc->type,
					    address + written,
					    icc->pagemode) != IFD_TOWITOKO_OK)
	    return ICC_SYNC_IFD_ERROR;

	  if (IFD_Towitoko_WriteBuffer (icc->ifd, to_write, data + written) !=
	      IFD_TOWITOKO_OK)
	    return ICC_SYNC_IFD_ERROR;

	  if (ICC_SYNC_NEEDS_DEACTIVATE (icc))
	    {
	      if (IFD_Towitoko_DeactivateICC (icc->ifd) != IFD_TOWITOKO_OK)
		return ICC_SYNC_IFD_ERROR;

	      icc->pin_needed = TRUE;
	      icc->active = FALSE;
	    }

	  /* See if the buffer has been written */
	  ret = ICC_Sync_Read (icc, address + written, to_write, buffer);

	  if (ret != ICC_SYNC_OK)
	    return ret;
	}
      while ((memcmp (data + written, buffer, to_write) != 0) &&
	     ((++retries) < max_retries));

      if (retries == max_retries)
	return ICC_SYNC_RO_ERROR;

      if (IFD_Towitoko_GetType (icc->ifd) == IFD_TOWITOKO_CHIPDRIVE_INT)
	{
#ifdef HAVE_NANOSLEEP
	  struct timespec req_ts;

	  req_ts.tv_sec = 0;
	  req_ts.tv_nsec = 90000000;
	  nanosleep (&req_ts, NULL);
#else
	  usleep (90000);
#endif
	}
    }

  return ICC_SYNC_OK;
}

int
ICC_Sync_EnterPin (ICC_Sync * icc, BYTE * pin, unsigned *trials)
{
  unsigned new_trials;

  /* See if card needs PIN */
  if (icc->type == ICC_SYNC_I2C_LONG || icc->type == ICC_SYNC_I2C_SHORT)
    return ICC_SYNC_OK;

  /* 2W ICC's needs to be re-activated before entering PIN */
  if (ICC_SYNC_NEEDS_ACTIVATE (icc))
    {
      if (IFD_Towitoko_ActivateICC (icc->ifd) != IFD_TOWITOKO_OK)
	return ICC_SYNC_IFD_ERROR;

      icc->active = TRUE;
    }

  /* Get error counter and stop if it is exahusted */
  if (IFD_Towitoko_ReadErrorCounter (icc->ifd, icc->type, trials) !=
      IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  if ((*trials) == 0)
    return ICC_SYNC_BLOCKED_ERROR;

  /* Enter PIN */
  if (IFD_Towitoko_EnterPin (icc->ifd, icc->type, pin, (*trials)) !=
      IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  /* See if the error counter has maximum value */
  if (IFD_Towitoko_ReadErrorCounter (icc->ifd, icc->type, &new_trials) !=
      IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  if (new_trials < (*trials))
    {
      icc->pin_ok = FALSE;
      (*trials) = new_trials;
      return ICC_SYNC_PIN_ERROR;
    }

  memcpy (icc->pin, pin, ICC_SYNC_PIN_SIZE);
  icc->pin_ok = TRUE;
  icc->pin_needed = FALSE;

  (*trials) = new_trials;
  return ICC_SYNC_OK;
}

int
ICC_Sync_ChangePin (ICC_Sync * icc, BYTE * pin)
{
  unsigned trials;
  int ret;

  /* See if card needs PIN */
  if (icc->type == ICC_SYNC_I2C_LONG || icc->type == ICC_SYNC_I2C_SHORT)
    return ICC_SYNC_OK;

  /* Re-activate card */
  if (ICC_SYNC_NEEDS_ACTIVATE (icc))
    {
      if (IFD_Towitoko_ActivateICC (icc->ifd) != IFD_TOWITOKO_OK)
	return ICC_SYNC_IFD_ERROR;

      icc->active = TRUE;

      /* 2W cards needs to re-enter PIN */
      if (icc->pin_ok && ICC_SYNC_NEEDS_PIN (icc))
	{
	  ret = ICC_Sync_EnterPin (icc, icc->pin, &trials);
	  if (ret != ICC_SYNC_OK)
	    return ret;
	}
    }

  /* Change PIN */
  if (IFD_Towitoko_ChangePin (icc->ifd, icc->type, pin) != IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  memcpy (icc->pin, pin, ICC_SYNC_PIN_SIZE);
  icc->pin_ok = TRUE;
  icc->pin_needed = FALSE;

  return ICC_SYNC_OK;
}

int
ICC_Sync_BeginTransmission (ICC_Sync * icc)
{
  /* Setup baudrate for this ICC */
  if (IFD_Towitoko_SetBaudrate (icc->ifd, ICC_SYNC_BAUDRATE) !=
      IFD_TOWITOKO_OK)
    return ICC_SYNC_IFD_ERROR;

  return ICC_SYNC_OK;
}

int
ICC_Sync_SetBaudrate (ICC_Sync * icc, unsigned long baudrate)
{
  icc->baudrate = baudrate;
  return ICC_SYNC_OK;
}

int
ICC_Sync_GetBaudrate (ICC_Sync * icc, unsigned long *baudrate)
{
  (*baudrate) = icc->baudrate;

  return ICC_SYNC_OK;
}

unsigned
ICC_Sync_GetLength (ICC_Sync * icc)
{
  return icc->length;
}

int
ICC_Sync_GetType (ICC_Sync * icc)
{
  return icc->type;
}

BYTE
ICC_Sync_GetPagemode (ICC_Sync * icc)
{
  return icc->pagemode;
}

IFD *
ICC_Sync_GetIFD (ICC_Sync * icc)
{
  return icc->ifd;
}

ATR_Sync *
ICC_Sync_GetAtr (ICC_Sync * icc)
{
  return icc->atr;
}

/*
 * Not exported functions definition
 */

static int
ICC_Sync_ProbeCardType (ICC_Sync * icc)
{
#ifndef ICC_SYNC_MEMORY_TYPE
  BYTE protocol, status[1], orig[1], modif[1];
  int ret;
  
  if (icc->atr != NULL)
    {
      ret = ICC_SYNC_OK;

      protocol = ATR_Sync_GetProtocolType(icc->atr);
       
      if (protocol == ATR_SYNC_PROTOCOL_TYPE_3W)
        icc->type = ICC_SYNC_3W;
        
      else if (protocol == ATR_SYNC_PROTOCOL_TYPE_2W)
        icc->type = ICC_SYNC_2W;
        
      else if (protocol == ATR_SYNC_PROTOCOL_TYPE_SDA)
        icc->type = ICC_SYNC_I2C_SHORT;
        
      else
      {
        ret = ICC_SYNC_DETECT_ERROR;
#ifdef DEBUG_ICC
	printf ("ICC: Detected synchronous card with unknown ATR\n");
#endif 
      }
    }
    
  else
    { 
      /* Check for I2C (short or long) */
      IFD_Towitoko_SetReadAddress (icc->ifd,  IFD_TOWITOKO_I2C_SHORT, 0);
      IFD_Towitoko_GetStatus  (icc->ifd, status);
      IFD_Towitoko_DeactivateICC (icc->ifd);

      if ((status[0] & 0x10) != 0x10)
        {
	  /* Check for I2CX card */
	  icc->type = IFD_TOWITOKO_I2C_SHORT;

          ICC_Sync_Read (icc, 0, 1, orig);

	  if (orig[0] == 0xFF)
	    modif[0] = 0x01;
	  else if (orig[0] == 0x00)
	    modif[0] = 0xFE;
	  else
	    modif[0] = ~orig[0];

	  if (ICC_Sync_Write (icc, 0, 1, modif) == ICC_SYNC_OK)
	    ICC_Sync_Write (icc, 0, 1, orig);
	  else
            icc->type = IFD_TOWITOKO_I2C_LONG;
	
          IFD_Towitoko_ActivateICC (icc->ifd);
          
          ret = ICC_SYNC_OK;
        }
      
      else
        ret = ICC_SYNC_DETECT_ERROR;
    }

#ifdef DEBUG_ICC
  if (ret == ICC_SYNC_OK)
    printf ("ICC: Detected %s memory card\n", 
  	  icc->type == 0? "I2C short": 
  	  icc->type == 1? "I2C long": 
  	  icc->type == 2? "2-wire bus protocol":
  	  icc->type == 3? "3-wire bus protocol": 
  	  "invalid");
#endif

  return ret;

#else
  icc->type = ICC_SYNC_MEMORY_TYPE;

  return ICC_SYNC_OK;
#endif
}

static int
ICC_Sync_ProbeMemoryLength (ICC_Sync * icc)
{
  int ret;
#ifndef ICC_SYNC_MEMORY_LENGTH
  unsigned min, max;
  BYTE status[1];

  if (icc->atr != NULL)
    {
      icc->length = ATR_Sync_GetNumberOfDataUnits(icc->atr) * ATR_Sync_GetLengthOfDataUnits(icc->atr) / 8;
      ret = ICC_SYNC_OK;
    }

  else
    {
      if  (icc->type == ICC_SYNC_I2C_SHORT)
        {
	  min = 256L;
	  max = 2048L;
	}
      else if (icc->type == ICC_SYNC_I2C_LONG)
        {
	  min = 2048L;
	  max = 32768L;
	}
      else
	{
	  min = 256L;
	  max = 2048L;
	}

      for (icc->length = min; icc->length<max; icc->length*=2)
        {
          IFD_Towitoko_SetReadAddress (icc->ifd,  icc->type, icc->length);
          IFD_Towitoko_GetStatus  (icc->ifd, status);
          IFD_Towitoko_DeactivateICC (icc->ifd);
          IFD_Towitoko_ActivateICC (icc->ifd);

          if ((status[0] & 0x10) == 0x10)
	    break;
	}

      ret = ICC_SYNC_OK;
    }

#else
  icc->length = ICC_SYNC_MEMORY_LENGTH;
  ret = ICC_SYNC_OK;
#endif

#ifdef DEBUG_ICC
  printf ("ICC: Memory size = %d\n", icc->length);
#endif

  return ret;
}

static int
ICC_Sync_ProbePagemode (ICC_Sync * icc)
{
  if (icc->type == ICC_SYNC_I2C_LONG)
    icc->pagemode = 0x40;
  else
    icc->pagemode = 0x00;

  return ICC_SYNC_OK;
}

static ATR_Sync * 
ICC_Sync_CreateAtr(ICC_Sync * icc)
{
  ATR_Sync * atr;
  BYTE atr_buffer[ATR_SYNC_SIZE], protocol;

  if (icc->atr !=NULL)
    return icc->atr;

  protocol = (icc->type==ICC_SYNC_I2C_SHORT)? ATR_SYNC_PROTOCOL_TYPE_SDA:
	(icc->type==ICC_SYNC_I2C_LONG)? ATR_SYNC_PROTOCOL_TYPE_SDA:
	(icc->type==ICC_SYNC_2W)? ATR_SYNC_PROTOCOL_TYPE_2W:
	(icc->type==ICC_SYNC_3W)? ATR_SYNC_PROTOCOL_TYPE_3W: ATR_SYNC_PROTOCOL_TYPE_SDA;
  
  atr_buffer[0] = (protocol << 4) | ATR_SYNC_STRUCTURE_ID_GENERAL;
  atr_buffer[1] = (icc->length == 256L)? 0x13:
	  (icc->length == 512L)? 0x1B:
	  (icc->length == 1024L)? 0x23:
	  (icc->length == 2048L)? 0x2B:
	  (icc->length == 4096L)? 0x33:
	  (icc->length == 8192L)? 0x3B: 
	  (icc->length == 16384)? 0x43:
	  (icc->length == 32768)? 0x4B: 0x4B;
  atr_buffer[2] = 0x10;
  atr_buffer[3] = 0x84;

  atr = ATR_Sync_New();
  if (atr!=NULL)
    ATR_Sync_Init (atr, atr_buffer, ATR_SYNC_SIZE);
  
  return atr;
}

static void
ICC_Sync_Clear (ICC_Sync * icc)
{
  icc->ifd = NULL;
  icc->atr = NULL;
  icc->type = 0;
  icc->length = 0;
  icc->pagemode = 0x00;
  memset (icc->pin, 0, ICC_SYNC_PIN_SIZE);
  icc->pin_ok = FALSE;
  icc->pin_needed = FALSE;
  icc->active = FALSE;
  icc->baudrate = 0L;
}


syntax highlighted by Code2HTML, v. 0.9.1