/*
 A swiss-army tool. Uses vendor extensions, both internal to FX2 and
 implemented in the firmware, to control several important target
 startup functions:
 
 Firmware loading
 I2C reading/writing
 USB error counter readback
 
 Copyright (c) 2008 Dimtel, Inc., All Rights Reserved

 $Id: usbio_utils.c,v 1.2 2013/09/05 03:40:06 dim Exp $
 $Date: 2013/09/05 03:40:06 $
 $Author: dim $
 $Revision: 1.2 $
 $Log: usbio_utils.c,v $
 Revision 1.2  2013/09/05 03:40:06  dim
 Got rid of program_invocation_name due to portability issues

 Revision 1.1  2012/11/11 05:25:19  dim
 Moved from iGp12/src/usbio, updated to handle LLRF4 and iGp12

 Revision 1.2  2012/08/29 06:45:59  dim
 Added usbio_write_eeprom(), write_offsets command

 Revision 1.1.1.1  2010/10/05 16:42:44  dim
 Initial import of iGp12

 Revision 1.2  2010/03/12 19:04:59  dim
 Updates for multiple boards, bug fixes

 Revision 1.1.1.1  2009/06/02 20:26:02  dim
 Initial import

 Revision 1.1  2009/06/02 19:56:51  dim
 Full support for service functions (fw load, 8051 control, EEPROM) added


*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "usbio.h"
#include "../include/eeprom.h"
#include "../include/vendor_commands.h"

extern char *exnm;

enum cmds
{
  CMD_I2C_READ,
  CMD_I2C_WRITE,
  CMD_IF_RESET,
  CMD_USBERR_READ,
  CMD_FW_LOAD,
  CMD_RAM_READ,
  CMD_WRITE_SERIAL,
  CMD_WRITE_OFFSETS,
  CMD_8051RESET
};

void usage(void)
{
  fprintf(stderr, "Usage: %s cmd args\n", program_invocation_name);
  fprintf(stderr, "\tThe following commands are available\n");
  fprintf(stderr, "\t\ti2c_read addr length\n");
  fprintf(stderr, "\t\ti2c_write addr data\n");
  fprintf(stderr, "\t\tif_reset\n");
  fprintf(stderr, "\t\tusberr_read\n");
  fprintf(stderr, "\t\t8051reset value\n");
  fprintf(stderr, "\t\tfw_load filename\n");
  fprintf(stderr, "\t\tram_read addr length\n");
  fprintf(stderr, "\t\twrite_serial serial_number\n");
  fprintf(stderr, "\t\twrite_offsets Val1 Val2 Val3 ...\n");
  exit(1);
}

#define ROW_CNT 16
void print_hex(FILE *file, char *buf, unsigned int len)
{
  int i, k;

  for (i = 0; i < len; i += ROW_CNT)
  {
    fprintf(file, "%04x", i);
    for (k = 0; k < (MIN(len-i, ROW_CNT)); k++)
      fprintf(file, " %02x", (unsigned char)buf[i+k]);
    fprintf(file, "\n");
  }
}

int main(int argc, char **argv)
{
  char buf[EP0_MAX_PKT_SIZE], *fw_file;
  unsigned char *ram;
  unsigned short bcdDev, *bcd = NULL;
  unsigned int addr = 0, len = -1;
  int value = 0, rc, cmd = -1, k;
  short *off;
  usbio_context *ctxt;
  
  exnm = basename(argv[0]);
  
  if (argc < 2)
    usage();

  /* Do argument parsing and command-line error handling */
  if (strcmp(argv[1], "i2c_write") == 0)
  {
    if (argc != 4)
      usage();
    else
    {
      addr = strtoul(argv[2], NULL, 0);
      if (addr > 127)
      {
        fprintf(stderr, "%s: I2C address must be in [0..127] range\n",
                exnm);
        return 1;
      }
      len = string2bytes(buf, argv[3], EP0_MAX_PKT_SIZE);
      if (len == 0)
        return 1;
      cmd = CMD_I2C_WRITE;
    }
  }
  else if (strcmp(argv[1], "i2c_read") == 0)
  {
    if (argc != 4)
      usage();
    else
    {
      addr = strtoul(argv[2], NULL, 0);
      if (addr > 127)
      {
        fprintf(stderr, "%s: I2C address must be in [0..127] range, got %d\n",
                exnm, addr);
        return 1;
      }
      len = strtoul(argv[3], NULL, 0);
      if (len < 1 || len > EP0_MAX_PKT_SIZE)
      {
        fprintf(stderr, "%s: read length must be [1..%d], got %d\n",
                exnm, EP0_MAX_PKT_SIZE, len);
        return 1;
      }
      cmd = CMD_I2C_READ;
    }
  }
  else if (strcmp(argv[1], "if_reset") == 0)
  {
    if (argc != 2)
      usage();
    else
      cmd = CMD_IF_RESET;
  }
  else if (strcmp(argv[1], "usberr_read") == 0)
  {
    if (argc != 2)
      usage();
    else
      cmd = CMD_USBERR_READ;
  }
  else if (strcmp(argv[1], "write_serial") == 0)
  {
    if (argc != 3)
      usage();
    else
      cmd = CMD_WRITE_SERIAL;
  }
  else if (strcmp(argv[1], "write_offsets") == 0)
  {
    if (argc < 3)
      usage();
    else
      cmd = CMD_WRITE_OFFSETS;
  }
  else if (strcmp(argv[1], "8051reset") == 0)
  {
    if (argc == 3)
    {
      cmd   = CMD_8051RESET;
      value = strtol(argv[2], NULL, 0);
    }
    else
      usage();
  }
  else if (strcmp(argv[1], "ram_read") == 0)
  {
    if (argc != 4)
      usage();
    else
    {
      addr = strtoul(argv[2], NULL, 0);
      if (addr > 0xffff)
      {
        fprintf(stderr, "%s: RAM address must be in [0..%d] range, got %d\n",
                exnm, 0xffff, addr);
        return 1;
      }
      len = strtoul(argv[3], NULL, 0);
      if (len < 1 || addr + len > 0xffff)
      {
        fprintf(stderr, "%s: read length invalid\n",
                exnm);
        return 1;
      }
      cmd = CMD_RAM_READ;
    }
  }
  else if (strcmp(argv[1], "fw_load") == 0)
  {
    if (argc < 3)
      usage();
    fw_file = argv[2];
    cmd = CMD_FW_LOAD;
    if (argc == 4)
    {
      bcdDev = (unsigned short)strtol(argv[3], NULL, 0);
      bcd    = &bcdDev;
    }
  }
      
  
  ctxt = usbio_open_full(1, getenv("BOARD_SERNO"), bcd);
  if (ctxt == NULL)
  {
    fprintf(stderr, "%s: usbio_open() failed\n", exnm);
    return 1;
  }

  switch (cmd)
  {
    case CMD_I2C_WRITE:
      usbio_i2c_write(ctxt, addr, buf, len);
      break;
    case CMD_I2C_READ:
      rc = usbio_i2c_read(ctxt, addr, buf, len);
      if (rc != len)
        fprintf(stderr, "%s: usbio_i2c_read() failed, return value %d\n",
                exnm, rc);
      else
        print_hex(stdout, buf, len);
      break;
    case CMD_IF_RESET:
      usbio_if_reset(ctxt);
      break;
    case CMD_USBERR_READ:
      rc = usbio_usberr_read(ctxt);
      printf("USB error counter: %d\n", rc);
      break;
    case CMD_WRITE_SERIAL:
      rc = usbio_write_sernum(ctxt, argv[2]);
      break;
    case CMD_WRITE_OFFSETS:
      off = (short *)buf;
      for (k = 2; (k < argc) & (k < 10); k++)
        *off++ = (short)strtol(argv[k], NULL, 0);
      rc = usbio_write_eeprom(ctxt, EEPROM_OFFSETS, buf, (k-2)*sizeof(short));
      break;
    case CMD_8051RESET:
      rc = usbio_cpu_reset(ctxt, value);
      break;
    case CMD_RAM_READ:
      rc = usbio_readram(ctxt, addr, len, &ram);
      if (rc != len)
        fprintf(stderr, "%s: usbio_readram() failed (%d/%d)", 
                exnm, rc, len);
      else
        print_hex(stdout, (char *)ram, len);
      if (ram != NULL)
        free(ram);
      break;
    case CMD_FW_LOAD:
      rc = usbio_fw_load(ctxt, fw_file);
      if (rc > 0)
        fprintf(stdout, "%s: Firmware loaded, %d bytes written\n",
                exnm, rc);
      break;
    default:
      usage();
  }
  
  usbio_close(ctxt, 1);

  return 0;
}

