/*
    cardterminal.c
    Card Terminal handling and CT-BCS 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 "cardterminal.h"
#include "atr.h"
#include "atr_sync.h"
#include <stdlib.h>
#include <string.h>

/*
 * Not exported constants definition
 */

#define CARDTERMINAL_RESETCT_BUFFER_SIZE	35
#define CARDTERMINAL_REQUESTICC_BUFFER_SIZE	35
#define CARDTERMINAL_GETSTATUS_BUFFER_SIZE	19
#define CARDTERMINAL_EJECTICC_BUFFER_SIZE	2
#define CARDTERMINAL_MANUFACTURER		"DETWK"

/* 
 * Not exported functions declaration
 */

static char
CardTerminal_ResetCT (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp); 

static char
CardTerminal_RequestICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp);

static char
CardTerminal_GetStatus (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp);

static char
CardTerminal_EjectICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp);

static void 
CardTerminal_Clear (CardTerminal * ct);

/*
 * Exported functions definition
 */

CardTerminal *
CardTerminal_New (void)
{
  CardTerminal *ct;

  ct = (CardTerminal *) malloc (sizeof (CardTerminal));

  if (ct != NULL)
    CardTerminal_Clear (ct);

  return ct;
}

char
CardTerminal_Init (CardTerminal * ct, unsigned short pn)
{
  char ret;
  int i;
  bool usbserial;

  /* Create a new IO_Serial */
  ct->io = IO_Serial_New ();

  /* Memory error */
  if (ct->io == NULL)
    return ERR_MEMORY;

  /* 
   * Handle USB port numbers: first USB serial port starts at 0x8000,
   * 0x8001 if CTAPI_WIN32_COM is set 
   */
  if ((pn & 0x8000) == 0x8000)
    {
      usbserial = TRUE;
      pn &= 0x7FFF;
    }
  else
    usbserial = FALSE;
      
  /* Initialise serial port */
#ifndef CTAPI_WIN32_COM
  if (!IO_Serial_Init (ct->io, pn + 1, usbserial, TRUE))
#else
  if (!IO_Serial_Init (ct->io, pn, usbserial, TRUE))
#endif
    {
      IO_Serial_Delete (ct->io);
      ct->io = NULL;
      return ERR_TRANS;
    }

  /* Cearte all reader slots */
  ct->num_slots = 0;
  do
    {
      i = ct->num_slots++;

      /* Create one slot */
      ct->slots[i] = CT_Slot_New ();

      if (ct->slots[i] == NULL)
	{
	  ret = ERR_MEMORY;
	  break;
	}

      /* Initialise slot */
      ret = CT_Slot_Init (ct->slots[i], ct->io, i);

      if (ret != OK)
	break;
    }
  while (!CT_Slot_IsLast(ct->slots[i]));

  /* On error restore initial state */
  if (ret != OK)
    {
      while (ct->num_slots > 0)
	{
	  if (ct->slots[i] != NULL)
	    {
	      CT_Slot_Delete (ct->slots[i]);
	      ct->slots[i] = NULL;
	    }

	  ct->num_slots --;
	  i--;
	}
      
      IO_Serial_Close (ct->io);
      IO_Serial_Delete (ct->io);
      ct->io = NULL;
    }
#ifdef HAVE_PTHREAD_H
    else
      pthread_mutex_init(&(ct->mutex), NULL);
#endif
  return ret;
}

