/*
    protocol_t1.c
    Handling of ISO 7816 T=1 protocol
    
    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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "protocol_t1.h"
#include "t1_block.h"

/*
 * Not exported constants definition
 */
#define PROTOCOL_T1_DEFAULT_IFSC        32
#define PROTOCOL_T1_DEFAULT_IFSD        32
#define PROTOCOL_T1_MAX_IFSC            251  /* Cannot send > 255 buffer */
#define PROTOCOL_T1_DEFAULT_CWI         13
#define PROTOCOL_T1_DEFAULT_BWI         4
#define PROTOCOL_T1_EDC_LRC             0
#define PROTOCOL_T1_EDC_CRC             1

/*
 * Not exported functions declaration
 */

static void 
Protocol_T1_Clear (Protocol_T1 * t1);

static int
Protocol_T1_SendBlock (Protocol_T1 * t1, T1_Block * block);

static int
Protocol_T1_ReceiveBlock (Protocol_T1 * t1, T1_Block ** block);

static int
Protocol_T1_UpdateBWT (Protocol_T1 * t1, unsigned short bwt);

/*
 * Exproted funtions definition
 */

Protocol_T1 *
Protocol_T1_New (void)
{
  Protocol_T1 *t1;

  t1 = (Protocol_T1 *) malloc (sizeof (Protocol_T1));

  if (t1 != NULL)
    Protocol_T1_Clear (t1);

  return t1;
}

int
Protocol_T1_Init (Protocol_T1 * t1, ICC_Async * icc, PPS_ProtocolParameters * params)
{
  ICC_Async_Timings timings;
  BYTE ta, tb, tc, cwi, bwi;
  unsigned long baudrate;
  double work_etu;
  ATR *atr;
  int i;

  /* Set ICC */
  t1->icc = icc;

  /* Get ATR of the card */
  atr = ICC_Async_GetAtr (t1->icc);

  /* Set IFSC */
  if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TA, &ta) == ATR_NOT_FOUND)
    t1->ifsc = PROTOCOL_T1_DEFAULT_IFSC;
  else if ((ta != 0x00) && (ta != 0xFF))
    t1->ifsc = ta;
  else
    t1->ifsc = PROTOCOL_T1_DEFAULT_IFSC;

  /* Towitoko does not allow IFSC > 251 */
  t1->ifsc = MIN (t1->ifsc, PROTOCOL_T1_MAX_IFSC);

  /* Set IFSD */
  t1->ifsd = PROTOCOL_T1_DEFAULT_IFSD;

#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS
  /* Calculate CWI and BWI */
  if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TB, &tb) == ATR_NOT_FOUND)
    {
#endif
      cwi  = PROTOCOL_T1_DEFAULT_CWI;
      bwi = PROTOCOL_T1_DEFAULT_BWI;
#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS
    }
  else
    {
      cwi  = tb & 0x0F;
      bwi = (tb & 0xF0) >> 4;
    }
#endif
  
  /* Work etu  = (1000 / baudrate) milliseconds */
  ICC_Async_GetBaudrate (t1->icc, &baudrate);
  work_etu = 1000 / (double)baudrate;

  /* Set CWT = (2^CWI + 11) work etu */
  t1->cwt = 1;

  for (i = 0; i < cwi ; i++)
    t1->cwt *= 2;

  t1->cwt = (unsigned short) ((t1->cwt + 11) * work_etu);

  /* Set BWT = (2^BWI * 960 + 11) work etu */
  t1->bwt = 1;
  for (i = 0; i < bwi; i++)
    t1->bwt *= 2;

  t1->bwt = (unsigned short) ((t1->bwt * 960 + 11) * work_etu);

  /* Set BGT = 22 * work etu */
  t1->bgt = (unsigned short) (22 * work_etu);

  /* Set the error detection code type */
  if (ATR_GetInterfaceByte (atr, 3, ATR_INTERFACE_BYTE_TC, &tc) == ATR_NOT_FOUND)
    t1->edc = PROTOCOL_T1_EDC_LRC;
  else
    t1->edc = tc & 0x01;

  /* Set initial send sequence (NS) */
  t1->ns = 1;
  
  /* Set timings */
  ICC_Async_GetTimings (t1->icc, &timings);

  timings.block_timeout = t1->bwt;
  timings.char_timeout = t1->cwt;
  timings.block_delay = t1->bgt;

  ICC_Async_SetTimings (t1->icc, &timings);

#ifdef DEBUG_PROTOCOL
  printf ("Protocol: T=1: IFSC=%d, IFSD=%d, CWT=%d, BWT=%d, BGT=%d, EDC=%s\n",
          t1->ifsc, t1->ifsd, t1->cwt, t1->bwt, t1->bgt,
          (t1->edc == PROTOCOL_T1_EDC_LRC) ? "LRC" : "CRC");
