//---------------------------------------------------------------------------
// 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.
//--------------------------------------------------------------------------
//
//  jibtest.c - Test each Java-powered iButton found on the 1-Wire
//
//  version 3.00B
//

#include "jib96.h"
#include "ownet.h"

#define MAXDEVICES 10

// local functions
static void TestJiB(int portnum, uchar *dev);
extern SMALLINT FindDevices(int, uchar FamilySN[][8], SMALLINT, int);

// global serial numbers
uchar FamilySN[MAXDEVICES][8];

//--------------------------------------------------------------------------
// Main for the Jib test
//
int main(int argc, char *argv[]) {
  int portnum = 0, i;
  SMALLINT NumDevices;

  printf("\nJiBTest, Java Powered iButton HW/SW Tester, Version 3.00B\n");

  // check for required port name
  if (argc != 2) {
    printf("1-Wire Net name required on command line!\n"
           " (example: \"COM1\" (Win32 DS2480),\"/dev/cua0\" "
           "(Linux DS2480),\"1\" (Win32 TMEX)\n");
    exit(1);
  }

  // attempt to acquire the 1-Wire Net
  if ((portnum = owAcquireEx(argv[1])) < 0) {
    OWERROR_DUMP(stdout);
    exit(1);
  }

  // success
  printf("Port opened: %s\n", argv[1]);

  // Find the device(s)
  NumDevices = FindDevices(portnum, &FamilySN[0], 0x16, MAXDEVICES);
  if (NumDevices > 0) {
    printf("\n");
    printf("JiB(s) Found: \n");
    for (i = 0; i < NumDevices; i++) {
      PrintSerialNum(FamilySN[i]);
      printf("\n");
    }
    printf("\n\n");

    // loop through the devices and do a test on each
    for (i = 0; i < NumDevices; i++)
      TestJiB(portnum, FamilySN[i]);
  }

  // release the 1-Wire Net
  owRelease(portnum);
  printf("Closing port %s.\n", argv[1]);
  exit(0);

  return 0;
}