char
CardTerminal_Command (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  BYTE buffer[CTBCS_MIN_RESPONSE_SIZE], cla, ins;
  long length;
  char ret;

  /* Get class of command */
  cla = APDU_Cmd_Cla (cmd);

  if (cla != CTBCS_CLA)
    {
      length = CTBCS_MIN_RESPONSE_SIZE;
      buffer[0] = CTBCS_SW1_WRONG_CLA;
      buffer[1] = CTBCS_SW2_WRONG_CLA;

      (*rsp) = APDU_Rsp_New (buffer, length);
      ret = OK;
    }

  else
    {
      /* Get instruction */
      ins = APDU_Cmd_Ins (cmd);

      /* Reset CT */
      if (ins == CTBCS_INS_RESET)
	ret = CardTerminal_ResetCT (ct, cmd, rsp);

      /* Request ICC */
      else if (ins == CTBCS_INS_REQUEST)
	ret = CardTerminal_RequestICC (ct, cmd, rsp);

      /* Get Status */
      else if (ins == CTBCS_INS_STATUS)
	ret = CardTerminal_GetStatus (ct, cmd, rsp);

      /* Eject ICC */
      else if (ins == CTBCS_INS_EJECT)
	ret = CardTerminal_EjectICC (ct, cmd, rsp);

      /* Wrong instruction */
      else
	{
	  length = CTBCS_MIN_RESPONSE_SIZE;
	  buffer[0] = CTBCS_SW1_WRONG_INS;
	  buffer[1] = CTBCS_SW2_WRONG_INS;

	  (*rsp) = APDU_Rsp_New (buffer, length);
	  ret = OK;
	}
    }

  return ret;
}

char
CardTerminal_Close (CardTerminal * ct)
{
  char ret, aux;
  int i;

  ret = OK;

  for (i = 0; i < ct->num_slots; i++)
    {
      if (ct->slots[i] != NULL)
	{
	  aux = CT_Slot_Close (ct->slots[i]);
	  if (aux != OK)
	    ret = aux;

	  CT_Slot_Delete (ct->slots[i]);
	}
    }

  if (ct->io != NULL)
    {
      if (!IO_Serial_Close (ct->io))
	ret = ERR_TRANS;

      IO_Serial_Delete (ct->io);
    }

  CardTerminal_Clear (ct);

#ifdef HAVE_PTHREAD_H
  pthread_mutex_destroy(&(ct->mutex));
#endif
  return ret;
}

void
CardTerminal_Delete (CardTerminal * ct)
{
  free (ct);
}

CT_Slot *
CardTerminal_GetSlot (CardTerminal * ct, int number)
{
  if (number < (ct->num_slots))
    return ct->slots[number];

  return NULL;
}

#ifdef HAVE_PTHREAD_H
pthread_mutex_t *
CardTerminal_GetMutex (CardTerminal * ct)
{
  return &(ct->mutex);
}
#endif

/* 
 * Not exported functions definition
 */