#endif

  return PROTOCOL_T1_OK;
}

int
Protocol_T1_Command (Protocol_T1 * t1, APDU_Cmd * cmd, APDU_Rsp ** rsp)
{
  T1_Block *block;
  BYTE *buffer, rsp_type, bytes, nr, wtx;
  unsigned short counter;
  int ret;
  bool more;

  /* Calculate the number of bytes to send */
  counter = 0;
  bytes = MIN (APDU_Cmd_RawLen (cmd), t1->ifsc);

  /* See if chaining is needed */
  more = (APDU_Cmd_RawLen (cmd) > t1->ifsc);

  /* Increment ns */
  t1->ns = (t1->ns + 1) %2;

  /* Create an I-Block */
  block = T1_Block_NewIBlock (bytes, APDU_Cmd_Raw (cmd), t1->ns, more);

#ifdef DEBUG_PROTOCOL
  printf ("Sending block I(%d,%d)\n", t1->ns, more);
#endif

  /* Send a block */
  ret = Protocol_T1_SendBlock (t1, block);

  /* Delete I-block */
  T1_Block_Delete (block);

  while ((ret == PROTOCOL_T1_OK) && more)
    {
      /* Receive a block */
      ret = Protocol_T1_ReceiveBlock (t1, &block);

      if (ret == PROTOCOL_T1_OK)
        {
          rsp_type = T1_Block_GetType (block);

          /* Positive ACK R-Block received */
          if (rsp_type == T1_BLOCK_R_OK)
            {
#ifdef DEBUG_PROTOCOL
              printf ("Protocol: Received block R(%d)\n", T1_Block_GetNR (block));
#endif                   
              /* Delete block */
              T1_Block_Delete (block);
 
              /* Increment ns  */
              t1->ns = (t1->ns + 1) % 2;

              /* Calculate the number of bytes to send */
              counter += bytes;
              bytes = MIN (APDU_Cmd_RawLen (cmd) - counter, t1->ifsc);

              /* See if chaining is needed */
              more = (APDU_Cmd_RawLen (cmd) - counter > t1->ifsc);

              /* Create an I-Block */
              block =
                T1_Block_NewIBlock (bytes, APDU_Cmd_Raw (cmd) + counter,
                                    t1->ns, more);
#ifdef DEBUG_PROTOCOL
              printf ("Protocol: Sending block I(%d,%d)\n", t1->ns, more);
#endif
              /* Send a block */
              ret = Protocol_T1_SendBlock (t1, block);

              /* Delete I-block */
              T1_Block_Delete (block);
            }
                                   
          else
            {
              /* Delete block */
              T1_Block_Delete (block);

              ret = PROTOCOL_T1_NOT_IMPLEMENTED;
            }
        }

      else
        {
          ret = PROTOCOL_T1_NOT_IMPLEMENTED;
        }
    }

  /* Reset counter */
  buffer = NULL;
  counter = 0;      
  more = TRUE;
  wtx = 0;
      
  while ((ret == PROTOCOL_T1_OK) && more)
    {
      if (wtx > 1)
        Protocol_T1_UpdateBWT (t1, wtx * (t1->bwt));          

      /* Receive a block */
      ret = Protocol_T1_ReceiveBlock (t1, &block);

      if (wtx > 1)
        {
          Protocol_T1_UpdateBWT (t1, t1->bwt);          
          wtx = 0;
        }

      if (ret == PROTOCOL_T1_OK)
        {
          rsp_type = T1_Block_GetType (block);

          if (rsp_type == T1_BLOCK_I)
            {
#ifdef DEBUG_PROTOCOL
              printf ("Protocol: Received block I(%d,%d)\n", 
              T1_Block_GetNS(block), T1_Block_GetMore (block));
#endif
              /* Calculate nr */
              nr = (T1_Block_GetNS (block) + 1) % 2;
                               
              /* Save inf field */
              bytes = T1_Block_GetLen (block);
	      buffer = (BYTE *) realloc(buffer, counter + bytes);
              memcpy (buffer + counter, T1_Block_GetInf (block), bytes);
              counter += bytes;

              /* See if chaining is requested */
              more = T1_Block_GetMore (block);

              /* Delete block */
              T1_Block_Delete (block);

              if (more)
                {
                  /* Create an R-Block */
                  block = T1_Block_NewRBlock (T1_BLOCK_R_OK, nr);
#ifdef DEBUG_PROTOCOL
                  printf ("Protocol: Sending block R(%d)\n", nr);
#endif                    
                  /* Send R-Block */
                  ret = Protocol_T1_SendBlock (t1, block);

                  /* Delete I-block */
                  T1_Block_Delete (block);
                }
            }

          /* WTX Request S-Block received */ 
          else if (rsp_type == T1_BLOCK_S_WTX_REQ)
            {
              /* Get wtx multiplier */
              wtx = (*T1_Block_GetInf (block));
#ifdef DEBUG_PROTOCOL
              printf ("Protocol: Received block S(WTX request, %d)\n", wtx);
#endif                                  
              /* Delete block */
              T1_Block_Delete (block);
             
              /* Create an WTX response S-Block */
              block = T1_Block_NewSBlock (T1_BLOCK_S_WTX_RES, 1, &wtx);
#ifdef DEBUG_PROTOCOL
              printf ("Protocol: Sending block S(WTX response, %d)\n", wtx);
#endif                    
              /* Send WTX response */
              ret = Protocol_T1_SendBlock (t1, block);
                  
              /* Delete block */
              T1_Block_Delete (block);
            }

          else
            {
              ret = PROTOCOL_T1_NOT_IMPLEMENTED;
            }
        }
    }

  if (ret == PROTOCOL_T1_OK)
    (*rsp) = APDU_Rsp_New (buffer, counter);

  if (buffer != NULL)
    free (buffer);

  return ret;
}

