/*
    EIBD eib bus access and management daemon
    Copyright (C) 2005-2007 Martin Koegler <mkoegler@auto.tuwien.ac.at>

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "addrtab.h"

const uchar EMI2_TLL[] = { 0xA9, 0x00, 0x12, 0x34, 0x56, 0x78, 0x0A };
const uchar EMI2_NORM[] = { 0xA9, 0x00, 0x12, 0x34, 0x56, 0x78, 0x8A };
const uchar EMI2_LCON[] = { 0x43, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uchar EMI2_LDIS[] = { 0x44, 0x00, 0x00, 0x00, 0x00, 0x00 };

const uchar EMI1_READ[] = { 0x4C, 0x01, 0x01, 0x16 };


static void
llwait (LowLevelDriverInterface * iface)
{
  if (!iface->Send_Queue_Empty ())
    {
      pth_event_t
	e = pth_event (PTH_EVENT_SEM, iface->Send_Queue_Empty_Cond ());
      pth_wait (e);
      pth_event_free (e, PTH_FREE_THIS);
    }
}

int
readEMI1Mem (LowLevelDriverInterface * iface, memaddr_t addr, uchar len,
	     CArray & result)
{
  CArray *d1, d;
  d.resize (4);
  d[0] = 0x4C;
  d[1] = len;
  d[2] = (addr >> 8) & 0xff;
  d[3] = (addr) & 0xff;
  iface->Send_Packet (d);
  llwait (iface);
  d1 = iface->Get_Packet (0);
  if (!d1)
    return 0;
  d = *d1;
  delete d1;
  if (d () != 4 + len)
    return 0;
  if (d[0] != 0x4B)
    return 0;
  if (d[1] != len)
    return 0;
  if (d[2] != ((addr >> 8) & 0xff))
    return 0;
  if (d[3] != ((addr) & 0xff))
    return 0;
  result.set (d.array () + 4, len);
  return 1;
}

int
writeEMI1Mem (LowLevelDriverInterface * iface, memaddr_t addr, CArray data)
{
  CArray d;
  iface->SendReset ();
  d.resize (4 + data ());
  d[0] = 0x46;
  d[1] = data.len () & 0xff;
  d[2] = (addr >> 8) & 0xff;
  d[3] = (addr) & 0xff;
  d.setpart (data, 4);
  iface->Send_Packet (d);
  llwait (iface);
  if (!readEMI1Mem (iface, addr, data (), d))
    return 0;
  return d == data;
}

int
readEMI2Mem (LowLevelDriverInterface * iface, memaddr_t addr, uchar len,
	     CArray & result)
{
  CArray *d1, d;
  iface->SendReset ();
  iface->Send_Packet (CArray (EMI2_TLL, sizeof (EMI2_TLL)));
  iface->Send_Packet (CArray (EMI2_LCON, sizeof (EMI2_LCON)));

  // ignore ACKs
  d1 = iface->Get_Packet (0);
  if (d1)
    {
      d = *d1;
      delete d1;
      if (d () != 6)
	return 0;
      if (d[0] != 0x86)
	return 0;
    }
  else
    return 0;

  d.resize (11);
  d[0] = 0x41;
  d[1] = 0x00;
  d[2] = 0x00;
  d[3] = 0x00;
  d[4] = 0x00;
  d[5] = 0x00;
  d[6] = 0x03;
  d[7] = 0x02;
  d[8] = len & 0x0f;
  d[9] = (addr >> 8) & 0xff;
  d[10] = (addr) & 0xff;

  iface->Send_Packet (d);

  // ignore ACKs
  d1 = iface->Get_Packet (0);
  if (d1)
    {
      d = *d1;
      delete d1;
      if (d () != 11)
	return 0;
      if (d[0] != 0x8E)
	return 0;
    }
  else
    return 0;

  d1 = iface->Get_Packet (0);
  if (!d1)
    return 0;
  d = *d1;
  delete d1;
  if (d () != 11 + len)
    return 0;
  if (d[0] != 0x89)
    return 0;
  if (d[1] != 0x00)
    return 0;
  if (d[2] != 0x00)
    return 0;
  if (d[3] != 0x00)
    return 0;
  if (d[4] != 0x00)
    return 0;
  if (d[5] != 0x00)
    return 0;
  if (d[6] != 0x03 + len)
    return 0;
  if ((d[7] & 0x03) != 0x02)
    return 0;
  if (d[8] != (0x40 | len))
    return 0;
  if (d[9] != ((addr >> 8) & 0xff))
    return 0;
  if (d[10] != ((addr) & 0xff))
    return 0;
  result.set (d.array () + 11, len);
  iface->Send_Packet (CArray (EMI2_LDIS, sizeof (EMI2_LDIS)));
  d1 = iface->Get_Packet (0);
  if (!d1)
    return 0;
  else
    {
      d = *d1;
      delete d1;
      if (d () != 6)
	return 0;
      if (d[0] != 0x88)
	return 0;
    }
  iface->Send_Packet (CArray (EMI2_NORM, sizeof (EMI2_NORM)));
  llwait (iface);
  return 1;
}

int
writeEMI2Mem (LowLevelDriverInterface * iface, memaddr_t addr, CArray data)
{
  CArray *d1, d;
  iface->SendReset ();
  iface->Send_Packet (CArray (EMI2_TLL, sizeof (EMI2_TLL)));

  iface->Send_Packet (CArray (EMI2_LCON, sizeof (EMI2_LCON)));

  // ignore ACKs
  d1 = iface->Get_Packet (0);
  if (d1)
    {
      d = *d1;
      delete d1;
      if (d () != 6)
	return 0;
      if (d[0] != 0x86)
	return 0;
    }
  else
    return 0;

  d.resize (11 + data ());
  d[0] = 0x41;
  d[1] = 0x00;
  d[2] = 0x00;
  d[3] = 0x00;
  d[4] = 0x00;
  d[5] = 0x00;
  d[6] = 0x03;
  d[7] = 0x02;
  d[8] = (0x80 | (data () & 0x0f));
  d[9] = (addr >> 8) & 0xff;
  d[10] = (addr) & 0xff;
  d.setpart (data, 11);

  iface->Send_Packet (d);
  d1 = iface->Get_Packet (0);
  if (d1)
    {
      d = *d1;
      delete d1;
      if (d () != 11 + data ())
	return 0;
      if (d[0] != 0x8E)
	return 0;
    }
  else
    return 0;

  iface->Send_Packet (CArray (EMI2_LDIS, sizeof (EMI2_LDIS)));
  d1 = iface->Get_Packet (0);
  if (!d1)
    return 0;
  else
    {
      d = *d1;
      delete d1;
      if (d () != 6)
	return 0;
      if (d[0] != 0x88)
	return 0;
    }

  iface->Send_Packet (CArray (EMI2_NORM, sizeof (EMI2_NORM)));
  llwait (iface);

  if (!readEMI2Mem (iface, addr, data (), d))
    return 0;
  return d == data;
}

int
readEMIMem (LowLevelDriverInterface * iface, memaddr_t addr, uchar len,
	    CArray & result)
{
  switch (iface->getEMIVer ())
    {
    case LowLevelDriverInterface::vEMI1:
      return readEMI1Mem (iface, addr, len, result);
    case LowLevelDriverInterface::vEMI2:
      return readEMI2Mem (iface, addr, len, result);

    default:
      return 0;
    }
}

int
writeEMIMem (LowLevelDriverInterface * iface, memaddr_t addr, CArray data)
{
  switch (iface->getEMIVer ())
    {
    case LowLevelDriverInterface::vEMI1:
      return writeEMI1Mem (iface, addr, data);
    case LowLevelDriverInterface::vEMI2:
      return writeEMI2Mem (iface, addr, data);

    default:
      return 0;
    }
}

int
readAddrTabSize (LowLevelDriverInterface * iface, uchar & result)
{
  CArray x;
  if (!readEMIMem (iface, 0x116, 1, x))
    return 0;
  result = x[0];
  return 1;
}

int
writeAddrTabSize (LowLevelDriverInterface * iface, uchar size)
{
  CArray x;
  x.resize (1);
  x[0] = size;
  return writeEMIMem (iface, 0x116, x);
}


syntax highlighted by Code2HTML, v. 0.9.1