//---------------------------------------------------------------------------
// Copyright (C) 2001 Dallas Semiconductor Corporation, All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//---------------------------------------------------------------------------
//
//     Module Name: Jib API
//
//     Description: API for executing methods on the iButton for Java and
//                  for executing the iButton Command Processor.
//
//        Filename: jib96.c
//
// Dependant Files: jib96.h
//
//         Version: 1.00 (based on jibapi.c V1.24)
//
//        Comments: - "iButton with Java" API.
//
//           Notes: - The format for all values passed to or from the 
//                    iButton's Command Processor are "Little Endian". The 
//                    format for values passed to/from an applet's process 
//                    method are determined by the applet's author.
//                  - Not all commands in this API version are supported in 
//                    older firmware revisions of the Java Button.
//

#include "jib96.h"

// globals to this module
static ushort       g_LastError       = ERR_ISO_NORMAL_00;
static CMDPACKET    g_CmdPacket;
static RETPACKET    g_RetPacket;
static ushort       g_ExecTimeMS      = 64;
static ushort       g_MinTimeMS       = 0;
static RESPONSEAPDU g_ResponseAPDU;
static JIBMASTERPIN g_MasterPIN       = {0,{0,0,0,0,0,0,0,0}};

//--------------------------------------------------------------------------
// Sets the default execution time.
//  
void SetDefaultExecTime(ushort p_ExecTimeMS)
{
  g_ExecTimeMS = p_ExecTimeMS;
}

//--------------------------------------------------------------------------
// Sends a generic command APDU to the iButton
//  @param portnum is the port number where the device is
//  @param p_lpCommandAPDU is configured by the caller
//  @param p_ExecTimeMS is the estimated runtime in 
// milliseconds. Valid runtimes range from 64 milliseconds to just under
// 4 seconds (4000 ms). Invalid runtimes will be adjusted up or down
// as necessary.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SendAPDU(int portnum, 
                        LPCOMMANDAPDU  p_lpCommandAPDU, 
                        ushort         p_ExecTimeMS)
{
  ushort l_Error;
  ushort l_RecvLen;
  ushort l_Delay = 0;

  memset((uchar*)&g_ResponseAPDU,0,sizeof(g_ResponseAPDU));

  if(p_ExecTimeMS)
  {
    l_Delay = (p_ExecTimeMS + g_MinTimeMS) / 250;   

    if(l_Delay > 0x0F)
      l_Delay = 0x0F;
  }

  if(!p_lpCommandAPDU)
  {
    SetJiBError(g_ResponseAPDU.SW);
    return &g_ResponseAPDU;
  }  
  
  g_CmdPacket.CmdByte = 137;
  g_CmdPacket.GroupID = 0;
  // add 3 bytes for the packet header
  g_CmdPacket.Len = 3 + sizeof(p_lpCommandAPDU->Header); 
                                                              
  memcpy(g_CmdPacket.CmdData,
          p_lpCommandAPDU->Header,
          sizeof(p_lpCommandAPDU->Header));
  g_CmdPacket.CmdData[g_CmdPacket.Len++ - 3] = p_lpCommandAPDU->Lc;
  
  if(p_lpCommandAPDU->Lc)
  {
    if(!p_lpCommandAPDU->Data)
    {
      SetJiBError(g_ResponseAPDU.SW);
      return &g_ResponseAPDU;
    }  

    memcpy(g_CmdPacket.CmdData + g_CmdPacket.Len - 3, // CmdData is 3 bytes into structure  
            p_lpCommandAPDU->Data,
            p_lpCommandAPDU->Lc);
    
    g_CmdPacket.Len += p_lpCommandAPDU->Lc;
  }
  
  g_CmdPacket.CmdData[g_CmdPacket.Len++ - 3] = p_lpCommandAPDU->Le;          

  l_Error = SendCiBMessage(portnum, (uchar*)&(g_CmdPacket.Len),
                           (ushort) (g_CmdPacket.Len+1),
                           (uchar)l_Delay);

  if(!l_Error)
  {
    l_RecvLen = sizeof(g_RetPacket); 

    // Read the devices response  
    l_Error = RecvCiBMessage(portnum, (uchar*)&(g_RetPacket.CSB), 
                             &l_RecvLen);
 
    if(!l_Error)    
    {
      // fill in the response APDU  
      if(l_RecvLen >= 5)
        g_ResponseAPDU.Len  = l_RecvLen - 5; // subtract two for SW, three for the header  
      else
        g_ResponseAPDU.Len  = 0;

      g_ResponseAPDU.Data = g_RetPacket.CmdData;
      g_ResponseAPDU.SW   = ((ushort)(g_RetPacket.CmdData[g_ResponseAPDU.Len]) << 8) +
                            g_RetPacket.CmdData[g_ResponseAPDU.Len+1];
    }
  }
    
  if(!l_Error)
  {
    if(!g_RetPacket.CSB)
      SetJiBError(g_ResponseAPDU.SW);
    else  
      SetJiBError(g_RetPacket.CSB);
  }
  else
  {
    g_ResponseAPDU.SW = ERR_COMM_FAILURE;
    SetJiBError(ERR_COMM_FAILURE); 
  }         

  return &g_ResponseAPDU;                          
}