static char
CardTerminal_ResetCT (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  BYTE buffer[CARDTERMINAL_RESETCT_BUFFER_SIZE], p1, p2;
  bool card, change;
  unsigned sn, length;
  void *atr;
  char ret = OK;

  /* Get functional unit */
  p1 = APDU_Cmd_P1 (cmd);

  /* Funcional unit is the card-terminal */
  if (p1 == CTBCS_P1_CT_KERNEL)
    {
      /* Get command cualifier */
      p2 = APDU_Cmd_P2 (cmd);

      /* Wrong command cualifier */
      if (p2 != CTBCS_P2_RESET_NO_RESP &&
	  p2 != CTBCS_P2_RESET_GET_ATR && p2 != CTBCS_P2_RESET_GET_HIST)
	{
	  buffer[0] = CTBCS_SW1_WRONG_PARAM;
	  buffer[1] = CTBCS_SW2_WRONG_PARAM;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return OK;
	}

      /* Close slots */
      for (sn = 0; sn < ct->num_slots; sn++)
	{
	  /* Close this slot */
	  ret = CT_Slot_Close (ct->slots[sn]);

	  if (ret != OK)
	    {
	      buffer[0] = CTBCS_SW1_RESET_ERROR;
	      buffer[1] = CTBCS_SW2_RESET_ERROR;

	      (*rsp) = APDU_Rsp_New (buffer, 2);
	      return ret;
	    }

	  /* Initialise this slot */
	  ret = CT_Slot_Init (ct->slots[sn],ct->io,sn);

	  if (ret != OK)
	    {
	      buffer[0] = CTBCS_SW1_RESET_ERROR;
	      buffer[1] = CTBCS_SW2_RESET_ERROR;

	      (*rsp) = APDU_Rsp_New (buffer, 2);
	      return ret;
	    }
	}

      length = 2;
      buffer[0] = CTBCS_SW1_RESET_CT_OK;
      buffer[1] = CTBCS_SW2_RESET_CT_OK;
    }

  /* Funtional unit is an ICC */
  else if ((p1 == CTBCS_P1_INTERFACE1) || (p1 == CTBCS_P1_INTERFACE2))
    {
      /* Get slot number */
      sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1;

      if (!(sn < ct->num_slots))
	{
	  buffer[0] = CTBCS_SW1_WRONG_PARAM;
	  buffer[1] = CTBCS_SW2_WRONG_PARAM;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ERR_INVALID;
	}

      /* Release the slot */
      ret = CT_Slot_Release (ct->slots[sn]);

      if (ret != OK)
	{
	  buffer[0] = CTBCS_SW1_RESET_ERROR;
	  buffer[1] = CTBCS_SW2_RESET_ERROR;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ret;
	}

      /* Check for card */
      ret = CT_Slot_Check (ct->slots[sn], 0, &card, &change);
      
      if (ret != OK)
	{
	  buffer[0] = CTBCS_SW1_RESET_ERROR;
	  buffer[1] = CTBCS_SW2_RESET_ERROR;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ret;
	}

      /* No card present */
      if (!card)
	{
	  buffer[0] = CTBCS_SW1_RESET_ERROR;
	  buffer[1] = CTBCS_SW2_RESET_ERROR;;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return OK;
	}

      /* Probe card type */
      if (APDU_Cmd_Lc (cmd) > 1)
        ret = CT_Slot_Probe (ct->slots[sn], APDU_Cmd_Data(cmd), APDU_Cmd_Lc(cmd));
      else
        ret = CT_Slot_Probe (ct->slots[sn], NULL, 0);

      if (ret != OK || 
	  (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL))
	{
	  buffer[0] = CTBCS_SW1_RESET_ERROR;
	  buffer[1] = CTBCS_SW2_RESET_ERROR;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ret;
	}

      /* Get command cualifier */
      p2 = APDU_Cmd_P2 (cmd);

      /* Do not return data */
      if (p2 == CTBCS_P2_RESET_NO_RESP)
	{
	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      buffer[0] = CTBCS_SW1_RESET_ASYNC_OK;
	      buffer[1] = CTBCS_SW2_RESET_ASYNC_OK;
	      length = 2;
	    }
	  else
	    {
	      buffer[0] = CTBCS_SW1_RESET_SYNC_OK;
	      buffer[1] = CTBCS_SW2_RESET_SYNC_OK;
	      length = 2;
	    }
	}

      /* Return complete ATR of ICC */
      else if (p2 == CTBCS_P2_RESET_GET_ATR)
	{
	  atr = CT_Slot_GetAtr (ct->slots[sn]);

	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      if (atr != NULL)
		ATR_GetRaw ((ATR *) atr, buffer, &length);
	      else 
		length = 0;
	      
	      buffer[length] = CTBCS_SW1_RESET_ASYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_RESET_ASYNC_OK;
	      length += 2;
	    }
	  else
	    {
	      if (atr != NULL)
		ATR_Sync_GetRaw ((ATR_Sync *) atr, buffer, &length);
	      else
		length = 0;
				    
	      buffer[length] = CTBCS_SW1_RESET_SYNC_OK;
	      buffer[length +1] = CTBCS_SW2_RESET_SYNC_OK;
	      length += 2;
	    }
	}

      /* Return historical bytes of ATR */
      else if (p2 == CTBCS_P2_RESET_GET_HIST)
	{
	  atr = CT_Slot_GetAtr (ct->slots[sn]);

	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      if (atr != NULL)
		ATR_GetHistoricalBytes ((ATR *) atr, buffer, &length);
	      else
		length = 0;
	      
	      buffer[length] = CTBCS_SW1_RESET_ASYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_RESET_ASYNC_OK;
	      length += 2;
	    }
	  else
	    {
	      if (atr != NULL)
		ATR_Sync_GetHistoricalBytes ((ATR_Sync *) atr, buffer, &length);
	      else
		length = 0;

	      buffer[length] = CTBCS_SW1_RESET_SYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_RESET_SYNC_OK;
	      length +=2;
	    }
	}

      /* Wrong command cualifier */
      else
	{
	  length = 2;
	  buffer[0] = CTBCS_SW1_WRONG_PARAM;
	  buffer[1] = CTBCS_SW2_WRONG_PARAM;
	  ret = OK;
	}
    }

  /* Wrong functional unit */
  else
    {
      length = 2;
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;
      ret = OK;
    }

  (*rsp) = APDU_Rsp_New (buffer, length);
  return ret;
}