//--------------------------------------------------------------------------
// Run the Java-powered Button through most of the read functions.
//
void TestJiB(int portnum, uchar *dev) {
  char l_Buff[256];
  int l_FailureCount = 0, l_TestCount = 0, l_AppletNum, l_CurrentOffset = 0, l_CurrentLength = 0;
  uint l_FreeRam = 0;
  LPRESPONSEAPDU l_lpResponseAPDU;

  // select the device and put in overdrive
  printf("                  Device: ");
  PrintSerialNum(dev);
  printf("\n");

  owSerialNum(portnum, dev, 0);
  if (!owOverdriveAccess(portnum)) {
    printf("ERROR, could not get device in overdrive!\n");
    l_FailureCount++;
  }

  l_TestCount++;

  // Get Firmware Version String
  l_lpResponseAPDU = GetFirmwareVersionString(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Firmware Version", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    // trim out the length byte
    memcpy(l_Buff, l_lpResponseAPDU->Data, l_lpResponseAPDU->Len);
    memcpy(l_lpResponseAPDU->Data, l_Buff + 1, --(l_lpResponseAPDU->Len));
    // zero terminate the strings
    l_lpResponseAPDU->Data[l_lpResponseAPDU->Len] = 0;
    printf(" Firmware Version String: %s\n", l_lpResponseAPDU->Data);
  }

  // Get Real-Time Clock
  l_lpResponseAPDU = GetRealTimeClock(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Real Time Clock", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("Real Time Clock (1 of 2): 0x%08X\n", ((uint)l_lpResponseAPDU->Data[3] << 24) +
                                                     ((uint)l_lpResponseAPDU->Data[2] << 16) +
                                                     ((uint)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0]);
  }

  // Get Free RAM
  l_lpResponseAPDU = GetFreeRam(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Free RAM", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    l_FreeRam = ((ushort)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0];

    if (l_lpResponseAPDU->Len > 2)
      l_FreeRam += ((uint)l_lpResponseAPDU->Data[2] << 16);

    printf("                Free RAM: %d bytes\n", l_FreeRam);
  }

  // Get Power On Reset Count
  l_lpResponseAPDU = GetPORCount(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "POR Count", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("         Power On Resets: %hd\n", ((ushort)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0]);
  }

  // Get Ephemeral GC Mode
  l_lpResponseAPDU = GetEphemeralGCMode(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Ephemeral GC Mode", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("            Ephemeral GC: %s\n", l_lpResponseAPDU->Data[0] ? "On" : "Off");
  }

  // Get Applet GC Mode
  l_lpResponseAPDU = GetAppletGCMode(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Applet GC Mode", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("               Applet GC: %s\n", l_lpResponseAPDU->Data[0] ? "On" : "Off");
  }

  // Get Restore Mode
  l_lpResponseAPDU = GetRestoreMode(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Restore Mode", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("            Restore Mode: %s\n", l_lpResponseAPDU->Data[0] ? "Enabled" : "Disabled");
  }

  // Get Exception Mode
  l_lpResponseAPDU = GetExceptionMode(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Exception Mode", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("          Exception Mode: %s\n", l_lpResponseAPDU->Data[0] ? "Enabled" : "Disabled");
  }

  // Get Commit Buffer Size
  l_lpResponseAPDU = GetCommitBufferSize(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Commit Buffer Size", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("      Commit Buffer Size: %hd bytes", ((ushort)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0]);
  }

  // Get Commit Buffer Fields
  l_lpResponseAPDU = GetCommitBufferFields(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("\n%s Test Failed with SW = 0x%04hX\n", "Commit Buffer Fields", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf(" -> %hd fields\n", ((ushort)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0]);
  }

  // Get Real-Time Clock
  l_lpResponseAPDU = GetRealTimeClock(portnum);

  l_TestCount++;

  if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
    printf("%s Test Failed with SW = 0x%04hX\n", "Real Time Clock", l_lpResponseAPDU->SW);
    l_FailureCount++;
  } else {
    printf("Real Time Clock (2 of 2): 0x%08X\n", ((uint)l_lpResponseAPDU->Data[3] << 24) +
                                                     ((uint)l_lpResponseAPDU->Data[2] << 16) +
                                                     ((uint)l_lpResponseAPDU->Data[1] << 8) + l_lpResponseAPDU->Data[0]);
  }

  printf("       Installed Applets:\n");

  if (!(dev[0] & (uchar)0x80)) { // quick test for crusty firmware
    // Enumerate all applets
    for (l_AppletNum = 0; l_AppletNum < 16; l_AppletNum++) {
      l_lpResponseAPDU = GetAIDByNumber(portnum, (uchar)l_AppletNum);

      l_TestCount++;

      if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
        // Not an error if no applet is installed at this position
        if (l_lpResponseAPDU->SW != 0x8453) {
          printf("%s Test Failed with SW = 0x%04hX\n", "Get AID", l_lpResponseAPDU->SW);
          l_FailureCount++;
        }
      } else {
        if (l_lpResponseAPDU->Len) {
          // trim out the length byte
          memcpy(l_Buff, l_lpResponseAPDU->Data + 1, (int)l_lpResponseAPDU->Data[0]);

          // zero terminate the string
          l_Buff[*(uchar *)l_lpResponseAPDU->Data] = 0;

          printf("%24c%2d) %s\n", ' ', l_AppletNum, l_Buff);
        }
      }
    }
  } else { // can get all AIDS in single APDU
    l_TestCount++;
    l_lpResponseAPDU = GetAllAIDs(portnum);

    if (l_lpResponseAPDU->SW != ERR_ISO_NORMAL_00) {
      if (l_lpResponseAPDU->SW != 0x8453) {
        printf("%s Test Failed with SW = 0x%04hX\n", "Get All AIDs", l_lpResponseAPDU->SW);
        l_FailureCount++;
      } else
        printf("%24c No Applets found\n", ' ');
    } else {
      while ((unsigned)l_CurrentOffset < l_lpResponseAPDU->Len) {
        l_AppletNum = l_lpResponseAPDU->Data[l_CurrentOffset++];
        l_CurrentLength = l_lpResponseAPDU->Data[l_CurrentOffset++];
        memcpy(l_Buff, l_lpResponseAPDU->Data + l_CurrentOffset, l_CurrentLength);

        // zero terminate the string
        l_Buff[l_CurrentLength] = 0;

        l_CurrentOffset += l_CurrentLength;

        printf("%24c%2d) %s\n", ' ', l_AppletNum, l_Buff);
      }
    }
  }

  if (l_FailureCount)
    printf("\n%d of %d tests failed! Check Hardware and Driver (if applicable) Installation\n\n", l_FailureCount, l_TestCount);
  else
    printf("\nHardware Installation Ok!\n\n");
}