//--------------------------------------------------------------------------
// Sets the Master PIN variable that is sent to the iButton with commands
// that require a PIN. This command does not set the iButton's Master PIN.
//  
void SetPIN(LPJIBMASTERPIN p_lpMasterPIN)
{
   if(p_lpMasterPIN)
     if(p_lpMasterPIN->Len <= MAX_PIN_SIZE)
       memcpy(&g_MasterPIN,p_lpMasterPIN,sizeof(JIBMASTERPIN));
}

//--------------------------------------------------------------------------
// Runs the process method on the selected applet. Valid CLA, INS, P1 and P2 
// values are chosen by the applet writer. CLA, INS, P1 and P2 values used 
// by the API and/or the iButton Command Processor are reserved.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
// 
LPRESPONSEAPDU Process(int portnum,
                       uchar   p_CLA, 
                       uchar   p_INS, 
                       uchar   p_P1, 
                       uchar   p_P2, 
                       uchar   p_Lc, 
                       uchar* p_lpData,
                       ushort   p_RunTimeMS)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = p_CLA;
  l_CommandAPDU.INS  = p_INS;
  l_CommandAPDU.P1   = p_P1;
  l_CommandAPDU.P2   = p_P2;
  l_CommandAPDU.Lc   = p_Lc;
  l_CommandAPDU.Data = p_lpData;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  return SendAPDU(portnum, &l_CommandAPDU,p_RunTimeMS);  
}

//--------------------------------------------------------------------------
// Selects an applet to be active on the iButton.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
// NOTE: The iButton will return an error if the string is not in the
//       range 5 to 16 bytes (inclusive).
//  
LPRESPONSEAPDU Select(int portnum, LPAID p_lpAID)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_SELECT;
  l_CommandAPDU.INS  = JIB_APDU_INS_SELECT;
  l_CommandAPDU.P1   = JIB_APDU_P1_SELECT;
  l_CommandAPDU.P2   = JIB_APDU_UNUSED;
  l_CommandAPDU.Lc   = p_lpAID->Len;
  l_CommandAPDU.Data = p_lpAID->Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Issues a master erase of the iButton
//  @return A ResponseAPDU object containing the response apdu sent by the 
//          iButton. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU MasterErase(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 0;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+1);
  l_CommandAPDU.Data = (uchar*)&g_MasterPIN;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//  @return A ResponseAPDU that contains the amount of free RAM. 
