/*
    ctapi.c
    CT-API interface implementation for multiple card-terminals

    This file is part of the Unix driver for Towitoko smartcard readers
    Copyright (C) 1998 1999 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 "ctapi.h"
#include "ctbcs.h"
#include "ct_list.h"
#include "cardterminal.h"
#include "ct_slot.h"
#include <stdio.h>
#include <string.h>
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif

/* 
 * Not exported variables definition
 */

/* Linked list of card-terminals */
static CT_List *ct_list = NULL;

#ifdef HAVE_PTHREAD_H
static pthread_mutex_t ct_list_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif

/*
 * Exported functions definition
 */

char
CT_init (unsigned short ctn, unsigned short pn)
{
  CardTerminal *ct;
  char ret;
  bool ct_list_empty;

#ifdef HAVE_PTHREAD_H
  pthread_mutex_lock (&ct_list_mutex);
#endif

  /* Check that ctn is not in use */
  if (CT_List_GetCardTerminal (ct_list, ctn) == NULL)
    {
      /* Create a new CardTerminal */
      ct = CardTerminal_New ();

      if (ct != NULL)
        {   
          /* Initialize CardTerminal */
          ret = CardTerminal_Init (ct, pn);

          /* Add CardTerminal to list */
          if (ret == OK)
            {  
              /* See if list is initialised */
              ct_list_empty = (ct_list == NULL);
              
              if (ct_list_empty)
                ct_list = CT_List_New ();

              /* Add the CardTerminal to the list */
              if (!CT_List_AddCardTerminal (ct_list, ct, ctn))
                {
                  CardTerminal_Close (ct);
                  CardTerminal_Delete (ct);
                  
                  if (ct_list_empty)
                    {
                      CT_List_Delete (ct_list);
                      ct_list = NULL;
                    }
                  
                  ret = ERR_MEMORY;
                }
            }
          
          else
            CardTerminal_Delete(ct);
        }
      else
        ret = ERR_MEMORY;
    }
  else
    ret = ERR_CT;

#ifdef HAVE_PTHREAD_H
  pthread_mutex_unlock (&ct_list_mutex);
#endif  

#ifdef DEBUG_CTAPI
  printf ("CTAPI: CT_init(ctn=%u, pn=%u)=%d\n", ctn, pn, ret);
#endif

  return ret;
}

char
CT_close (unsigned short ctn)
{
  CardTerminal *ct;
  char ret;

#ifdef HAVE_PTHREAD_H
  pthread_mutex_lock (&ct_list_mutex);
#endif

  /* Get the card-terminal */
  ct = CT_List_GetCardTerminal (ct_list, ctn);

  if (ct != NULL)
    {    
      /* Close CardTerminal */
      ret = CardTerminal_Close(ct);
  
      /* Remove card-terminal from list */
      CT_List_RemoveCardTerminal (ct_list, ctn);

      /* Delete the list if there are no more card-terminals */
      if (CT_List_GetNumberOfElements (ct_list) == 0)
        {
          CT_List_Delete (ct_list);
          ct_list = NULL;
        }
    }

  else
    ret = ERR_CT;

#ifdef HAVE_PTHREAD_H
  pthread_mutex_unlock (&ct_list_mutex);
#endif

#ifdef DEBUG_CTAPI
  printf ("CTAPI: CT_close(ctn=%d)=%u\n", ctn, ret);
#endif

  return ret;
}

char
CT_data (unsigned short ctn, unsigned char *dad, unsigned char *sad,
	 unsigned short lc, unsigned char *cmd, unsigned short *lr,
	 unsigned char *rsp)
{
  CardTerminal *ct;
  CT_Slot *slot;
  APDU_Cmd *apdu_cmd;
  APDU_Rsp *apdu_rsp = NULL;
  int remain;
  unsigned char aux;
  char ret;

#ifdef DEBUG_CTAPI
  int i;

  printf ("CTAPI: CT_data(ctn=%u, *dad=0x%02X, *sad=0x%02X, lc=%u, *cmd={", 
	  ctn, *dad, *sad, lc);

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

  printf ("}, *lr=%u, rsp=[])\n", *lr); 
#endif

#ifdef HAVE_PTHREAD_H
  pthread_mutex_lock (&ct_list_mutex);
#endif

  /* Get card-terminal */
  ct = CT_List_GetCardTerminal (ct_list, ctn);

#ifdef HAVE_PTHREAD_H
  pthread_mutex_unlock (&ct_list_mutex);
#endif

  if (ct != NULL)
    {
      /* Create a command APDU */
      apdu_cmd = APDU_Cmd_New (cmd, lc);

      if (apdu_cmd != NULL)
        {

#ifdef HAVE_PTHREAD_H
          pthread_mutex_lock (CardTerminal_GetMutex(ct));
#endif

          /* Command goes to the reader */
          if ((*dad) == 1)
            {
              /* CT-BCS command */
              ret = CardTerminal_Command (ct, apdu_cmd, &apdu_rsp);

              (*sad) = 1;
              (*dad) = (*sad);
            }

          /* Command goes to an ICC */
          else 
            {
              /* Get the slot */
              slot = CardTerminal_GetSlot(ct, ((*dad)==0)? 0: (*dad)-1);

              if (slot != NULL)
                {
                  /* ICC command */
                  ret = CT_Slot_Command (slot, apdu_cmd, &apdu_rsp);
        
                  if (CT_Slot_GetICCType (slot) != CT_SLOT_NULL)
                    {
                      aux = (*sad);
                      (*sad) = (*dad);
                      (*dad) = aux;
	            }
                  else
	            {
	              (*dad) = (*sad);
	              (*sad) = 1;
	            }
                }
      
              else
                {
                  /* Invalid DAD address */
                  (*dad) = (*sad);
                  (*sad) = 1;
                  apdu_rsp = NULL;

                  ret = ERR_INVALID;
                }
            }

#ifdef HAVE_PTHREAD_H
          pthread_mutex_unlock (CardTerminal_GetMutex(ct));
#endif

          if (apdu_rsp != NULL)
            {
              /* Copy APDU data to rsp */
              remain = MAX ((short)APDU_Rsp_RawLen(apdu_rsp) - (*lr),0);

              if (remain > 0)
	        ret = ERR_MEMORY;

              (*lr) = MIN ((*lr), (short)APDU_Rsp_RawLen (apdu_rsp));
         
              memcpy (rsp, APDU_Rsp_Raw (apdu_rsp) + remain, (*lr));

              /* Delete response APDU */
              APDU_Rsp_Delete (apdu_rsp);
            }

          else 
            (*lr) = 0;

          /* Delete command APDU */
          APDU_Cmd_Delete (apdu_cmd);
	}
      else
        ret = ERR_MEMORY;
    }     
  else
    ret = ERR_CT;
  
#ifdef DEBUG_CTAPI
  printf ("CTAPI: CT_data(ctn=%u, *dad=0x%02X, *sad=0x%02X, lc=%u, *cmd={}, *lr=%u, rsp={", 
	  ctn, *dad, *sad, lc, *lr);

  for (i=0; i<*lr; i++)
    printf ("%02X ", rsp[i]);

  printf ("})=%d\n", ret); 
#endif

  return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1