/*
    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 "managementclient.h"
#include "management.h"
#include "loadimage.h"

void
ReadIndividualAddresses (Layer3 * l3, Trace * t, ClientConnection * c,
			 pth_event_t stop)
{
  try
  {
    Layer7_Broadcast b (l3, t);
    CArray erg;
    Array < eibaddr_t > e = b.A_IndividualAddress_Read ();
    erg.resize (2 + 2 * e ());
    EIBSETTYPE (erg, EIB_M_INDIVIDUAL_ADDRESS_READ);
    for (unsigned i = 0; i < e (); i++)
      {
	erg[2 + i * 2] = (e[i] >> 8) & 0xff;
	erg[2 + i * 2 + 1] = (e[i]) & 0xff;
      }
    c->sendmessage (erg (), erg.array (), stop);
  }
  catch (Exception e)
  {
    c->sendreject (stop, EIB_PROCESSING_ERROR);
  }
}

void
ChangeProgMode (Layer3 * l3, Trace * t, ClientConnection * c,
		pth_event_t stop)
{
  try
  {
    eibaddr_t dest;
    uchar res[3];
    int i;
    EIBSETTYPE (res, EIB_PROG_MODE);
    res[2] = 0;
    if (c->size < 5)
      {
	c->sendreject (stop);
	return;
      }
    dest = (c->buf[2] << 8) | (c->buf[3]);
    Management_Connection m (l3, t, dest);
    switch (c->buf[4])
      {
      case 0:
	if (m.X_Progmode_Off () == -1)
	  c->sendreject (stop);
	else
	  c->sendmessage (3, res, stop);
	break;
      case 1:
	if (m.X_Progmode_On () == -1)
	  c->sendreject (stop);
	else
	  c->sendmessage (3, res, stop);
	break;
      case 2:
	if (m.X_Progmode_Toggle () == -1)
	  c->sendreject (stop);
	else
	  c->sendmessage (3, res, stop);
	break;
      case 3:
	if ((i = m.X_Progmode_Status ()) == -1)
	  c->sendreject (stop);
	else
	  {
	    res[2] = i;
	    c->sendmessage (3, res, stop);
	  }
	break;
      default:
	c->sendreject (stop);
      }
  }
  catch (Exception e)
  {
    c->sendreject (stop, EIB_PROCESSING_ERROR);
  }
}

void
GetMaskVersion (Layer3 * l3, Trace * t, ClientConnection * c,
		pth_event_t stop)
{
  try
  {
    eibaddr_t dest;
    uchar res[4];
    uint16_t maskver;
    EIBSETTYPE (res, EIB_MASK_VERSION);
    res[2] = 0;
    if (c->size < 4)
      {
	c->sendreject (stop);
	return;
      }

    dest = (c->buf[2] << 8) | (c->buf[3]);
    Management_Connection m (l3, t, dest);
    if (m.A_Device_Descriptor_Read (maskver) == -1)
      c->sendreject (stop);
    else
      {
	res[2] = (maskver >> 8) & 0xff;
	res[3] = (maskver) & 0xff;
	c->sendmessage (4, res, stop);
      }
  }
  catch (Exception e)
  {
    c->sendreject (stop, EIB_PROCESSING_ERROR);
  }
}

void
WriteIndividualAddress (Layer3 * l3, Trace * t, ClientConnection * c,
			pth_event_t stop)
{
  try
  {
    eibaddr_t dest;
    uint16_t maskver;
    if (c->size < 4)
      {
	c->sendreject (stop);
	return;
      }

    dest = (c->buf[2] << 8) | (c->buf[3]);
    Layer7_Broadcast b (l3, t);
    {
      Management_Connection m (l3, t, dest);
      if (m.A_Device_Descriptor_Read (maskver) != -1)
	{
	  c->sendreject (stop, EIB_ERROR_ADDR_EXISTS);
	  return;
	}
    }
    Array < eibaddr_t > addr = b.A_IndividualAddress_Read ();
    if (addr () > 1)
      {
	c->sendreject (stop, EIB_ERROR_MORE_DEVICE);
	return;
      }
    if (addr () == 0)
      {
	c->sendreject (stop, EIB_ERROR_TIMEOUT);
	return;
      }
    b.A_IndividualAddress_Write (dest);
    // wait 100ms
    pth_usleep (100000);

    Management_Connection m1 (l3, t, dest);
    if (m1.A_Device_Descriptor_Read (maskver) == -1)
      {
	c->sendreject (stop, EIB_PROCESSING_ERROR);
	return;
      }
    if (m1.X_Progmode_Off () == -1)
      {
	c->sendreject (stop, EIB_PROCESSING_ERROR);
	return;
      }
    c->sendreject (stop, EIB_M_INDIVIDUAL_ADDRESS_WRITE);
  }
  catch (Exception e)
  {
    c->sendreject (stop, EIB_PROCESSING_ERROR);
  }
}
void
ManagementConnection (Layer3 * l3, Trace * t, ClientConnection * c,
		      pth_event_t stop)
{
  try
  {
    eibaddr_t dest;
    uint16_t maskver;
    int16_t val;
    uchar buf[10];
    int i;
    eibkey_type key;

    if (c->size < 4)
      {
	c->sendreject (stop);
	return;
      }

    dest = (c->buf[2] << 8) | (c->buf[3]);
    Management_Connection m (l3, t, dest);
    if (m.A_Device_Descriptor_Read (maskver) == -1)
      {
	c->sendreject (stop);
	return;
      }
    c->sendreject (stop, EIB_MC_CONNECTION);
    do
      {
	i = c->readmessage (stop);
	if (i != -1)
	  switch (EIBTYPE (c->buf))
	    {
	    case EIB_MC_PROG_MODE:
	      if (c->size < 3)
		{
		  c->sendreject (stop);
		  break;
		}
	      EIBSETTYPE (buf, EIB_MC_PROG_MODE);
	      buf[2] = 0;
	      switch (c->buf[2])
		{
		case 0:
		  if (m.X_Progmode_Off () == -1)
		    c->sendreject (stop);
		  else
		    c->sendmessage (3, buf, stop);
		  break;
		case 1:
		  if (m.X_Progmode_On () == -1)
		    c->sendreject (stop);
		  else
		    c->sendmessage (3, buf, stop);
		  break;
		case 2:
		  if (m.X_Progmode_Toggle () == -1)
		    c->sendreject (stop);
		  else
		    c->sendmessage (3, buf, stop);
		  break;
		case 3:
		  if ((i = m.X_Progmode_Status ()) == -1)
		    c->sendreject (stop);
		  else
		    {
		      buf[2] = i;
		      c->sendmessage (3, buf, stop);
		    }
		  break;
		default:
		  c->sendreject (stop);
		}
	      break;
	    case EIB_MC_MASK_VERSION:
	      if (m.A_Device_Descriptor_Read (maskver) == -1)
		c->sendreject (stop);
	      else
		{
		  EIBSETTYPE (buf, EIB_MC_MASK_VERSION);
		  buf[2] = (maskver >> 8) & 0xff;
		  buf[3] = (maskver) & 0xff;
		  c->sendmessage (4, buf, stop);
		}
	      break;
	    case EIB_MC_PEI_TYPE:
	      if (m.X_Get_PEIType (val) == -1)
		c->sendreject (stop);
	      else
		{
		  EIBSETTYPE (buf, EIB_MC_PEI_TYPE);
		  buf[2] = (val >> 8) & 0xff;
		  buf[3] = (val) & 0xff;
		  c->sendmessage (4, buf, stop);
		}
	      break;
	    case EIB_MC_ADC_READ:
	      if (c->size < 4)
		{
		  c->sendreject (stop);
		  break;
		}
	      if (m.A_ADC_Read (c->buf[2], c->buf[3], val) == -1)
		c->sendreject (stop);
	      else
		{
		  EIBSETTYPE (buf, EIB_MC_ADC_READ);
		  buf[2] = (val >> 8) & 0xff;
		  buf[3] = (val) & 0xff;
		  c->sendmessage (4, buf, stop);
		}
	      break;

	    case EIB_MC_READ:
	      if (c->size < 6)
		{
		  c->sendreject (stop);
		  break;
		}
	      {
		memaddr_t addr = (c->buf[2] << 8) | (c->buf[3]);
		unsigned len = (c->buf[4] << 8) | (c->buf[5]);
		CArray data, erg;
		if (m.X_Memory_Read_Block (addr, len, data) == -1)
		  c->sendreject (stop);
		else
		  {
		    erg.resize (6);
		    EIBSETTYPE (erg, EIB_MC_READ);
		    erg.setpart (data, 2);
		    c->sendmessage (erg (), erg.array (), stop);
		  }
	      }
	      break;

	    case EIB_MC_WRITE:
	      if (c->size < 6)
		{
		  c->sendreject (stop);
		  break;
		}
	      {
		memaddr_t addr = (c->buf[2] << 8) | (c->buf[3]);
		unsigned len = (c->buf[4] << 8) | (c->buf[5]);
		if (c->size < len + 6)
		  {
		    c->sendreject (stop);
		    break;
		  }
		i = m.X_Memory_Write_Block (addr, CArray (c->buf + 6, len));
		if (i == -2)
		  c->sendreject (stop, EIB_ERROR_VERIFY);
		else if (i != 0)
		  c->sendreject (stop, EIB_PROCESSING_ERROR);
		else
		  c->sendreject (stop, EIB_MC_WRITE);
	      }
	      break;

	    case EIB_MC_PROP_READ:
	      if (c->size < 7)
		{
		  c->sendreject (stop);
		  break;
		}
	      {
		CArray data, erg;
		if (m.
		    A_Property_Read (c->buf[2], c->buf[3],
				     (c->buf[4] << 8) | c->buf[5], c->buf[6],
				     data) == -1)
		  c->sendreject (stop);
		else
		  {
		    erg.resize (2);
		    EIBSETTYPE (erg, EIB_MC_PROP_READ);
		    erg.setpart (data, 2);
		    c->sendmessage (erg (), erg.array (), stop);
		  }
	      }
	      break;

	    case EIB_MC_PROP_WRITE:
	      if (c->size < 7)
		{
		  c->sendreject (stop);
		  break;
		}
	      {
		CArray data, erg;
		if (m.
		    A_Property_Write (c->buf[2], c->buf[3],
				      (c->buf[4] << 8) | c->buf[5], c->buf[6],
				      CArray (c->buf + 7, c->size - 7),
				      erg) == -1)
		  c->sendreject (stop);
		else
		  {
		    erg.resize (2);
		    EIBSETTYPE (erg, EIB_MC_PROP_WRITE);
		    erg.setpart (data, 2);
		    c->sendmessage (erg (), erg.array (), stop);
		  }
	      }
	      break;

	    case EIB_MC_AUTHORIZE:
	      if (c->size < 6)
		{
		  c->sendreject (stop);
		  break;
		}
	      EIBSETTYPE (buf, EIB_MC_AUTHORIZE);
	      key =
		(c->buf[2] << 24) | (c->buf[3] << 16) | (c->
							 buf[4] << 8) | (c->
									 buf
									 [5]);
	      if (m.A_Authorize (key, buf[2]) == -1)
		c->sendreject (stop);
	      else
		c->sendmessage (3, buf, stop);
	      break;

	    case EIB_MC_KEY_WRITE:
	      if (c->size < 7)
		{
		  c->sendreject (stop);
		  break;
		}
	      key =
		(c->buf[2] << 24) | (c->buf[3] << 16) | (c->
							 buf[4] << 8) | (c->
									 buf
									 [5]);
	      if (m.A_KeyWrite (key, *(c->buf + 6)) == -1)
		c->sendreject (stop);
	      else
		c->sendreject (stop, EIB_MC_KEY_WRITE);
	      break;

	    case EIB_MC_PROP_DESC:
	      if (c->size < 4)
		{
		  c->sendreject (stop);
		  break;
		}
	      if (m.
		  A_Property_Desc (c->buf[2], c->buf[3], 0, buf[2], maskver,
				   buf[5]) == -1)
		c->sendreject (stop);
	      else
		{
		  EIBSETTYPE (buf, EIB_MC_PROP_DESC);
		  buf[3] = (maskver >> 8) & 0xff;
		  buf[4] = (maskver) & 0xff;
		  c->sendmessage (6, buf, stop);
		}
	      break;

	    case EIB_MC_PROP_SCAN:
	      {
		Array < PropertyInfo > p;
		if (m.X_PropertyScan (p) == -1)
		  c->sendreject (stop);
		else
		  {
		    CArray erg;
		    erg.resize (2 + p () * 6);
		    EIBSETTYPE (erg, EIB_MC_PROP_SCAN);
		    for (unsigned i = 0; i < p (); i++)
		      {
			erg[i * 6 + 2] = p[i].obj;
			erg[i * 6 + 3] = p[i].property;
			erg[i * 6 + 4] = p[i].type;
			erg[i * 6 + 5] = (p[i].count >> 8) & 0xff;
			erg[i * 6 + 6] = (p[i].count) & 0xff;
			erg[i * 6 + 7] = p[i].access;
		      }
		    c->sendmessage (erg (), erg.array (), stop);
		  }
	      }
	      break;

	    case EIB_RESET_CONNECTION:
	      i = -1;
	      break;

	    case EIB_MC_RESTART:
	      m.A_Restart ();
	      c->sendreject (stop, EIB_MC_RESTART);
	      break;

	    case EIB_MC_WRITE_NOVERIFY:
	      if (c->size < 6)
		{
		  c->sendreject (stop);
		  break;
		}
	      {
		memaddr_t addr = (c->buf[2] << 8) | (c->buf[3]);
		unsigned len = (c->buf[4] << 8) | (c->buf[5]);
		if (c->size < len + 6)
		  {
		    c->sendreject (stop);
		    break;
		  }
		i = m.A_Memory_Write_Block (addr, CArray (c->buf + 6, len));
		if (i != 0)
		  c->sendreject (stop, EIB_PROCESSING_ERROR);
		else
		  c->sendreject (stop, EIB_MC_WRITE_NOVERIFY);
	      }
	      break;

	    default:
	      c->sendreject (stop);
	    }
      }
    while (i != -1);
  }
  catch (Exception e)
  {
    c->sendreject (stop);
  }
}

void
LoadImage (Layer3 * l3, Trace * t, ClientConnection * c, pth_event_t stop)
{
  uchar buf[200];
  CArray img (c->buf + 2, c->size - 2);
  CArray erg;
  int j;
  BCUImage *i;
  BCU_LOAD_RESULT r = PrepareLoadImage (img, i);
  if (r != IMG_IMAGE_LOADABLE)
    {
      if (i)
	delete i;
      EIBSETTYPE (buf, EIB_LOAD_IMAGE);
      buf[2] = (r >> 8) & 0xff;
      buf[3] = (r) & 0xff;
      c->sendmessage (4, buf, stop);
      return;
    }

  try
  {
    uint16_t maskver;
    uchar c;
    r = IMG_NO_DEVICE_CONNECTION;
    Management_Connection m (l3, t, i->addr);
    r = IMG_MASK_READ_FAILED;
    if (m.A_Device_Descriptor_Read (maskver) == -1)
      goto out;
    r = IMG_WRONG_MASK_VERSION;
    if (i->BCUType == BCUImage::B_bcu1)
      {
	if (maskver != 0x0012)
	  goto out;

	/* set error flags in BCU (0x10D = 0x00) */
	r = IMG_CLEAR_ERROR;
	c = 0;
	if (m.X_Memory_Write_Block (0x010d, CArray (&c, 1)) != 0)
	  goto out;

	/*set length of the address tab to 1 */
	r = IMG_RESET_ADDR_TAB;
	c = 0x01;
	if (m.X_Memory_Write_Block (0x0116, CArray (&c, 1)) != 0)
	  goto out;

	/*load the data from 0x100 to 0x100 */
	r = IMG_LOAD_HEADER;
	c = 0xff;
	if (m.X_Memory_Write_Block (0x0100, CArray (&c, 1)) != 0)
	  goto out;

	/*load the data from 0x103 to 0x10C */
	if (m.
	    X_Memory_Write_Block (0x0103,
				  CArray (i->code.array () + 0x03, 10)) != 0)
	  goto out;

	/*load the data from 0x10E to 0x115 */
	if (m.
	    X_Memory_Write_Block (0x010E,
				  CArray (i->code.array () + 0x0E, 8)) != 0)
	  goto out;

	/*load the data from 0x119H to eeprom end */
	r = IMG_LOAD_MAIN;
	if (m.
	    X_Memory_Write_Block (0x119,
				  CArray (i->code.array () + 0x19,
					  i->code () - 0x19)) != 0)
	  goto out;

	if (m.X_Memory_Write_Block (0x0100, CArray (i->code.array (), 1)) !=
	    0)
	  goto out;

	/*erase the user RAM (0x0CE to 0x0DF) */
	r = IMG_ZERO_RAM;
	uchar zero[18] = { 0 };
	if (m.X_Memory_Write_Block (0x00ce, CArray (zero, 18)) != 0)
	  goto out;

	/* set the length of the address table */
	r = IMG_FINALIZE_ADDR_TAB;
	if (m.
	    X_Memory_Write_Block (0x0116,
				  CArray (i->code.array () + 0x16, 1)) != 0)
	  goto out;

	/* reset all error flags in the BCU (0x10D = 0xFF) */
	r = IMG_PREPARE_RUN;
	c = 0xff;
	if (m.X_Memory_Write_Block (0x010d, CArray (&c, 1)) != 0)
	  goto out;

	r = IMG_RESTART;
	m.A_Restart ();

	r = IMG_LOADED;
	goto out;
      }
    if (i->BCUType == BCUImage::B_bcu20 || i->BCUType == BCUImage::B_bcu21)
      {
	if (maskver != 0x0020 && i->BCUType == BCUImage::B_bcu20)
	  goto out;

	if (maskver != 0x0021 && i->BCUType == BCUImage::B_bcu21)
	  goto out;

	uchar level;
	r = IMG_AUTHORIZATION_FAILED;
	if (m.A_Authorize (i->installkey, level) == -1)
	  goto out;

	if (level)
	  goto out;

	r = IMG_KEY_WRITE;
	for (j = 0; j < 3; j++)
	  {
	    level = j;
	    if (m.A_KeyWrite (i->keys[level], level) == -1)
	      goto out;
	    if (j != level)
	      goto out;
	  }

	for (j = 0; j < i->load (); j++)
	  {
	    r = i->load[j].error;
	    if (i->load[j].obj != 0xff)
	      {
		if (m.A_Property_Write (i->load[j].obj, i->load[j].prop,
					i->load[j].start, 1, i->load[j].req,
					erg) == -1)
		  goto out;
		if (erg != i->load[j].result)
		  goto out;
	      }
	    if (i->load[j].memaddr != 0xffff)
	      if (m.
		  X_Memory_Write_Block (i->load[j].memaddr,
					CArray (i->code.array () +
						i->load[j].memaddr - 0x100,
						i->load[j].len)) != 0)
		goto out;
	  }

	r = IMG_RESTART;
	m.A_Restart ();

	r = IMG_LOADED;
	goto out;
      }
  }
  catch (Exception e)
  {
  }
out:

  if (i)
    delete i;
  EIBSETTYPE (buf, EIB_LOAD_IMAGE);
  buf[2] = (r >> 8) & 0xff;
  buf[3] = (r) & 0xff;
  c->sendmessage (4, buf, stop);
}


syntax highlighted by Code2HTML, v. 0.9.1