//          SW = 0x9000 indicates success. 
//
LPRESPONSEAPDU GetFreeRam(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 1;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//  @return A ResponseAPDU that contains the amount of free RAM. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetFreeRamPerBank(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_STATS;
  l_CommandAPDU.P2   = 1;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//  @return A ResponseAPDU containing the Firmware Version String.
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetFirmwareVersionString(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Reads the ephemeral garbage collection mode. 
//  @return The ResponseAPDU containing the value of the ephemeral garbage
//          Collector. A value of 1 indicates the garbage collector is 
//          turned on. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetEphemeralGCMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 2;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the mode of the ephemeral garbage collector.  Setting the mode to 
// 1 turns the collector on, setting it to 0 turns the collector off.   
// If the iButton is password protected, setPIN() should be called before  
// invoking this method. 
// Parameter: p_Mode 1  - turn the collector on, 
//                   0  - turn the collector off.  
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
// 
LPRESPONSEAPDU SetEphemeralGCMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 2;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Reads the applet garbage collection mode. 
//  @return The ResponseAPDU containing the mode of the applet garbage 
//          Collector. A value of 1 indicates the garbage collector is 
//          turned on. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetAppletGCMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 3;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the mode of the applet garbage collector.  Setting the mode to 1 
// turns the collector on, setting it to 0 turns the collector off.   
// If the iButton is password protected, setPIN() should be called before  
// invoking this method. 
// Parameter: p_Mode 1  - turn the collector on, 
//                     0  - turn the collector off.  
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetAppletGCMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 3;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Returns the mode of the CommandPIN.  If a value of 1 is returned by 
// the iButton, PINs are required to perform all Admistrative commands. 
//  @return The ResponseAPDU containing the mode of the CommandPIN. 
//          A value of 1 indicates a PIN is required for all Administrative 
//          commands. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetCommandPINMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 4;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the mode of the CommandPIN.  Setting the mode to 1 indicates the 
// the iButton should require a PIN for all Administrative commands. 
// Parameter: p_Mode 1  - set the CommandPIN mode to require a PIN for 
//                            all Administrative commands. 
//                   0  - set the CommandPIN mode to not require a PIN 
//                            for Administrative commands. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetCommandPINMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 4;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Returns the mode of the LoadPIN.  If a value of 1 is returned by the 
// iButton, PINs are required to load applets. 
//  @return The ResponseAPDU containing the mode of the LoadPIN. 
//          A value of 1 indicates a PIN is required for loading applets 
//          into the iButton. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetLoadPINMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 5;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the mode of the LoadPIN.  Setting the mode to 1 indicates the
// iButton should require a PIN for all applets loads. 
// Parameter: p_Mode 1 - set the LoadPIN mode to require a PIN for applet 
//                       load. 
//                   0 - set the LoadPIN mode to not require a PIN for 
//                       applet load. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetLoadPINMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 5;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//  @return The ResponseAPDU containing the restore mode.
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetRestoreMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 6;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Setting the mode to 1 enables restoration of fields to previous values 
// if a transaction was interrupted. 
// Parameter: p_Mode 1 - Allow restoration. 
//                   0 - Do not allow restoration. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetRestoreMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 6;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the Exception mode of the iButton.  If the mode is 1, then VM 
// exceptions are thrown. If the mode is 0, then VM exceptions are not 
// thrown. 
//  @return The ResponseAPDU containing the Exception mode.
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetExceptionMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 7;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the Exception mode of the iButton.  If the mode is 1, then VM 
// exceptions are thrown. If the mode is 0, then VM exceptions are not 
// thrown. 
// Parameter: p_Mode 1 - set the Exception mode to allow VM thrown 
//                       exceptions. 
//                   0 - set the Exception mode to not allow the VM to 
//                       throw exceptions. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetExceptionMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 7;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the ISOException mode of the iButton.  If the mode is 1,  
// ISOExceptions are handled differently from all other exceptions. 
// Instead of returning 0x6f00 for an uncaught ISOException, the reason is
// returned in the SW. If the mode is 0, ISOExceptions are handled like
// all other exceptions.
//  @return The ResponseAPDU containing the ISOException mode.
//          SW = 0x9000 indicates success.
// NOTE: Requires Firmware Version 1.00 or greater. 
//  
LPRESPONSEAPDU GetISOExceptionMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 8;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the ISOException mode of the iButton.  If the mode is 1,  
// ISOExceptions are handled differently from all other exceptions. 
// Instead of returning 0x6f00 for an uncaught ISOException, the reason is
// returned in the SW. If the mode is 0, ISOExceptions are handled like
// all other exceptions. 
// Parameter: p_Mode 1 - set the ISOException mode. 
//                   0 - clear the ISOException mode. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
// NOTE: Requires Firmware Version 1.00 or greater. 
//  
LPRESPONSEAPDU SetISOExceptionMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 8;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the Error Reporting mode of the iButton. If the mode is 1, 
// GetLastJCREError will return the error byte indicating the reason for 
// the last "Java Card Runtime Environment" thrown error.
//  @return The ResponseAPDU containing the Error Reporting mode.
//          SW = 0x9000 indicates success. 
// NOTE: Requires Firmware Version 1.00 or greater. 
//  
LPRESPONSEAPDU GetErrorReportMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 9;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the Error Report mode of the iButton. If the mode is 1, 
// GetLastJCREError will return the error byte indicating the reason for 
// the last "Java Card Runtime Environment" thrown error. 
// Parameter: p_Mode 1 - set the Error Report mode to. 
//                   0 - set the Error Report mode to. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
// NOTE: Requires Firmware Version 1.00 or greater. 
//  
LPRESPONSEAPDU SetErrorReportMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 9;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the size of the iButton's Commit Buffer.   
//  @return The ResponseAPDU containing the size of the iButton's commit 
//          buffer in bytes. The ResponseAPDU's data field will contain two 
//          bytes representing the size (short). 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetCommitBufferSize(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0A;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the number of fields in the iButton's Commit Buffer.   
//  @return The ResponseAPDU containing the size of the iButton's commit 
//          buffer in number of fields. The ResponseAPDU's data field will 
//          contain two bytes representing the size (short). 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetCommitBufferFields(int portnum)
{
  LPRESPONSEAPDU  l_lpResponseAPDU;
  short           l_BufferSize;  
  
  l_lpResponseAPDU = GetCommitBufferSize(portnum);
  
  if(l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00)
    return l_lpResponseAPDU;

  l_BufferSize  = ((short)l_lpResponseAPDU->Data[0]&0xff) + 
                  ((short)l_lpResponseAPDU->Data[1]<<8);
  l_BufferSize /= COMMIT_BUFFER_BYTES_PER_FIELD;
  l_lpResponseAPDU->Data[0] = (uchar)(l_BufferSize & 0xff);
  l_lpResponseAPDU->Data[1] = (uchar)(l_BufferSize>>8);
  return l_lpResponseAPDU;
}

//--------------------------------------------------------------------------
// Sets the iButtons CommitBufferSize.  The value passed in is rounded up 
// by the iButton to the nearest multiple of COMMIT_BUFFER_ucharS_PER_FIELD. 
// Parameter: p_SizeInBytes the new size of the commit buffer in bytes. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetCommitBufferSize(int portnum, short p_SizeInBytes)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 0x0A;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+3); // PIN Length + PIN Data + size  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = (uchar)(p_SizeInBytes&0xff);
  l_Data[g_MasterPIN.Len+2] = (uchar)(p_SizeInBytes>>8);
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Calculates the number of bytes that is needed to store p_NumberOfFields
// fields in the commit buffer and resizes the buffer accordingly. 
// Parameter: p_NumberOfFields - the new size of the commit buffer using 
//                               number of fields. 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetCommitBufferFields(int portnum, short p_NumberOfFields)
{
  LPRESPONSEAPDU  l_lpResponseAPDU;
  short           l_BufferSize;

  l_BufferSize = p_NumberOfFields*COMMIT_BUFFER_BYTES_PER_FIELD;

  l_lpResponseAPDU = SetCommitBufferSize(portnum, l_BufferSize);
  return l_lpResponseAPDU;  
}

//--------------------------------------------------------------------------
// Reads the Answer to Reset from the iButton, as defined in ISO7816-5 
//  @return Response APDU that contains the iButton's ATR. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetATR(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0B;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Reads the Real Time Clock of the iButton.   
//  @return The ResponseAPDU containing the value of the Real Time Clock. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetRealTimeClock(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0C;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Reads random bytes from the iButton.  The maximum number of bytes that 
// can be reads is MAX_RECEIVE - APDU overhead. 
// Parameter: p_NumBytes - the number of random bytes to read from the 
//                         iButton. 
//  @return Response APDU that contains the requested number of random 
//          bytes. 
//          SW = 0x9000 indicates success. 
//
LPRESPONSEAPDU GetRandomBytes(int portnum, short p_NumBytes)
{
  uchar            l_Data[2];
  uchar            l_RecvData[MAX_RECEIVE];
  uchar*          l_Offset    = l_RecvData;
  COMMANDAPDU     l_CommandAPDU;
  LPRESPONSEAPDU  l_lpResponseAPDU;
  short           l_NumBytes  = p_NumBytes;

  // .03 Bug workaround
  if(!p_NumBytes)
  {
    g_ResponseAPDU.Len  = 0;
    g_ResponseAPDU.SW   = ERR_ISO_NORMAL_00;
    return &g_ResponseAPDU;
  }

  if(p_NumBytes > (MAX_RECEIVE - (sizeof(RESPONSEAPDU)-sizeof(uchar*))))
  {
    g_ResponseAPDU.Len  = 0;
    g_ResponseAPDU.SW   = ERR_API_INVALID_PARAMETER;
    return &g_ResponseAPDU;
  }

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0D;
  l_CommandAPDU.Lc   = 2;
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  // can only generate 110 bytes per APDU
  while(l_NumBytes > 110)
  {
    l_Data[0] = (uchar)120;
    l_Data[1] = (uchar)0;

    l_lpResponseAPDU = SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);
    
    if(l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00)
      return l_lpResponseAPDU;
    
    memcpy(l_Offset,l_lpResponseAPDU->Data,120);
    l_Offset    += 120;
    l_NumBytes  -= 120;
  }

  l_Data[0] = (uchar)(l_NumBytes & 0xff);
  l_Data[1] = (uchar)(l_NumBytes >> 8);
  
  l_lpResponseAPDU = SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);
    
  if(l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00)
    return l_lpResponseAPDU;

  if(p_NumBytes > 120)
  {
    memcpy(l_Offset,l_lpResponseAPDU->Data,l_NumBytes);
    memcpy(l_lpResponseAPDU->Data,l_RecvData,p_NumBytes);
    l_lpResponseAPDU->Len = (ushort)p_NumBytes;
  }

  return l_lpResponseAPDU; 
}