static char
CardTerminal_RequestICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  BYTE buffer[CARDTERMINAL_REQUESTICC_BUFFER_SIZE], p1, p2;
  unsigned timeout, sn, length;
  bool card, change;
  void * atr;
  char ret;

  /* Get functional unit */
  p1 = APDU_Cmd_P1 (cmd);

  if ((p1 == CTBCS_P1_INTERFACE1) || (p1 == CTBCS_P1_INTERFACE2))
    {
      /* Get the slot number */
      sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1;

      if (CT_Slot_GetICCType (ct->slots[sn]) != CT_SLOT_NULL)
	{
	  buffer[0] = CTBCS_SW1_REQUEST_CARD_PRESENT;
	  buffer[1] = CTBCS_SW2_REQUEST_CARD_PRESENT;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return OK;
	}

      /* Get the card insertion timeout */
      if (APDU_Cmd_Lc (cmd) == 1)
	timeout = (APDU_Cmd_Data (cmd)[0]);
      else
	timeout = 0;

      /* Check for card */
      ret = CT_Slot_Check (ct->slots[sn], timeout, &card, &change);
      
      if (ret != OK)
	{
	  buffer[0] = CTBCS_SW1_REQUEST_ERROR;
	  buffer[1] = CTBCS_SW2_REQUEST_ERROR;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ret;
	}

      /* No card present */
      if (!card)
	{
	  buffer[0] = CTBCS_SW1_REQUEST_NO_CARD;
	  buffer[1] = CTBCS_SW2_REQUEST_NO_CARD;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return OK;
	}

      /* Probe card type */
      if (APDU_Cmd_Lc (cmd) > 1)
        ret = CT_Slot_Probe (ct->slots[sn], APDU_Cmd_Data(cmd), APDU_Cmd_Lc(cmd));
      else
	ret = CT_Slot_Probe (ct->slots[sn], NULL, 0);

      if (ret != OK || 
	  (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL))
	{
	  buffer[0] = CTBCS_SW1_REQUEST_ERROR;
	  buffer[1] = CTBCS_SW2_REQUEST_ERROR;

	  (*rsp) = APDU_Rsp_New (buffer, 2);
	  return ret;
	}

      /* Get command cualifier */
      p2 = APDU_Cmd_P2 (cmd);

      /* Do not return data */
      if (p2 == CTBCS_P2_REQUEST_NO_RESP)
	{
	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      buffer[0] = CTBCS_SW1_REQUEST_ASYNC_OK;
	      buffer[1] = CTBCS_SW2_REQUEST_ASYNC_OK;
	      length = 2;
	    }
	  else
	    {
	      buffer[0] = CTBCS_SW1_REQUEST_SYNC_OK;
	      buffer[1] = CTBCS_SW2_REQUEST_SYNC_OK;
	      length = 2;
	    }
	}

      /* Return whole atr */
      else if (p2 == CTBCS_P2_REQUEST_GET_ATR)
	{
	  atr = CT_Slot_GetAtr (ct->slots[sn]);

	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      if (atr != NULL)
		ATR_GetRaw ((ATR *) atr, buffer, &length);
	      else
		length = 0;
	      
	      buffer[length] = CTBCS_SW1_REQUEST_ASYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_REQUEST_ASYNC_OK;
	      length += 2;
	    }
	  else
	    {
	      if (atr != NULL)
		ATR_Sync_GetRaw ((ATR_Sync *) atr, buffer, &length);
	      else
		length = 0;
	      
	      buffer[length] = CTBCS_SW1_REQUEST_SYNC_OK;
	      buffer[length +1] = CTBCS_SW2_REQUEST_SYNC_OK;
	      length += 2;
	    }
	}

      /* Return historical bytes */
      else if (p2 == CTBCS_P2_REQUEST_GET_HIST)
	{
	  atr = CT_Slot_GetAtr (ct->slots[sn]);
	  
	  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_ICC_ASYNC)
	    {
	      if (atr != NULL)
		ATR_GetHistoricalBytes ((ATR *) atr, buffer, &length);
	      else
		length = 0;

	      buffer[length] = CTBCS_SW1_REQUEST_ASYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_REQUEST_ASYNC_OK;
	      length += 2;
	    }
	  else
	    {
	      if (atr != NULL)
		ATR_Sync_GetHistoricalBytes ((ATR_Sync *) atr, buffer, &length);
	      else
		length = 0;

	      buffer[length] = CTBCS_SW1_REQUEST_SYNC_OK;
	      buffer[length + 1] = CTBCS_SW2_REQUEST_SYNC_OK;
	    }
	}

      /* Wrong command cualifier */
      else
	{
	  length = 2;
	  buffer[0] = CTBCS_SW1_WRONG_PARAM;
	  buffer[1] = CTBCS_SW2_WRONG_PARAM;
	  ret = OK;
	}
    }

  /* Wrong functional unit */
  else
    {
      length = 2;
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;
      ret = OK;
    }

  (*rsp) = APDU_Rsp_New (buffer, (long)length);
  return ret;
}