int
Protocol_T1_Close (Protocol_T1 * t1)
{
  Protocol_T1_Clear (t1);

  return PROTOCOL_T1_OK;
}

void
Protocol_T1_Delete (Protocol_T1 * t1)
{
  free (t1);
}

/*
 * Not exported functions definition
 */

static int
Protocol_T1_SendBlock (Protocol_T1 * t1, T1_Block * block)
{
  BYTE *buffer;
  int length, ret;

  /* Setup transmission */
  if (ICC_Async_BeginTransmission (t1->icc) != ICC_ASYNC_OK)
    ret = PROTOCOL_T1_ICC_ERROR;

  else
    {
      /* Send T=1 block */
      buffer = T1_Block_Raw (block);
      length = T1_Block_RawLen (block);

      if (ICC_Async_Transmit (t1->icc, length, buffer) != ICC_ASYNC_OK)
        {
          ICC_Async_EndTransmission (t1->icc);
          ret = PROTOCOL_T1_ICC_ERROR;
        }

      else
        ret = PROTOCOL_T1_OK;
    }

  return ret;
}

static int
Protocol_T1_ReceiveBlock (Protocol_T1 * t1, T1_Block ** block)
{
  BYTE buffer[T1_BLOCK_MAX_SIZE];
  int ret;

  /* Receive four mandatory bytes */
  if (ICC_Async_Receive (t1->icc, 4, buffer) != ICC_ASYNC_OK)
    {
      ret = PROTOCOL_T1_ICC_ERROR;
      (*block) = NULL;
    }

  else
    {
      if (buffer[2] != 0x00)
        {
          /* Set timings to read the remaining block */
          Protocol_T1_UpdateBWT (t1, t1->cwt);

          /* Receive remaining bytes */
          if (ICC_Async_Receive (t1->icc, buffer[2], buffer + 4) !=
              ICC_ASYNC_OK)
            {
              (*block) = NULL;
              ret = PROTOCOL_T1_ICC_ERROR;
            }

          else
            {
              (*block) = T1_Block_New (buffer, buffer[2] + 4);
              ret = PROTOCOL_T1_OK;
            }

          /* Restore timings */
          Protocol_T1_UpdateBWT (t1, t1->bwt);
        }
      else
        {
          ret = PROTOCOL_T1_OK;
          (*block) = T1_Block_New (buffer, 4);
        }
    }

  if (ICC_Async_Switch (t1->icc) != ICC_ASYNC_OK)
    ret = PROTOCOL_T1_ICC_ERROR;

  /* End of transmission */
  if (ICC_Async_EndTransmission (t1->icc) != ICC_ASYNC_OK)
    ret = PROTOCOL_T1_ICC_ERROR;

  return ret;
}

static void
Protocol_T1_Clear (Protocol_T1 * t1)
{
  t1->icc = NULL;
  t1->ifsc = 0;
  t1->ifsd = 0;
  t1->bgt = 0;
  t1->bwt = 0;
  t1->cwt = 0;
  t1->edc = 0;
  t1->ns = 0;
}

static int
Protocol_T1_UpdateBWT (Protocol_T1 * t1, unsigned short bwt)
{
  ICC_Async_Timings timings;
  
  ICC_Async_GetTimings (t1->icc, &timings);

  timings.block_timeout = bwt;

  ICC_Async_SetTimings (t1->icc, &timings);

  return PROTOCOL_T1_OK;
}


syntax highlighted by Code2HTML, v. 0.9.1