//--------------------------------------------------------------------------
// Returns the AID of the applet with the given number.  Each applet stored 
// on the iButton is assigned a number(0 - 15).  A directory structure of 
// applets on an iButton can be built by calling this method iteratively 
// with parameter values from 0 to 15. 
// NOTE:  - AIDs are not guaranteed to be stored in contiguous locations.
//          Therefore an exaustive search through all numbers 0 - 15 is
//          required to enumerate all applets on the iButton.
//        - Version 32 and below used 2 bytes for the AID length. Later
//          versions use 1 byte.
// Parameter: p_AIDNum - the number of the applet to retrieve the AID. 
//  @return Response APDU containing the AID of the applet requested. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetAIDByNumber(int portnum, uchar p_AIDNum)
{
  COMMANDAPDU     l_CommandAPDU;
  LPRESPONSEAPDU  l_lpResponseAPDU;
  uchar           l_Buff[21];// max AID length +Java Array length + SW length  
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0E;
  l_CommandAPDU.Lc   = 1;
  l_CommandAPDU.Data = &p_AIDNum;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  l_lpResponseAPDU = SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);
  
  if(l_lpResponseAPDU)
  {
    if(l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00)
    {
      // .03 firmware bug workaround    
      if(l_lpResponseAPDU->SW != 0x8453) // AID not found SW
      {
        if(l_lpResponseAPDU->Data)
        {
          // remove Java array length
          memcpy(l_Buff,l_lpResponseAPDU->Data,0x12);
          l_lpResponseAPDU->Data[0] = l_Buff[0];
          memcpy(l_lpResponseAPDU->Data+1,l_Buff+2,0x10);
          // now set the correct length
          l_lpResponseAPDU->Len = (ushort)l_Buff[0]+1;
          // insert the happy response
          l_lpResponseAPDU->SW = ERR_ISO_NORMAL_00;
        }
        else
        {
          // data not valid
          l_lpResponseAPDU->Len = 0;
          // insert the unhappy response
          l_lpResponseAPDU->SW = ERR_COMM_FAILURE;
        }
      }
    }
    else
      l_lpResponseAPDU->Len = (ushort)l_lpResponseAPDU->Data[0]+1;
  }

  return l_lpResponseAPDU;
}