static char
CardTerminal_GetStatus (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  BYTE buffer[CARDTERMINAL_GETSTATUS_BUFFER_SIZE], p1, p2;
  bool card, change;
  int i;
  unsigned length;
  char ret = OK;

  /* Get functional unit */
  p1 = APDU_Cmd_P1 (cmd);

  /* Wrong functional unit */
  if (p1 != CTBCS_P1_CT_KERNEL)
    {
      length = 2;
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;
      ret = OK;
    }

  /* Get command cualifier */
  p2 = APDU_Cmd_P2 (cmd);

  if (p2 == CTBCS_P2_STATUS_MANUFACTURER)
    {
      length = 17;

      /* CT Manufacturer */ 
      memcpy(buffer,CARDTERMINAL_MANUFACTURER,5);
  
      /* CT type */
      if (ct->slots[0] != NULL)
	CT_Slot_GetType(ct->slots[0], buffer + 5,5);

      /* CT version */
      memcpy(buffer+10,VERSION,5);

      buffer[15] = CTBCS_SW1_OK;
      buffer[16] = CTBCS_SW2_OK;
      ret = OK;
    }

  else if (p2 == CTBCS_P2_STATUS_ICC)
    {
      for (i = 0; i < ct->num_slots; i++)
	{
	  ret = CT_Slot_Check (ct->slots[i], 0, &card, &change);
	  
	  if (ret != OK)
	    {
	      /* There are no status bytes defined to be returned on error */
	      (*rsp) = NULL;
	      return ret;
	    }

	  /* Resynchronise the driver status with the actual status of slot */
	  if ((CT_Slot_GetICCType (ct->slots[i]) != CT_SLOT_NULL) && 
	      (!card || change))
	    {
	      ret = CT_Slot_Release (ct->slots[i]);

	      if (ret != OK)
	        {
		  (*rsp) = NULL;
		  return ret;
		}
	    }

	  buffer[i] = card? CTBCS_DATA_STATUS_CARD_CONNECT: CTBCS_DATA_STATUS_NOCARD;
	}
      
      length = i+2;
      buffer[i] = CTBCS_SW1_OK;
      buffer[i+1] = CTBCS_SW2_OK;
    }
  
  /* Wrong command cualifier */
  else 
    {
      length = 2;
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;
      ret = OK;
    }

  (*rsp) = APDU_Rsp_New (buffer, length);
  return ret;
}

