#include "archinc.h"
#include "cli.h"
#include "clichar.h"
#include "i2cmaster.h"
#include "i2cport.h"

#include <stdio.h>
#include <stdlib.h>

#if (defined(CONFIG_INCLUDE_LPC210X_I2C) && defined(CONFIG_INCLUDE_I2C_PORT))

static unsigned char slaveAddr[CONFIG_I2CPORT_MAX_PORTS] = {0x40, 0x48};

static unsigned short outputShadow[CONFIG_I2CPORT_MAX_PORTS];

static unsigned char busWriteData[4];

static int i2cportHandler(int argc, char * argv[], int flags);

static int writeConfig(int portId, unsigned char offset, unsigned short writeData)
{
  busWriteData[0] = offset;
  busWriteData[1] = (unsigned char)(writeData & 0xff);
  busWriteData[2] = (unsigned char)((writeData >> 8) & 0xff);

  return i2cmasterSendMult(slaveAddr[portId], busWriteData, 3, true); 
}

static int readConfig(int portId, unsigned char offset, unsigned short * pReadData)
{
  int retStat;
  unsigned char dummyAddr;
  unsigned char tmpByte;

  if (pReadData == NULL)
    return -1;

  // first write the offset, no stop bit
  retStat = i2cmasterSendOne(slaveAddr[portId], offset, false);

  // receive 2 bytes
  if (retStat != -1) {
    retStat = i2cmasterReceive(slaveAddr[portId], 2, true);
    }

  // read the bytes
  if (retStat != -1) {
    retStat = i2cmasterGet(&dummyAddr, &tmpByte);
    }

  *pReadData = (unsigned short)(tmpByte & 0x00ff);

  if (retStat != -1) {
    retStat = i2cmasterGet(&dummyAddr, &tmpByte);
    }

  *pReadData |= ((tmpByte << 8) & 0xff00);

  return retStat;
}

int i2cportInit(void)
{
  int retVal = 0;
  int i;

  cliRegisterCommand("i2cport", i2cportHandler, "i2cport [in] [out <16-bit hex value>]");

  for(i=0;i<CONFIG_I2CPORT_MAX_PORTS;i++)
    outputShadow[i] = (unsigned short)0;

  for(i=0;((retVal == 0) && (i<CONFIG_I2CPORT_MAX_PORTS));i++)
    retVal = i2cmasterProbe(slaveAddr[i]);

  return retVal;
}

int i2cportDirConfig(int portId, unsigned short iobitMask)
{
  return writeConfig(portId, 6, iobitMask);
}

int i2cportAssign(int portId, unsigned short iobitMask)
{
  outputShadow[portId] = iobitMask;
  return writeConfig(portId, 2, iobitMask);
}

int i2cportSet(int portId, unsigned short iobitMask)
{
  unsigned short tmpReg = outputShadow[portId];
  tmpReg |= iobitMask;
  outputShadow[portId] = tmpReg;
  return writeConfig(portId, 2, tmpReg);
}

int i2cportClear(int portId, unsigned short iobitMask)
{
  unsigned short tmpReg = outputShadow[portId];
  tmpReg &= ~iobitMask;
  outputShadow[portId] = tmpReg;
  return writeConfig(portId, 2, tmpReg);
}

int i2cportGet(int portId, unsigned short * pIobitMask)
{
  return readConfig(portId, 0, pIobitMask);
}

static int i2cportHandler(int argc, char * argv[], int flags)
{
  int retVal = -1;
  int bufLen;
  int portNum;
  unsigned short portData;
  char * pOutBuf;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc < 2)
    return -1;

  // TODO: put this in a while loop to process a list of op/data pairs

  // write gets two parameters [index] [data], read gets one
  switch(*argv[1]) {
    case 'i':
      // input
      if (i2cportGet(0, &portData) == 0) {
        sprintf(pOutBuf, "i2cport: port 0 [%04X]\n", portData);
        retVal = 0;
        }
      else {
        sprintf(pOutBuf, "i2cport: error reading port 0 addr[%02X]\n", slaveAddr[0]);
        }
      clicharSendOutputBuffer(pOutBuf, bufLen);
      break;
    case 'o':
      // output [mask]
      portData = (unsigned short)strtol(argv[2], NULL, 16);
      
      if (i2cportAssign(1, portData) == 0) {
        sprintf(pOutBuf, "i2cport: port 1 <-- [%04X]\n", portData);
        retVal = 0;
        }
      else {
        sprintf(pOutBuf, "i2cport: error writing port 1 addr[%02X]\n", slaveAddr[1]);
        }
      clicharSendOutputBuffer(pOutBuf, bufLen);
      break;
    default:
      break;
    }
    
  return retVal;
}

#endif /* (defined(CONFIG_INCLUDE_LPC210X_I2C) && defined(CONFIG_INCLUDE_I2C_PORT)) */