//--------------------------------------------------------------------------
// If Error Reporting Mode is enabled, the error byte indicating the reason 
// for the last JCRE thrown error is returned. Otherwise an error is 
// returned in the SW (see: GetErrorReportMode,SetErrorReportMode).
//  @return The ResponseAPDU containing the Error Reporting mode.
//          SW = 0x9000 indicates success. 
// NOTE: Requires Firmware Version 1.00 or greater. 
//  
LPRESPONSEAPDU GetLastJCREError(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x0F;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//
//  @return . 
// NOTE: Requires Firmware Version 1.10 or greater. 
//  
LPRESPONSEAPDU GetAllAIDs(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x10;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//
//  @return . 
// NOTE: Requires Firmware Version 1.10 or greater. 
//  
LPRESPONSEAPDU GetActivationTimeRemaining(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x11;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//
//  @return . 
// NOTE: Requires Firmware Version 1.10 or greater. 
//  
LPRESPONSEAPDU GetVerifyLoadWithIdMode(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_INFO;
  l_CommandAPDU.P2   = 0x12;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;
  
  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
//
//  @return . 
// NOTE: Requires Firmware Version 1.10 or greater. 
//  
LPRESPONSEAPDU SetVerifyLoadWithIdMode(int portnum, uchar p_Mode)
{
  uchar        l_Data[32];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 0x12;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2); // PIN Length + PIN Data + Mode Byte  
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_Mode;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Gets the number of Power On Resets the device has experienced since the 
// last master erase. 
//  @return Response APDU containing the number of POR since the last 
//          master erase. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU GetPORCount(int portnum)
{
  COMMANDAPDU l_CommandAPDU;
  
  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_STATS;
  l_CommandAPDU.P2   = 0;
  l_CommandAPDU.Lc   = 0;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Sets the master PIN of the iButton. A call to SetPIN with the old PIN 
// is required before this method can be called. The PIN to be set is 
// passed in. 
// Parameter: p_lpNewMasterPin the new PIN for the iButton 
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU SetMasterPIN(int portnum, LPJIBMASTERPIN p_lpNewMasterPin)
{
  uchar            l_Data[2*(MAX_PIN_SIZE+1)];
  COMMANDAPDU     l_CommandAPDU;
  LPRESPONSEAPDU  l_lpResponseAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_ADMIN;
  l_CommandAPDU.P2   = 1;
  // PIN Length + PIN Data + PIN Length + PIN Data  
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+p_lpNewMasterPin->Len+2); 
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  // copy current PIN  
  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  // copy new PIN  
  memcpy(l_Data+g_MasterPIN.Len+1,p_lpNewMasterPin,p_lpNewMasterPin->Len+1);

  l_lpResponseAPDU = SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);

  // go ahead and set the local PIN if the command succeeded  
  if(l_lpResponseAPDU->SW == (ushort)0x9000)
    SetPIN(p_lpNewMasterPin);

  return l_lpResponseAPDU;
}

//--------------------------------------------------------------------------
// Deletes the currently selected Applet.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU DeleteSelectedApplet(int portnum)
{
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_AID;
  l_CommandAPDU.P2   = 0;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+1); 
  l_CommandAPDU.Data = (uchar*)&g_MasterPIN;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Deletes Applet number p_AppletNumber.
// Parameter: p_AppletNumber - number of the applet to be deleted.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU DeleteAppletByNumber(int portnum, uchar p_AppletNumber)
{
  uchar        l_Data[MAX_PIN_SIZE+2];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_AID;
  l_CommandAPDU.P2   = 1;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+2);
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  l_Data[g_MasterPIN.Len+1] = p_AppletNumber;

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Deletes the Applet with AID p_lpAID.
// Parameter: p_lpAID - AID of the applet to be deleted.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU DeleteAppletByAID(int portnum, LPAID p_lpAID)
{
  uchar        l_Data[MAX_PIN_SIZE+MAX_AID_SIZE+2];
  COMMANDAPDU l_CommandAPDU;

  l_CommandAPDU.CLA  = JIB_APDU_CLA_COMMAND_PROC;
  l_CommandAPDU.INS  = JIB_APDU_INS_COMMAND_PROC;
  l_CommandAPDU.P1   = JIB_APDU_P1_COMMAND_AID;
  l_CommandAPDU.P2   = 2;
  l_CommandAPDU.Lc   = (uchar)(g_MasterPIN.Len+p_lpAID->Len+2); 
  l_CommandAPDU.Data = l_Data;
  l_CommandAPDU.Le   = JIB_APDU_LE_DEFAULT;

  memcpy(l_Data,(uchar*)&g_MasterPIN,g_MasterPIN.Len+1);
  memcpy(l_Data+g_MasterPIN.Len+1,p_lpAID,p_lpAID->Len+1);

  return SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);  
}

//--------------------------------------------------------------------------
// Loads the applet stored in p_lpAppletBuffer.
// Parameter: p_lpAppletBuffer  - Buffer containing the applet (ASSUMPTION:
//                                Buffer was previously loaded with the 
//                                contents of a .JiB "dot JiB" file).
//            p_AppletLen       - Length of the applet in the buffer.
//            p_lpAID           - Pointer the the applet's AID.
//  @return The ResponseAPDU containing the Status Word of the response. 
//          SW = 0x9000 indicates success. 
//  
LPRESPONSEAPDU LoadApplet(int portnum, 
                          uchar* p_lpAppletBuffer, 
                          uint  p_AppletLen,
                          LPAID  p_lpAID)
{
  uchar *l_lpData, *l_lpOffset;
  COMMANDAPDU     l_CommandAPDU;
  LPRESPONSEAPDU  l_lpResponseAPDU;
  uint           l_BufferSize  = p_AppletLen+sizeof(AID)+sizeof(JIBMASTERPIN);
  uint           l_PacketSize;

  // make sure AID length fits
  if(p_lpAID->Len > MAX_AID_SIZE)
  {
    g_ResponseAPDU.Len  = 0;
    g_ResponseAPDU.SW   = ERR_API_INVALID_PARAMETER;
    return &g_ResponseAPDU;
  }
  
  if(!(l_lpOffset = (l_lpData = malloc(l_BufferSize))))
  {
    g_ResponseAPDU.Len  = 0;
    g_ResponseAPDU.SW   = ERR_API_MEMORY_ALLOCATION;
    return &g_ResponseAPDU;
  }

  memset(l_lpData,0,l_BufferSize);

  // copy the master pin first
  memcpy(l_lpOffset,(uchar*)&g_MasterPIN,sizeof(g_MasterPIN));
  l_lpOffset += sizeof(g_MasterPIN);
  l_PacketSize  = sizeof(g_MasterPIN); 

  // copy the AID
  memcpy(l_lpOffset,(uchar*)p_lpAID,p_lpAID->Len+1);
  memset(l_lpOffset+p_lpAID->Len+1,0,sizeof(AID)-(p_lpAID->Len+1));
  l_lpOffset    += sizeof(AID);
  l_PacketSize  += sizeof(AID);

  // copy the applet
  memcpy(l_lpOffset,p_lpAppletBuffer,p_AppletLen);
  l_PacketSize += p_AppletLen;
  l_lpOffset  = l_lpData;
   
  l_CommandAPDU.CLA   = JIB_APDU_CLA_LOAD_APPLET;
  l_CommandAPDU.INS   = JIB_APDU_INS_LOAD_APPLET;
  l_CommandAPDU.P1    = JIB_APDU_P1_LOAD_APPLET;
  l_CommandAPDU.P2    = JIB_APDU_UNUSED;
  l_CommandAPDU.Lc    = (uchar)((l_PacketSize > MAX_APDU_SIZE) ? MAX_APDU_SIZE:
                                                                l_PacketSize);
  l_CommandAPDU.Data  = l_lpOffset;
  l_CommandAPDU.Le    = JIB_APDU_LE_DEFAULT;

  l_lpResponseAPDU    = SendAPDU(portnum, &l_CommandAPDU, 
                                 (ushort)((l_CommandAPDU.Lc == MAX_APDU_SIZE)? 64: g_ExecTimeMS));

  if((l_lpResponseAPDU->SW == ERR_ISO_NORMAL_00)||
     (l_lpResponseAPDU->SW != ERR_NEXT_LOAD_PACKET_EXPECTED))
  {
    free(l_lpData);
    return l_lpResponseAPDU;
  }

  l_lpOffset += MAX_APDU_SIZE;
  l_PacketSize -= MAX_APDU_SIZE;
  l_CommandAPDU.P1  = JIB_APDU_P1_CONTINUE_LOAD;

  while(l_PacketSize > MAX_APDU_SIZE)
  {
    l_CommandAPDU.Data  = l_lpOffset;
    l_lpResponseAPDU    = SendAPDU(portnum, &l_CommandAPDU,
                                   (ushort)((l_lpResponseAPDU->SW == ERR_NEXT_LOAD_PACKET_EXPECTED)? 64: g_ExecTimeMS));
    
    l_lpOffset += MAX_APDU_SIZE;
    l_PacketSize -= MAX_APDU_SIZE;

    if((l_lpResponseAPDU->SW == ERR_ISO_NORMAL_00)||
       (l_lpResponseAPDU->SW != ERR_NEXT_LOAD_PACKET_EXPECTED))
      break;
  }

  if(l_PacketSize)
  {
    l_CommandAPDU.Lc    = (uchar)l_PacketSize; 
    l_CommandAPDU.Data  = l_lpOffset;
    l_lpResponseAPDU    = SendAPDU(portnum, &l_CommandAPDU,g_ExecTimeMS);
  }

  free(l_lpData);
  return l_lpResponseAPDU;  
}

//--------------------------------------------------------------------------
//
//    Set new error code.
//
void SetJiBError(ushort p_Error)
{
  g_LastError = g_ResponseAPDU.SW = p_Error;
}

//--------------------------------------------------------------------------
//
//    Get error code.
//
ushort GetJiBError()
{
  return g_LastError;
}