static char
CardTerminal_EjectICC (CardTerminal * ct, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  BYTE buffer[CARDTERMINAL_EJECTICC_BUFFER_SIZE], p1, p2;
  int sn, timeout;
  unsigned length;
  bool card, change;
  char ret;

  /* Get functional unit */
  p1 = APDU_Cmd_P1 (cmd);

  /* Wrong functional unit */
  if ((p1 != CTBCS_P1_INTERFACE1) && (p1 != CTBCS_P1_INTERFACE2))
    {
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;

      (*rsp) = APDU_Rsp_New (buffer, 2);
      return OK;
    }

  /* Get the slot number */
  sn = (p1 == CTBCS_P1_INTERFACE1) ? 0 : 1;

  if (!(sn < ct->num_slots))
    {
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;

      (*rsp) = APDU_Rsp_New (buffer, 2);
      return ERR_INVALID;
    }

  /* Get command cualifier */
  p2 = APDU_Cmd_P2 (cmd);

#if 0
  /* Wrong command cualifier */
  if (p2 != 0)
    {
      buffer[0] = CTBCS_SW1_WRONG_PARAM;
      buffer[1] = CTBCS_SW2_WRONG_PARAM;

      (*rsp) = APDU_Rsp_New (buffer, 2);
      return OK;
    }
#endif

  if (CT_Slot_GetICCType (ct->slots[sn]) == CT_SLOT_NULL)
    {
      buffer[0] = CTBCS_SW1_EJECT_OK;
      buffer[1] = CTBCS_SW2_EJECT_OK;

      (*rsp) = APDU_Rsp_New (buffer, 2);
      return OK;
    }

  /* Get the card removal timeout */
  if (APDU_Cmd_Lc (cmd) == 1)
    timeout = (int) (*APDU_Cmd_Data (cmd));
  else
    timeout = 0;

  /* Check for card removal */
  ret = CT_Slot_Check (ct->slots[sn], timeout, &card, &change);

  if (ret != OK)
    {
      (*rsp) = NULL;
      return ret;
    }

  /* Release ICC (always or only when card is removed?) */
  ret = CT_Slot_Release (ct->slots[sn]);

  if (ret != OK)
    {
      (*rsp) = NULL;
      return ret;
    }

  if (timeout != 0)
    {
      if (card)
        {
          buffer[0] = CTBCS_SW1_EJECT_NOT_REMOVED;      
          buffer[1] = CTBCS_SW2_EJECT_NOT_REMOVED;
          length = 2;
        }
      else
        {
          buffer[0] = CTBCS_SW1_EJECT_REMOVED;      
          buffer[1] = CTBCS_SW2_EJECT_REMOVED;
          length = 2;
        }
    }
  else
    {
          buffer[0] = CTBCS_SW1_EJECT_OK;;      
          buffer[1] = CTBCS_SW2_EJECT_OK;;
          length = 2;
    }

  (*rsp) = APDU_Rsp_New (buffer, length);
  return ret;
}

static void
CardTerminal_Clear (CardTerminal * ct)
{
  int i;

  ct->io = NULL;
  ct->num_slots = 0;

  for (i = 0; i < CARDTERMINAL_MAX_SLOTS; i++)
    ct->slots[i] = NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1