/*
    ozedit.c - A user-space program to read/write to the oz263
               chip in the ASUS Digimatrix.
    Copyright (c) 2006 Andrew Calkin <calkina@geexbox.org>

    Based heavily on code by
    Copyright (c) 2005 Richard Taylor <richard@artaylor.co.uk>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <time.h>
#include "digitools.h"
#include "oz263.h"

static int axtoi(char *hexstg)
{
  int n = 0;         /* position in string */
  int m = 0;         /* position in digit[] to shift */
  int count;         /* loop index */
  int intvalue = 0;  /* integer value of hex string */
  int digit[5];      /* hold values to convert */
  while (n < 4)
  {
    if (hexstg[n]=='\0')
      break;
    if (hexstg[n] > 0x29 && hexstg[n] < 0x40 ) /*if 0 to 9 */
      digit[n] = hexstg[n] & 0x0f;            /* convert to int */
    else if (hexstg[n] >='a' && hexstg[n] <= 'f') /* if a to f */
      digit[n] = (hexstg[n] & 0x0f) + 9;      /* convert to int */
    else if (hexstg[n] >='A' && hexstg[n] <= 'F') /* if A to F */
      digit[n] = (hexstg[n] & 0x0f) + 9;      /* convert to int */
    else
      break;
    n++;
  }
  count = n;
  m = n - 1;
  n = 0;
  while(n < count)
  {
    /* digit[n] is value of hex digit at position n
       (m << 2) is the number of positions to shift
       OR the bits into return value */
    intvalue = intvalue | (digit[n] << (m << 2));
    m--;   /* adjust the position to set */
    n++;   /* next digit to process */
  }
  return intvalue;
}

static void ozedit_help(char *argv0)
{
#ifndef SILENT
  printf(
"\n"
"Usage: %s [OPTIONS]\n"
"\n"
"  WARNING!: This program affects the oz263 chip, which is\n"
"  yet to have any specifications released. Modifications made with\n"
"  this program may leave your chip in an unrecoverable state, or\n"
"  otherwise cause some hardware failure. Use at your own risk!\n\n"
"\n"
"  This program needs to be run as root.\n"
"\n"
"  -m, --manual           Allows you to interactively change a register.\n"
"  -r, --read             Allows you to interactively read a register.\n"
"\n"
"  -v, --version          Print version number.\n"
"  -h, --help             Display this help message.\n"
"\n", argv0);
#endif
}

static struct option ozedit_longopts[] = {
    { "manual",      0, 0, 'm' },
    { "read",        0, 0, 'r' },
    { "version",     0, 0, 'v' },
    { "help",        0, 0, 'h' },
    { NULL,          0, 0,  0  }
};


int ozedit_main(int argc, char *argv[])
{
  extern int file;
  int ch, longindex = 0;
  char buffer[20], *ptr;

  if ( (int)getuid() != 0)
  {
#ifndef SILENT
    fprintf(stderr,"Must be root to run this program!\n");
#endif
    return -1;
  }

  if (argc == 1)
  {
#ifndef SILENT
    printf("\nPlease supply some parameters.\n"
             "Use %s -h for list.\n\n", argv[0]);
#endif
    return 1;
  }

  /* Parse input params */
  for (;;)
  {
    if ((ch = getopt_long(argc, argv, "mrvh",
                ozedit_longopts, &longindex)) == -1)
      break;

    switch (ch)
    {
      case 'v':
#ifndef SILENT
        printf("ASUS DigiMatrix ozedit oz263 R&D tool, from "
               "DigiTools Version %s\n", DIGI_VER);
#endif
        break;

      case 'h':
        ozedit_help(argv[0]);
        return 0;
        break;

      case 'r':
        if (!file)
          file=oz263_I2C_init();
        printf("Entering interactive read mode. Press enter to exit\n");
        printf("Please enter \"REG\"\n");
        printf("Where REG is a hexadecimal value (without 0x).\n");
        while (buffer[0] != '\n')
        {
          /* Get at most 20 bytes from user */
          if (fgets(buffer, 20, stdin))
          {
            if (axtoi(buffer) > 0x45 )
              printf("Write forbidden - the I2C bus will lock up!\n");
            else
            {
              oz263_write_byte(file, axtoi(buffer));
              printf("Read back: 0x%X = 0x%X\n",
                  axtoi(buffer), oz263_read_byte(file));
            }
          }
        }
        break;

      case 'm':
        if (!file)
          file=oz263_I2C_init();
        printf("Entering interactive write mode. Press enter to exit\n");
        printf("Please enter \"REG VALUE\"\n");
        printf("Where REG and VALUE are hexadecimal values "
                  "(without 0x).\n");
        while (buffer[0] != '\n')
        {
          /* Get at most 20 bytes from user */
          if (fgets(buffer, 20, stdin))
          {
            if (axtoi(buffer) > 0x45)
              printf("Write forbidden - the I2C bus will lock up!\n");
            else
            {
              /* Find the start of the data value */
              ptr = strstr(buffer, " ");
              if (ptr)
              {
                printf("0x%X = 0x%X ", axtoi(buffer), axtoi(ptr));
                oz263_write_byte_data(file, axtoi(buffer), axtoi(ptr));
                printf("Read back: 0x%0X\n", oz263_read_byte(file));
              }
            }
          }
        }
        break;

      default:
        ozedit_help(argv[0]);
        break;

    }
  }
  return 0;
}
