/*
 ******************************************************************************
 *  Copyright 1995 DeltaLink bv. All Rights Reserved.
 ******************************************************************************
 *
 * DeltaLink Decoder Task
 *
 * File: decoder.c
 *
 * This file contains the DeltaLink general purpose Decoder Task. This
 * task communicates with DeltaLink Protocol Drivers via the Mailbox Communication
 * Interface (MCI) V3 and en -decodes the data. 
 *
 */
#ifdef SLEEPPPP
#define NOMSG             /* typedef MSG and associated routines */
#define _OLE2_H_          			/* no ole */

#include  <windows.h>
#include  <winbase.h>
#endif

#include  <stdlib.h>
#include  <string.h>
#include  <stdio.h>
#include  <stdarg.h>
#include  <math.h>

#include  <../../version.h>             /*  FactoryLink definitions */

#include  <flib.h>                      /*  FactoryLink definitions */

#ifdef OS2hhh
#include  <dos.h>
#endif

#include  <g_utils.h>
#include  <fl_utils.h>
#include  <modules.h>

#include  <mci3.h>                       /*  Mailbox Communication Interface definitions */
#include  <format3.h>                    /*  Mailbox Communication Interface definitions */

#include  <decoder.h>                    /*  DeltaLink  Protocol Decoder definitions */

#define  XBUFSIZE  1024                  /*  fl_xlate buffer size */

/*
 * Task global variables
 */
TASK_ID       Task;												/* decoder task information */
MSG     			Rx_buf;                 		/* buffer for receiving with MCI */

TAG           *Tags;
u16           *Chbits;

int fl_sizes[ 5] = { sizeof( DIG),        /*  digital  */
										 sizeof( ANA),        /*  analog */
                     sizeof( FLP),        /*  floating point */
                     sizeof( MSG),        /*  message */
                     sizeof( LANA)};      /*  long analog */

char          msg_global[ MAX_MSG];

char          *ready_txt[] = { "read ready",
                               "write ready",
                               "rcv ready" };

DEC_TMFMT     time_conv;


/*
 * Task function prototypes
 */
void  process       ( void);

void  update        ( MCIDS *);
int   rx_check      ( void *, void *, INFO *);
void  rx_convert    ( void *, void *, INFO *);
void  tx_convert    ( void *, void *, INFO *);
int   ex_convert    ( void *, void *, INFO *);

int   dec_init_time_conv();
u32   dec_time2long(uchar *, int, int);
void  dec_long2time(uchar *, u32, int);

int   fixup     		( MCIDS *);

char  *Mci_String_Swap( char *, int);


/*-----------------------------------------------------------------------------
 * FUNCTION: int  main(int argc, char *argv[])
 *
 * PURPOSE: Task main function
 -----------------------------------------------------------------------------*/
int main( void) {

  int   			ret;
  static char xlbuf[ XBUFSIZE];

  /* initialize first the task structure */

  strcpy( Task.name, "DECODER");
  strcpy( Task.desc, "DeltaLink Basic Decoder");

  Task.dl_id    = DECODER;
  Task.version  = ( (rmj << 8) & 0xFF00) | ( rmm & 0xFF00);

  /* initialize this task */

  if( ( ret = dl_init_task( &Task)) != GOOD) {

    if( ret == DLE_DEMO_INIT) {

      fl_xlate_init( Task.name, xlbuf, XBUFSIZE);

      fl_status( &Task, "DEMO_MODE", FLS_INACTIVE);    /* task already started in demo mode */

      #ifndef _DEBUG
      dl_exit_task( &Task, 1);                         /* exit to OS */
      #endif
    }
    else
      exit( 1);
  }

  /* Set up for message translation */
  
  fl_task_xlate_load( &Task);

  fl_status( &Task,"START", FLS_STARTING);							/* indicate startup state */

  fl_sleep( 100);

  if (dec_init_time_conv() != GOOD)
  {
    fl_status( &Task, "TIMER_CONV", FLS_INACTIVE);
    dl_exit_task( &Task, 1);
  }

  tmfmt_load( &time_conv, fl_xlate( "DATETIME"));

  ctload();                      												/* load task configuration */

  debug_log( &Task, "DBG_CTLOAD", 1);

	fl_status( &Task, "RUN", FLS_ACTIVE);                 /* indicate running state */

  if( Task.mode & LIC_DEMO)
    fl_status( &Task, "DEMO_RUN", FLS_ACTIVE);          /* indicate running state */

  if( Task.mode & LIC_LITE)
    fl_status( &Task, "LITE_RUN", FLS_ACTIVE);          /* indicate running state */

  /*
   * Continuously loop, processing all input until
   * a terminate message has been sent to this task.
   */

	do {

		if( ( ret = fl_wait_event( &Task)) != GOOD) 															
			fl_status( &Task, "FL_CHG_WAIT", FLS_ERROR, ret);

	} while( dl_test_term_flag( &Task) == OFF);

	/*
   * exit the task
   */
  dl_exit_task( &Task, GOOD);                           

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: void  mci_event( MCIDS *ds)
 *
 * PURPOSE :	handles an incoming MCI event. This event is always the receipt of
 *						an MCI mailbox message. These events are registered through the MCI
 *						library which will call this user function. The MCI message has been
 *						prepared by the MCI library and is ready for reading.
 *
 -----------------------------------------------------------------------------*/
int mci_event( MCIDS *ds) {

	int				ret;
	DS_INFO		*ds_info;
	VAL				v;

	/* get the pointer to the addiotional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

  /* check what kind of message came in */

  switch( Mci_Get_Type( ds)) {

	  case MCI_READ_RSP:
	  case MCI_READ_ERROR:
      if (ds_info->read_sync)
        return GOOD;
      break;

	  case MCI_WRITE_RSP:
	  case MCI_WRITE_ERROR:
      if (ds_info->write_sync)
        return GOOD;
      break;

	  case MCI_RCV_RDY:
	  case MCI_RCV_ERROR:
      if (ds_info->rcv_sync)
        return GOOD;
      break;

    default:
      break;
  }

	/*
	 * receive the message from the mailbox queue
	 */

	if ((ret = Mci_Recv( &Mcisys, ds)) != GOOD)
  {

		fl_status( &Task, "IMX_RECV", FLS_ERROR, Mci_Get_Name( ds), fl_error( Task.id));

		return GOOD;
	}

  /* check what kind of message came in */

  switch( Mci_Get_Type( ds)) {

	  case MCI_QUERY_RSP:

			debug_log( &Task, "DBG_QUERY", 1,
								 Mci_Get_Name( ds),
								 Mci_Get_Boundary( ds),
								 Mci_Get_Bnd_Dir( ds),
								 Mci_Get_Bit_Dir( ds),
								 Mci_Get_Start( ds),
								 Mci_Get_Len( ds));

			/*
	     * run a fix-up for the bit addressing specified in case of arrays
	     */
			fixup( ds);

			/*
	     * check if this is a continuous read and if so trigger the dataset
			 */
			if( ds_info->continuous) {

      	v.dig = 1;
				block_read( (void *)ds, v);
			}

      /*
       * Check for read trigger before query result 
       */
      if ((ds_info->fnc & RD_Q_MASK))
      {

        /* remove flag */
        ds_info->fnc &= ~RD_Q_MASK;

        /* do block read */
        v.dig = 1;
				block_read( (void *)ds, v);
      }

      /*
       * Check for write trigger before query result 
       */
      if ((ds_info->fnc & WR_Q_MASK))
      {

        /* remove flag */
        ds_info->fnc &= ~WR_Q_MASK;

        /* do block write */
        v.dig = 1;
				block_write( (void *)ds, v);
      }
  		break;

	  case MCI_READ_RSP:

	    /* check if the receive fuction is enabled
	     * or the triggered read function and the dataset was triggered
	     */
	    if ( !(ds_info->fnc & TRIGGERED) ) break;

			ds_info->fnc &= ~TRIGGERED;

      /* check if the read functionality has been specified */

	    if ( !(ds_info->fnc & RD_MASK) ) break;

			/* check if the dataset has already been queried */

			if ( Mci_Ds_Queried( ds) != GOOD) break;

			debug_log( &Task, "DBG_READ", 2,
								 Mci_Get_Name( ds),
								 Mci_Get_Boundary( ds),
								 Mci_Get_Bnd_Dir( ds),
								 Mci_Get_Bit_Dir( ds),
								 Mci_Get_Start( ds),
								 Mci_Get_Len( ds));

			/* process the received data */

      update( ds);

      /* completion of this dataset */

      ds_completion( ds, RD_FUNCTION, 0);

			/*
	     * check if this is a continuous read and if so trigger the dataset
			 */
			if( ds_info->continuous)
      {
      	v.dig = 1;
				block_read( (void *)ds, v);
			}

			break;
	  case MCI_WRITE_RSP:

			debug_log( &Task, "DBG_WRITE", 2, Mci_Get_Name( ds));

      /* completion of this dataset */

      ds_completion( ds, WR_FUNCTION, 0);

	    break;
	  case MCI_RCV_RDY:

	    /*
	     * check first if this dataset is still enabled
	     */
	    if (ds_info->rcv_disable)
	      break;

	    /* check if the receive fuction is enabled
	     * or the triggered read function and the dataset was triggered
	     */
	    if( !( ds_info->fnc & UR_MASK))	break;

			/* check if the dataset has already been queried */

			if( Mci_Ds_Queried( ds) != GOOD) break;

			debug_log( &Task, "DBG_RECV", 2,
															Mci_Get_Name( ds),
															Mci_Get_Boundary( ds),
															Mci_Get_Bnd_Dir( ds),
															Mci_Get_Bit_Dir( ds),
															Mci_Get_Start( ds),
															Mci_Get_Len( ds));
			update( ds);

      /* completion of this dataset */

      ds_completion( ds, RCV_FUNCTION, 0);

	    break;

	  case MCI_READ_ERROR:
  
			fl_status( &Task, "IMX_ERROR", FLS_ACTIVE, "READ", Mci_Get_Name( ds), *( u16 *)Mci_Get_U_Buffer( ds));

      /* completion of this dataset */

      ds_completion( ds, RD_FUNCTION, *( u16 *)Mci_Get_U_Buffer( ds));
   
			/*
	     * check if this is a continuous read and if so trigger the dataset
			 */
			if (ds_info->continuous)
      {

      	v.dig = 1;
				block_read( (void *)ds, v);
			}

			break;
	  case MCI_WRITE_ERROR:
  
			fl_status( &Task, "IMX_ERROR", FLS_ACTIVE, "WRITE", Mci_Get_Name( ds), *( u16 *)Mci_Get_U_Buffer( ds));
    
      /* completion of this dataset */

      ds_completion( ds, WR_FUNCTION, *( u16 *)Mci_Get_U_Buffer( ds));

	    break;
	  case MCI_RCV_ERROR:
  
			fl_status( &Task, "IMX_ERROR", FLS_ACTIVE, "RCV", Mci_Get_Name( ds), *( u16 *)Mci_Get_U_Buffer( ds));
    
      /* completion of this dataset */

      ds_completion( ds, RCV_FUNCTION, *( u16 *)Mci_Get_U_Buffer( ds));

	    break;

	  default:

			fl_status( &Task, "IMX_MSG", FLS_ERROR, Mci_Get_Name( ds), Mci_Get_Type( ds));

	  break;
  }

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: int block_read( void *data, VAL *val)
 *
 * PURPOSE :	This function triggers a dataset for reading by a protocol driver.
 *						The MCI library handles the real triggering of the dataset. 
 *
 -----------------------------------------------------------------------------*/
int block_read( void *data, VAL v) {

	MCIDS		*ds = ( MCIDS *)data;
	DS_INFO	*ds_info;
	int			ret;


  /*
   * check first if value is 1 so this is a real trigger
   */
  if( !v.dig)
    return GOOD;

	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

  /*
   * check if this dataset is still enabled
   */
  if (ds_info->read_disable)
		return GOOD;

	/* check if the dataset has already been queried */

	if( Mci_Ds_Queried( ds) != GOOD)
  {
  
    /* dataset not yet queried, remember just one read */
    ds_info->fnc |= RD_Q_MASK;

    return GOOD;
  }

	debug_log( &Task, "DBG_BLOCK_R", 1, Mci_Get_Name( ds));

	/* trigger the DataSet Control Tag associated with this trigger */
 
	if( ( ret = Mci_Ds_Trigger( &Mcisys, ds )) != GOOD)
  {

		fl_status( &Task, "IMX_DS_TRIGGER", FLS_ERROR, Mci_Get_Name( ds), ret+FL_BASE_ERROR);

		return GOOD;
	}

  /* mark this dataset as being triggered */ 

  ds_info->fnc |= TRIGGERED;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int block_write( void *data, VAL v)
 *
 * PURPOSE :	This function initiates a block write which means that a block of
 *						contiguous elements will be written. Gaps in this block of data will
 *						be filled with zeros.
 *
 -----------------------------------------------------------------------------*/
int block_write( void *data, VAL v) {

  uint        nr_msg,           /*  number of messages in PD queue */
              mci_offset,
              i;
  float       multiplier;
  int         ret;
	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* check if this was a real trigger */

  if( !v.dig)
    return GOOD;

	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

  /*
   * check if the write function of this dataset is still enabled
   */
  if (ds_info->write_disable)
		return GOOD;

	/* check if the dataset has already been queried */

	if( Mci_Ds_Queried( ds) != GOOD)
  {

    /* dataset not yet queried, remember just one write */
    ds_info->fnc |= WR_Q_MASK;

    return GOOD;
  }

	/*
   * check the number of messages in the queue of the protocol driver
   */
  if( fl_count_mbx( Task.id,
                    Mci_Get_Snd_Mbx( ds),
                    &nr_msg) != GOOD) {

		fl_status( &Task, "FL_COUNT_MBX", FLS_ERROR, Mci_Get_Name( ds), fl_error( Task.id));

    /* completion of this dataset */

    ds_completion( ds, WR_FUNCTION, (u16)fl_error( Task.id));

    return GOOD;
  }

	/* check if the number exceeds the maximum number of messages */

	if( nr_msg >= ds_info->nr_msg) {

		fl_status( &Task, "MAX_MSG", FLS_ERROR, ds_info->nr_msg, Mci_Get_Name( ds));

    /* completion of this dataset */

    ds_completion( ds, WR_FUNCTION, (u16)(MCI_MAX_MESSAGE+FL_BASE_ERROR));
    
    return GOOD;
  }

	debug_log( &Task, "DBG_BLOCK_W", 1, Mci_Get_Name( ds));

  /* read all the data of the overlay tags */

  if( fl_read( Task.id,
               ds_info->tags,
               ds_info->nr_tags,
               ds_info->val) != GOOD) {

    fl_status( &Task,"FL_READ", FLS_ERROR, "'block write'", Mci_Get_Name( ds), fl_error( Task.id));

    /* completion of this dataset */

    ds_completion( ds, WR_FUNCTION, (u16)fl_error( Task.id));

    return GOOD;
  }

  /*
   * prepare the MCI dataset for sending with the buffer set at registering time
   */
	Mci_Ds_Prepare			( ds, NULL);

  Mci_Set_Type        ( ds, MCI_WRITE_REQ);
  Mci_Set_Handling    ( ds, MCI_NORMAL_WRITE);
  Mci_Set_CDE         ( ds, CDE_BLOCK);

  /*
   * absolute or relative addressing 
   */
  if( ds_info->abs == ABSOLUTE) {

    Mci_Set_Start( ds, ds_info->ct_start);
    mci_offset = ds_info->ct_start;
  }
  else {

    Mci_Set_Start( ds, ( u16)( ds_info->ct_start + Mci_Get_Start( ds)) );
    mci_offset = 0;
	}

  /* set the length of the data block as specified in the panels */

  Mci_Set_Len( ds, ds_info->ct_len);

  /*
   * set the multiplier depending on boundary 
   */
  if( Mci_Get_Boundary( ds) == MCI_BIT_BND) 
    multiplier = ( float)1/8;
  else 
		multiplier = Mci_Get_Boundary( ds);

	/* reset the user buffer */

	memset( Mci_Get_U_Buffer( ds), 0x00, (Mci_Get_Len( ds) * (uint)multiplier) +1);

  /* convert the data for writing to the protocol driver */

  for( i = 0; i < ds_info->nr_tags; i++) {

    /* convert the data and put on right place in MCI message */

    tx_convert( ( void *)( Mci_Get_U_Buffer( ds) + ( ushort)( (ds_info->info[ i].address - mci_offset) * multiplier)),
						    ( char *)ds_info->val +  ds_info->offset[ i],
						    &ds_info->info[ i]);
  }

	/* send the block write MCI dataset message to the protocol driver */

  if( ( ret = Mci_Send( &Mcisys, ds )) != GOOD)
		 fl_status( &Task, "IMX_SEND", FLS_ERROR, Mci_Get_Name( ds), ret);

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int  ex_write( void *data, VAL v)
 *
 * PURPOSE :	This function will be called in case of an exception write event.
 *						A normal and encoded exception write will be handled by this
 *						function.
 *
 *						The function enters with a pointer to information to the changed
 *						element and the new value of that element.
 *
 -----------------------------------------------------------------------------*/
int ex_write( void *data, VAL v)
{
	INFO				*info = ( INFO *)data;
	DS_INFO			*ds_info;
  uint         nr_msg;           						
  int          ret;


	/* check if the dataset has already been queried */

	if( Mci_Ds_Queried( info->ds) != GOOD)
    return GOOD;

	/* set the pointers to the MCI dataset and the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( info->ds);

  /*
   * check if the write function of this dataset is still enabled
   */
  if (ds_info->write_disable)
		return GOOD;

  /*
   * check the number of messages in the queue of the protocol driver
   */
  if( fl_count_mbx( Task.id,
                    Mci_Get_Snd_Mbx( info->ds),
                    &nr_msg) != GOOD)
  {

    fl_status( &Task, "FL_COUNT_MBX", FLS_ERROR, Mci_Get_Name( info->ds), fl_error( Task.id));

    /* completion of this dataset */

    ds_completion( info->ds, WR_FUNCTION, (u16)fl_error( Task.id));

    return GOOD;
  }

	/* check if the number exceeds the maximum number of messages */

	if( nr_msg >= ds_info->nr_msg)
  {

		fl_status( &Task, "MAX_MSG", FLS_ERROR, ds_info->nr_msg, Mci_Get_Name( info->ds));

    /* completion of this dataset */

    ds_completion( info->ds, WR_FUNCTION, (u16)(MCI_MAX_MESSAGE+FL_BASE_ERROR));
    
		return GOOD;
  }

	debug_log( &Task, "DBG_EXCEPT", 1, Mci_Get_Name( info->ds), info->address);

	/*
   * message tags are not supported at the moment 
   */
	if( info->t_type == FL_MESSAGE)
  {

		fl_status( &Task, "EX_MSG", FLS_ERROR);
	
  	return GOOD;
  }

  /* prepare a MCI dataset message to send to the Protocol Driver */

	Mci_Ds_Prepare				( info->ds, NULL);
  Mci_Set_Type          ( info->ds, MCI_WRITE_REQ);

  /*
   * check first which addressing method used and calculate the start address
   */
  if( ds_info->abs == ABSOLUTE)
    Mci_Set_Start( info->ds, info->address);
  else 
    Mci_Set_Start( info->ds, ( u16)( Mci_Get_Start( info->ds) + info->address));

  /*
   * set the lenght according to the boundary. In case of bit boundary always read
   * one bit  
   */
  if( Mci_Get_Boundary( info->ds) == MCI_BIT_BND)
		 Mci_Set_Len( info->ds, 1);
	else
  {

		switch( info->conv) { 																		/*  set the length in bytes */

      case C_BIT:
      case C_BYTE: case C_BYTE_R: case C_BYTE_L: case C_BCD2:

           Mci_Set_Len( info->ds, 1);

           break;

      case C_WORD: case C_IBG: case C_IBAU: case C_IBAS: case C_BCD: case C_BCD4:

           if( Mci_Get_Boundary( info->ds) < MCI_WORD_BND)
             Mci_Set_Len( info->ds, ( u16)( MCI_WORD_BND / Mci_Get_Boundary( info->ds)));
           else
             Mci_Set_Len( info->ds, 1);
             
           break;
      case C_LONG: case C_R_LONG: case C_SIE_FLT: case C_R_FLT: case C_IEEE_FLT: case C_TIME:

           if( Mci_Get_Boundary( info->ds) < MCI_LONG_BND)
             Mci_Set_Len( info->ds, ( u16)( MCI_LONG_BND / Mci_Get_Boundary( info->ds)));
           else
             Mci_Set_Len( info->ds, 1);

           break;
      case C_IEEE_DOUBLE:

           if( Mci_Get_Boundary( info->ds) < MCI_DBL_BND)
						 Mci_Set_Len( info->ds, ( u16)( MCI_DBL_BND / Mci_Get_Boundary( info->ds)));
           else
             Mci_Set_Len( info->ds, 1);

           break;
      case C_MSG:

           Mci_Set_Len( info->ds, 0);

           break;
      case C_TIM0: case C_TIM1: case C_TIM2: case C_TIM3: case C_TIM4:
      case C_TIM5: case C_TIM6: case C_TIM7: case C_TIM8: case C_TIM9:

           Mci_Set_Len( info->ds, info->bit/Mci_Get_Boundary( info->ds));
           if (info->bit % Mci_Get_Boundary( info->ds))
             Mci_Set_Len( info->ds, Mci_Get_Len( info->ds)+1);
           break;
      default:
					 fl_status( &Task, "EW_CONV", FLS_ERROR, Mci_Get_Name( info->ds), info->conv);
           return GOOD;
    }
  }

  /* reset the data buffer first */

  memset( Mci_Get_U_Buffer( info->ds), 0x00, Mci_Get_Len( info->ds) * ( Mci_Get_Boundary( info->ds) +1) );

  if( ds_info->fnc & EW_MASK)
  {

    Mci_Set_Handling( info->ds, MCI_NORMAL_WRITE);

    /*
     * determine wether the data can be written as a block write (direct) or an
     * exception write is needed.
     */
    switch( Mci_Get_Boundary( info->ds))
    {

      case MCI_BIT_BND:

				Mci_Set_CDE( info->ds, CDE_BLOCK);

				break;
      case MCI_BYTE_BND:

        if( info->operation < CDE_BYTE)
          Mci_Set_CDE( info->ds, info->operation);
        else
          Mci_Set_CDE( info->ds, CDE_BLOCK);

        break;
      case MCI_WORD_BND:

        if( info->operation < CDE_SIGNED)
          Mci_Set_CDE( info->ds, info->operation);
        else
          Mci_Set_CDE( info->ds, CDE_BLOCK);

        break;
      case MCI_LONG_BND:

        if( info->operation < CDE_LONG)
          Mci_Set_CDE( info->ds, info->operation);
        else
          Mci_Set_CDE( info->ds, CDE_BLOCK);

        break;
      case MCI_DBL_BND:

        if( info->operation < CDE_DOUBLE_IEEE_FLT)
          Mci_Set_CDE( info->ds, info->operation);
        else
          Mci_Set_CDE( info->ds, CDE_BLOCK);

        break;
    }
  }
  else {
   
    Mci_Set_Handling( info->ds, MCI_ENCODED_WRITE);
    Mci_Set_CDE( info->ds, info->operation);
  }

  /*
   * in case a bit operation adjust the bit start
   */
#ifdef BIT_ADJUST
	if( ( Mci_Get_Boundary( info->ds) != MCI_BIT_BND) && info->conv == C_BIT)
    Mci_Set_CDE( info->ds, ( u16)( Mci_Get_CDE( info->ds) - Mci_Get_Bit_Offset( info->ds)));
#endif

  /*
   * in case of exception write the data will be always in the HOST machine direction
   * and the data will be a value.
   *
   * In case of block write the data will be set in the format of the destination device.
   */
  if( (Mci_Get_CDE( info->ds) != CDE_BLOCK) && (Mci_Get_Handling( info->ds) != MCI_ENCODED_WRITE)) {

    /* set the direction of the HOST machine because exception elements are always in host format */

    Mci_Set_Bnd_Dir     	( info->ds, HOST_DIRECTION);
    /*Mci_Set_Bit_Dir       ( info->ds, HOST_DIRECTION);**/

    /* convert the data and put it as a value in the MCI message */

    if( ex_convert( Mci_Get_U_Buffer( info->ds),
                   &v,
                   info)) {

			fl_status( &Task, "EX_CONVERT", FLS_ERROR, Mci_Get_Name( info->ds), info->address);
      return GOOD;
    }
  }
  else {

    /* convert the data and put on right place in MCI message */

    tx_convert( Mci_Get_U_Buffer( info->ds),
                &v,
                info);
  }

  /* do the real sending to the Protocol Driver */

  if( ( ret = Mci_Send( &Mcisys, info->ds )) != GOOD)
    fl_status( &Task,"IMX_SEND", FLS_ERROR, Mci_Get_Name( info->ds), ret);

  return ret;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: void  update( MCIDS *ds)
 *
 * PURPOSE :	Updates the tags in the overlay in case of receiving data from a
 *						specific protocol driver.
 *
 -----------------------------------------------------------------------------*/
void update( MCIDS *ds )
{
	DS_INFO	*ds_info;
  uint  mci_offset,
        fl_offset,
        index = 0,
        start,
        end,     
        i;
  int   nr;
  float multiplier;


  /* get the pointer to the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

  /*
   * determine the end address of the received MCI message
   */
  if ( Mci_Get_Boundary( ds ) == MCI_BIT_BND )
    multiplier = ( float)1/8;
  else
    multiplier = Mci_Get_Boundary( ds);

  /* get start address of dataset */

  if( ds_info->abs == ABSOLUTE)
    start = Mci_Get_Start( ds);
  else
    start = 0;

  /* get end address of dataset */

  end = start + Mci_Get_Len( ds);

  /*
   * register the tag list first in the kernel
   */
  if ( ds_info->fnc & EW_MASK || ds_info->fnc & CW_MASK )
  {
    if( fl_change_bits_read( Task.id,
                             ds_info->tags, 
                             ds_info->nr_tags, 
                             Chbits ) != GOOD)
    {
		  fl_status( &Task, "FL_GET_CHG", FLS_ERROR, Mci_Get_Name( ds), fl_error( Task.id ));
      return;
    }
  }

  /* process every tag in the overlay of this DataSet */

  for (i = 0, nr = 0, fl_offset = 0; i < ds_info->nr_tags; i++)
  {
    if ( Chbits[ i ] )
    {
      /*
       * restore the change flag to indicate an eventual write in case a write also
       * applies to this dataset which removes the change flag
       */
      if (fl_set_chng( Task.id, &ds_info->tags[ i], 1) != GOOD)
				fl_status( &Task, "FL_SET_CHG", FLS_ERROR, Mci_Get_Name( ds), fl_error( Task.id));

      continue;
    }

    /*
     * check the addressing 
     */
    if( ds_info->info[ i].address >= start &&
        ds_info->info[ i].address < end)
    {
      /* check if absolute or relative addressing is used */

      if( ds_info->abs == ABSOLUTE)
        mci_offset = ds_info->info[ i].address - Mci_Get_Start( ds);
      else
        mci_offset = ds_info->info[ i].address;

      /*
       * check if the dataset is configured to update on exception and if so check if
       * the value has changed
       */
      if ( ds_info->fnc & ERD_MASK )
      {
        if ( !ds_info->start_check )
        {
          if ( rx_check ( ( char *)ds_info->old_val + ( ushort)( mci_offset * multiplier),
                          ( char *)Mci_Get_U_Buffer( ds) + ( ushort)( mci_offset * multiplier),
                          &ds_info->info[ i ] ) == GOOD )
          {
            continue;
          }
        }
      }

      /* copy the tag to the temporary tag array */

      memcpy( &Tags[ nr++], &ds_info->tags[ i], sizeof( TAG));

      /* set the value in the buffer and increment pointer with size of data type */

      rx_convert( ( char *)ds_info->val + fl_offset,
                  ( char *)Mci_Get_U_Buffer( ds) + ( ushort)( mci_offset * multiplier),
                  &ds_info->info[ i]);

      /* calculate the offset in the data buffer for FactoryLink of next element */

      fl_offset += fl_sizes[ ds_info->info[ i].t_type];
    }
  }

  /* update the tag array with the converted values */

 	debug_log( &Task, "Update %d changed tags of %d total", 9, nr, ds_info->nr_tags );

  if ( nr )
  {
    if( fl_write( Task.id,
                  Tags,
                  nr,
                  ( void *)ds_info->val) != GOOD)
		  fl_status( &Task,"FL_WRITE", FLS_ERROR, "tags", Mci_Get_Name( ds), fl_error( Task.id));
  }

  /*
   * reset the change flags of all updated tags
   */
  if ( ds_info->fnc & EW_MASK || ds_info->fnc & CW_MASK )
  {
    if( fl_clear_chng( Task.id, Tags, nr) != GOOD)
		  fl_status( &Task, "FL_CLEAR_CHG", FLS_ERROR, "tags", Mci_Get_Name( ds), fl_error( Task.id));
  }

  /* copy the values to the old values array if necessary */

  if ( nr && ( ds_info->fnc & ERD_MASK ) )
  {
    ds_info->start_check = 0;

    memcpy( ds_info->old_val, ( char *)Mci_Get_U_Buffer( ds ), ds_info->nr_tags * sizeof( VAL ));
  }

  return;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	void  rx_convert( void * , void *, INFO *)
 *
 * PURPOSE :	Converts a specific element in case of receiving from a protocol
 *						driver. 
 *
 -----------------------------------------------------------------------------*/
void rx_convert( void *d , void *s, INFO *info) {

  uint i,
       swap_len;
  u32  tmp_float;
  u16  u16_var;
  u32  u32_var;
  VAL  v;
  int  index;
  uchar  *t;


	/*
   * determine the conversion type of this element
	 */
  switch( info->conv) {
    /*
     * check for every conversion in which direction the data in the DataSet is ordered
     */
    case C_BIT:
      /*
       * first check from which kind of data type the data must be taken
       */

      switch (Mci_Get_Boundary( info->ds))
      {

        case MCI_BIT_BND:                   /*  get the bit out of a byte */

  				*( DIG *)&v = Mci_Get_Bit_Byte( info->ds, ( u8 *)s, info->address);
          tag_type_xlate( &v, FL_DIGITAL, (VAL *)d, info->t_type);
          if (((VAL*)d)->dig)
            ((VAL*)d)->dig = 1;
          break;

        case MCI_BYTE_BND:                  /*  get the bit out of a character */

          *( DIG *)&v = Mci_Get_Bit_Byte( info->ds, ( u8 *)s, info->bit);
          tag_type_xlate( &v, FL_DIGITAL, (VAL *)d, info->t_type);
          if (((VAL*)d)->dig)
            ((VAL*)d)->dig = 1;
          break;

        case MCI_WORD_BND:                  /*  get the bit out of a word */

          *( DIG *)&v = Mci_Get_Bit_Word( info->ds, ( u16 *)s, info->bit);
          tag_type_xlate( &v, FL_DIGITAL, (VAL *)d, info->t_type);
          if (((VAL*)d)->dig)
            ((VAL*)d)->dig = 1;
          break;

        case MCI_LONG_BND:                  /*  get the bit out of a long */

          *( DIG *)&v = Mci_Get_Bit_Long( info->ds, ( u32 *)s, info->bit);
          tag_type_xlate( &v, FL_DIGITAL, (VAL *)d, info->t_type);
          if (((VAL*)d)->dig)
            ((VAL*)d)->dig = 1;
          break;

        default:

					fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
          break;
      }

      break;
    case C_BYTE:

      *( ANA *)&v = *( unsigned char *)s;
      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_BYTE_R:

			*( ANA *)&v = Mci_Get_Byte_Right( info->ds, ( u16 *)s);
      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_BYTE_L:

      *( ANA *)&v = Mci_Get_Byte_Left( info->ds, ( u16 *)s);
      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_WORD:

      *( ANA *)&v = Mci_Get_Word( info->ds, ( i16 *)s);
      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_LONG:
    
      memcpy( ( char *)&v, ( char *)s, 4);
      
      *( LANA *)&v = Mci_Get_Long( info->ds, ( i32 *)&v);
      tag_type_xlate( &v, FL_LANALOG, (VAL *)d, info->t_type);
      break;

    case C_R_LONG:          /*  temporary patch for the modicon long inconvenience */

      memcpy( ( char *)&v, ( char *)s, 4);
      
      *( LANA *)&v = Mci_Get_R_Long( info->ds, ( i32 *)&v);
      tag_type_xlate( &v, FL_LANALOG, (VAL *)d, info->t_type);
      break;

    case C_IEEE_DOUBLE:             /*  IEEE double precision floating point */

      memcpy( ( char *)&v, ( char *)s, 8);
      
      *( double *)&v = Mci_Get_Double( info->ds, ( double *)&v);
      tag_type_xlate( &v, FL_FLOAT, (VAL *)d, info->t_type);
      break;

    case C_IEEE_FLT:                /*  IEEE single precision floating point */
     
      memcpy( ( char *)&v, ( char *)s, 4);
      
      tmp_float = Mci_Get_Long( info->ds, ( u32 *)&v);

			*( FLP *)&v = ( FLP)(*( float *)&tmp_float);
      tag_type_xlate( &v, FL_FLOAT, (VAL *)d, info->t_type);
			break;

    case C_R_FLT:                /*  Inverted IEEE single precision floating point */
     
      memcpy( ( char *)&v, ( char *)s, 4);
      
      tmp_float = Mci_Get_R_Long( info->ds, ( u32 *)&v);

      *( FLP *)&v = ( FLP)(*( float *)&tmp_float);
      tag_type_xlate( &v, FL_FLOAT, (VAL *)d, info->t_type);
		 break;

    case C_SIE_FLT:

      memcpy( ( char *)&v, ( char *)s, 4);
      
      *( u32 *)&v = Mci_Get_Long( info->ds, ( u32 *)&v);

      *( FLP *)&v = sie2ieee( *( u32 *)&v);
      tag_type_xlate( &v, FL_FLOAT, (VAL *)d, info->t_type);
      break;

    case C_VALMET_FLT:

      /*
       * convert a Valmet floating point to IEEE floating point.
       *
       * Valmet:  15 14 13           0
       *          S  O  MMMMMMMMMMMMMM
       *
       *          S  bit 15 is signed bit
       *          O  bit 14 overflow bit
       *          M  bit 13-0 mantissa bits
       */

      memcpy( ( char *)&u16_var, ( char *)s, 2);
      
      tmp_float = ( u32)Mci_Get_Word( info->ds, &u16_var) << 16;

      /*
       * leave the sign bit and set the exponent to 1023 (0x3ff0) 
       */
      (( u32 *)&v)[ 1] = (tmp_float & 0x80000000) | 0x3ff00000;

      /* set the mantissa on the IEEE */

      (( u32 *)&v)[ 1] |= ((tmp_float >> 10) & 0x000fffc0);

      (( u32 *)&v)[ 0] = 0;

      /* check if the value has to be corrected with the offset of 1 */

      if( !( tmp_float & 0x40000000) ) {

        if( tmp_float & 0x80000000)
          *( double *)&v += ( double)1;
        else
          *( double *)&v -= ( double)1;
      }

      tag_type_xlate( &v, FL_FLOAT, (VAL *)d, info->t_type);
      break;

    case C_MSG:                       /*  message conversion */

      /*
       * this type must always be message because there is no point in converting
       * messages to other types.
       */
      if (info->t_type == FL_MESSAGE)
      {

        /* supply conversion if boundary >= word, and direction low_2_high */ 
        if (Mci_Get_Bnd_Dir( info->ds) == MCI_LOW_2_HIGH)
        {

          switch (Mci_Get_Boundary( info->ds))
          {

            case MCI_BIT_BND:                   /*  just do nothing */
            case MCI_BYTE_BND:                  

              strncpy( ( char *)info->m_ptr, ( char *)s, info->bit);
              break;

            case MCI_WORD_BND:                  /*  get the bit out of a word */
            case MCI_LONG_BND:                  /*  get the bit out of a long */

              swap_len = (uint)info->bit / Mci_Get_Boundary( info->ds);

              if ((uint)info->bit % Mci_Get_Boundary( info->ds))
                swap_len++;

              /* allocate temporary array */
              if ((t = calloc(swap_len * Mci_Get_Boundary( info->ds), sizeof( char))) == NULL)
              {

                fl_status( &Task, "NOMEMORY", FLS_ERROR);
                break;
              }

              /* copy raw data */
              memcpy( t, ( char *)s, swap_len * Mci_Get_Boundary( info->ds));

              /* swap the byte in a word */
              for (i = 0; i < swap_len; i++)
                Mci_String_Swap((char *)t + (Mci_Get_Boundary( info->ds)*i), Mci_Get_Boundary( info->ds));

              /* get the swapped string */
              strncpy( ( char *)info->m_ptr, ( char *)t, info->bit);

              /* free temporary string */
              free( t);
              break;

            default:

					    fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
              break;
          }
        }
        else
          strncpy( ( char *)info->m_ptr, ( char *)s, info->bit);

        (( MSG *)d)->m_ptr = info->m_ptr;
        (( MSG *)d)->m_max = MAX_MSG;
        (( MSG *)d)->m_len = info->bit;
      }
      break;

    case C_BCD2:

      /*  BCD conversion on a byte */

      u16_var = (u16)(*( char *)s);

      for( i = 0, *( ANA *)&v = 0; i < 2; i++)
        *( ANA *)&v += (ANA)((double)(( u16_var >> (i * 4)) & 0x000F) * pow( (double )10, ( double)i));

      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_BCD:
    case C_BCD4:

      /*  BCD conversion on a word */

      u16_var = Mci_Get_Word( info->ds, ( u16 *)s);


      for( i = 0, *( ANA *)&v = 0; i < 4; i++)
        *( ANA *)&v += (ANA)((double)(( u16_var >> (i * 4)) & 0x000F) * pow( (double )10, ( double)i));

      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_BCD8:

      /*  BCD conversion on a long */

      u32_var = Mci_Get_Long( info->ds, ( u32 *)s);


      for( i = 0, *( LANA *)&v = 0; i < 8; i++)
        *( LANA *)&v += (LANA)((double)(( u32_var >> (i * 4)) & 0x000F) * pow( (double )10, ( double)i));

      tag_type_xlate( &v, FL_LANALOG, (VAL *)d, info->t_type);
      break;

    case C_IBG:                             /*  Interbus-S Gain for analog in/out */

      *( ANA *)&v = Mci_Get_Word( info->ds, ( u16 *)s);
      *( ANA *)&v = ( ANA)pow( 10, (*( ANA *)&v >> 14) & 0x0003);

      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_IBAU:                            /*  Interbus-S Analog Input/Output 11 or 12 bits Unsigned */

      *( ANA *)&v = Mci_Get_Word( info->ds, ( u16 *)s);
      *( ANA *)&v &= 0x3fff;

      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_IBAS:                            /*  Interbus-S Analog Input/Output 11 or 12 bits two's complement */

      /* check the direction of the data of this element */

      *( ANA *)&v = Mci_Get_Word( info->ds, ( u16 *)s);

      if( (*( ANA *)&v & 0x3fff)  &  ( 0x0001 << info->bit))
        *( ANA *)&v |=  0xffff << info->bit;
      else
        *( ANA *)&v &= ~( 0xffff << info->bit);

      tag_type_xlate( &v, FL_ANALOG, (VAL *)d, info->t_type);
      break;

    case C_TIME:
    
      memcpy( ( char *)&v, ( char *)s, 4);
      
      *( LANA *)&v = Mci_Get_Long( info->ds, ( u32 *)&v);

      /* adjust for SECTIME */
      if (v.lana > SECONDS_DIFF_SECTIME)
        v.lana -= SECONDS_DIFF_SECTIME;
      else
        v.lana = 0L;

      /* conversion from lana to msg needed if destination is message tag */
      if (info->t_type == FL_MESSAGE)
      {

        (( MSG *)d)->m_ptr = fmt_time( info->m_ptr, &time_conv, v.lana);
        (( MSG *)d)->m_max = MAX_MSG;
        (( MSG *)d)->m_len = strlen( info->m_ptr);

      }
      else
        tag_type_xlate( &v, FL_LANALOG, (VAL *)d, info->t_type);
      break;

    case C_TIM0:
    case C_TIM1:
    case C_TIM2:
    case C_TIM3:
    case C_TIM4:
    case C_TIM5:
    case C_TIM6:
    case C_TIM7:
    case C_TIM8:
    case C_TIM9:

      if (!(swap_len = (uint)info->bit / Mci_Get_Boundary( info->ds)))
        break;
      
      if ((uint)info->bit % Mci_Get_Boundary( info->ds))
        swap_len++;

      /* allocate temporary array */
      if ((t = calloc(swap_len * Mci_Get_Boundary( info->ds), sizeof( char))) == NULL)
      {

        fl_status( &Task, "NOMEMORY", FLS_ERROR);
        break;
      }

      /* copy raw data */
      memcpy( t, ( char *)s, swap_len * Mci_Get_Boundary( info->ds));

      /* supply conversion if boundary >= word, and direction low_2_high */ 

      /*if (Mci_Get_Bnd_Dir( info->ds) == MCI_LOW_2_HIGH)*/

      if (Mci_Get_Bnd_Dir( info->ds) == MCI_HIGH_2_LOW)
      {

        switch (Mci_Get_Boundary( info->ds))
        {

          case MCI_BIT_BND:                   /*  just do nothing */
          case MCI_BYTE_BND:                  
            break;

          case MCI_WORD_BND:                  /*  get the bit out of a word */
          case MCI_LONG_BND:                  /*  get the bit out of a long */

            /* swap the byte in a word */
            for (i = 0; i < swap_len; i++)
              Mci_String_Swap((char *)t + (Mci_Get_Boundary( info->ds)*i), Mci_Get_Boundary( info->ds));
            break;

          default:

					  fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
            break;
        }
      }

      swap_len = swap_len * Mci_Get_Boundary( info->ds);
      
      /* user defined time conversion */
      index = info->conv - C_TIM0;

      /* now convert to time value */
      v.lana = dec_time2long(t, swap_len, index);

      /* conversion from lana to msg needed if destination is message tag */
      if (info->t_type == FL_MESSAGE)
      {

        (( MSG *)d)->m_ptr = fmt_time( info->m_ptr, &time_conv, v.lana);
        (( MSG *)d)->m_max = MAX_MSG;
        (( MSG *)d)->m_len = strlen( info->m_ptr);

      }
      else
        tag_type_xlate( &v, FL_LANALOG, (VAL *)d, info->t_type);

      /* free temporary string */
      free( t);
      break;

    default:
      break;
  }

  return;              
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	rx_check
 *
 * PURPOSE :	Checks if an element received has been changed with its old value
 *
 -----------------------------------------------------------------------------*/
int rx_check( void *old_val , void *new_val, INFO *info )
{
  uint swap_len;
  ANA  a;

	/*
   * determine the conversion type of this element
	 */
  switch( info->conv )
  {
    /*
     * check for every conversion in which direction the data in the DataSet is ordered
     */
    case C_BIT:
      /*
       * first check from which kind of data type the data must be taken
       */

      switch ( Mci_Get_Boundary( info->ds ) )
      {

        case MCI_BIT_BND:                   /*  get the bit out of a byte */

  				if ( Mci_Get_Bit_Byte( info->ds, ( u8 *)old_val, info->address ) !=
               Mci_Get_Bit_Byte( info->ds, ( u8 *)new_val, info->address )
             )
             return ERROR;

          break;

        case MCI_BYTE_BND:                  /*  get the bit out of a character */

          if ( Mci_Get_Bit_Byte( info->ds, ( u8 *)old_val, info->bit) !=
                Mci_Get_Bit_Byte( info->ds, ( u8 *)new_val, info->bit)
             )
             return ERROR;

          break;

        case MCI_WORD_BND:                  /*  get the bit out of a word */

          if ( Mci_Get_Bit_Word( info->ds, ( u16 *)old_val, info->bit) !=
               Mci_Get_Bit_Word( info->ds, ( u16 *)new_val, info->bit)
             )
             return ERROR;


          break;

        case MCI_LONG_BND:                  /*  get the bit out of a long */

          if ( Mci_Get_Bit_Long( info->ds, ( u32 *)old_val, info->bit) !=
               Mci_Get_Bit_Long( info->ds, ( u32 *)new_val, info->bit)
             )
             return ERROR;

          break;

        default:

					fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
          break;
      }

      break;
    case C_BYTE:

      if ( *( char *)old_val != *( char *)new_val )
        return ERROR;

      break;

    case C_BYTE_R:

			if( Mci_Get_Byte_Right( info->ds, ( u16 *)old_val ) !=
          Mci_Get_Byte_Right( info->ds, ( u16 *)new_val )
        )
        return ERROR;

      break;

    case C_BYTE_L:

      if ( Mci_Get_Byte_Left( info->ds, ( u16 *)old_val ) != 
           Mci_Get_Byte_Left( info->ds, ( u16 *)new_val )
         )
         return ERROR;

      break;

    case C_WORD:
    case C_VALMET_FLT:
    case C_BCD:
    case C_BCD2:
    case C_BCD4:
    case C_IBG:                             /*  Interbus-S Gain for analog in/out */
    case C_IBAU:                            /*  Interbus-S Analog Input/Output 11 or 12 bits Unsigned */
    case C_IBAS:                            /*  Interbus-S Analog Input/Output 11 or 12 bits two's complement */

      a = Mci_Get_Word( info->ds, ( i16 *)old_val);
      a = Mci_Get_Word( info->ds, ( i16 *)new_val);

      if ( Mci_Get_Word( info->ds, ( i16 *)old_val ) != 
           Mci_Get_Word( info->ds, ( i16 *)new_val )
         )
         return ERROR;

      break;

    case C_LONG:
    
      if ( Mci_Get_Long( info->ds, ( i32 *)old_val ) !=
           Mci_Get_Long( info->ds, ( i32 *)new_val )
         )
         return ERROR;

      break;

    case C_R_LONG:          /*  temporary patch for the modicon long inconvenience */
    case C_R_FLT:                /*  Inverted IEEE single precision floating point */
    case C_SIE_FLT:
    case C_BCD8:
    case C_TIME:


      if( Mci_Get_R_Long( info->ds, ( i32 *)old_val) !=
          Mci_Get_R_Long( info->ds, ( i32 *)new_val)
        )
        return ERROR;


      break;

    case C_IEEE_DOUBLE:             /*  IEEE double precision floating point */
    case C_IEEE_FLT:                /*  IEEE single precision floating point */
     

      if ( Mci_Get_Double( info->ds, ( double *)old_val ) !=
           Mci_Get_Double( info->ds, ( double *)new_val ) 
         )
         return ERROR;

      break;

    case C_TIM0:
    case C_TIM1:
    case C_TIM2:
    case C_TIM3:
    case C_TIM4:
    case C_TIM5:
    case C_TIM6:
    case C_TIM7:
    case C_TIM8:
    case C_TIM9:
    case C_MSG:                       

      /* determine the length of the time string first */

      if ( !( swap_len = ( uint )info->bit / Mci_Get_Boundary( info->ds ) ) )
        break;
      
      if ( ( uint )info->bit % Mci_Get_Boundary( info->ds ) )
        swap_len++;

      swap_len = swap_len * Mci_Get_Boundary( info->ds );

      /* do the compare of the data */

      if ( memcmp( old_val, new_val, swap_len) )
        return ERROR;

      break;

    default:
      break;
  }

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	void  tx_convert( void * , void *, INFO *)
 *
 * PURPOSE :	Converts the specific element in case sending to a protocol driver.
 *						This function must be used in case of block write data. 
 *
 -----------------------------------------------------------------------------*/
void tx_convert( void *d , void *s, INFO *info)
{

  uint  i,
        swap_len;
  VAL   v;
  u16   u16_var;
  u32   u32_var;
  uchar  *t;

 
	/*
   * determine the conversion type of the element 
	 */
	switch( info->conv)
  {

    /*
     * check for every conversion in which direction the data in the DataSet is ordered
     */
    case C_BIT:

      /*
       * first check from which kind of data type the data must be taken
       */
      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_BIT_BND:                   /*  get the bit out of a byte */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
					Mci_Set_Bit_Byte( info->ds, ( u8 *)d, info->address, ( DIG *)&v);

					break;
        case MCI_BYTE_BND:                  /*  get the bit out of a character */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          Mci_Set_Bit_Byte( info->ds, ( u8 *)d, info->bit, ( DIG *)&v);

          break;
        case MCI_WORD_BND:                  /*  get the bit out of a word */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          Mci_Set_Bit_Word( info->ds, ( u16 *)d, info->bit, ( DIG *)&v);

          break;
        case MCI_LONG_BND:                  /*  get the bit out of a long */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          Mci_Set_Bit_Long( info->ds, ( u32 *)d, info->bit, ( DIG *)&v);

          break;
      }

      break;
    case C_BYTE:

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);
      *( char *)d = ( char)( *( ANA *)&v);

      break;
    case C_BYTE_R:

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);
      Mci_Set_Byte_Right( info->ds, ( i16 *)d, ( ANA *)&v);

      break;
    case C_BYTE_L:

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);
      Mci_Set_Byte_Left( info->ds, ( i16 *)d, ( ANA *)&v);

      break;
    case C_WORD:
    case C_IBAU:                      /*  Interbus-S Analog Input/Output 11 or 12 bits Unsigned */
    case C_IBAS:                      /*  Interbus-S Analog Input/Output 11 or 12 bits two's complement */

      /* check the direction of the data of this element */

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);
      Mci_Set_Word( info->ds, ( i16 *)d, ( ANA *)&v);
      break;

    case C_LONG:

      tag_type_xlate( s, info->t_type, &v, FL_LANALOG);
      Mci_Set_Long( info->ds, ( i32 *)d, ( LANA *)&v);
      break;

    case C_R_LONG:

      tag_type_xlate( s, info->t_type, &v, FL_LANALOG);
      Mci_Set_R_Long( info->ds, ( i32 *)d, ( LANA *)&v);
      break;

    case C_IEEE_DOUBLE:             /*  IEEE double precision floating point 64 bits */

      tag_type_xlate( s, info->t_type, &v, FL_FLOAT);
      Mci_Set_Double( info->ds, ( double *)d, ( double *)&v);
      break;

    case C_IEEE_FLT:                /*  IEEE single precision floating point 32 bits */
     
     tag_type_xlate( s, info->t_type, &v, FL_FLOAT);
      *( float *)d = ( float)( *( FLP *)&v);

      Mci_Set_Long( info->ds, ( u32 *)d, ( LANA *)d);
      break;

    case C_R_FLT:                /*  IEEE single precision floating point 32 bits */
     
     tag_type_xlate( s, info->t_type, &v, FL_FLOAT);
      *( float *)d = ( float)( *( FLP *)&v);

			Mci_Set_R_Long( info->ds, ( u32 *)d, ( LANA *)d);
			break;

    case C_SIE_FLT:

      tag_type_xlate( s, info->t_type, &v, FL_FLOAT);
      *( u32 *)d = ieee2sie( *( FLP *)&v);

      Mci_Set_Long( info->ds, ( u32 *)d, ( LANA *)d);
      break;

    case C_VALMET_FLT:

      /*
       * convert a Valmet floatin point to IEEE floating point.
       *
       * Valmet:  15 14 13           0
       *          S  O  MMMMMMMMMMMMMM
       *
       *          S  bit 15 is signed bit
       *          O  bit 14 overflow bit
       *          M  bit 13-0 mantissa bits
       */

      tag_type_xlate( s, info->t_type, &v, FL_FLOAT);

      /*
       * the value of the Valmet floating point must be between 2 and -2
       */
      if( *( FLP *)&v >= ( FLP)2) 
      {
        *( u16 *)d = 0x7FFF;      /* value == 2 */
        break;
      }

      if( *( FLP *)&v <= ( FLP)-2)
      {
        *( u16 *)d = 0xFFFF;      /* value == -2 */
        break;
      }

      /* reset the valmet value */

      *( u16 *)d = 0;

      /* check if the overflow bit is set */

      if( *( FLP *)&v <= ( FLP)0)
        *( u16 *)d |= 0x8000;

      if( ( *( FLP *)&v <= ( FLP)-1 ) || ( *( FLP *)&v >= ( FLP)1 ))
      {
        *( u16 *)d |= 0x4000;
      }
      else
      {
        if( *( FLP *)&v <= ( FLP)0)
          *( FLP *)&v -= ( FLP)1;
        else
          *( FLP *)&v += ( FLP)1;
      }

      *( u16 *)d |= (( u16)( *( u32 *)&v >> 7 )) & 0x3FFF;

      Mci_Set_Word( info->ds, ( u16 *)d, ( ANA *)d);
      break;

    case C_MSG:                       /*  message conversion */

      /*
       * this type must always be message because there is no point in converting
       * messages to other types.
       */
      if( info->t_type == FL_MESSAGE)
      {

        /* check for swapping */
        if (Mci_Get_Bnd_Dir( info->ds) == MCI_LOW_2_HIGH)
        {

          switch (Mci_Get_Boundary( info->ds))
          {

            case MCI_BIT_BND:                   /*  just do nothing */
            case MCI_BYTE_BND:                  

              strncpy( (char *)d, ( char *)info->m_ptr, info->bit);
              break;

            case MCI_WORD_BND:                  /*  get the bit out of a word */
            case MCI_LONG_BND:                  /*  get the bit out of a long */

              swap_len = (uint)info->bit / Mci_Get_Boundary( info->ds);

              if ((uint)info->bit % Mci_Get_Boundary( info->ds))
                swap_len++;

              /* get last word/long and go to opposite direction */
              Mci_String_Swap((char *)d + (Mci_Get_Boundary( info->ds)*(swap_len - 1)), 
                              Mci_Get_Boundary( info->ds));

              strncpy( (char *)d, ( char *)info->m_ptr, info->bit);

              /* swap the byte in a word */
              for (i = 0; i < swap_len; i++)
                Mci_String_Swap((char *)d + (Mci_Get_Boundary( info->ds)*i), 
                Mci_Get_Boundary( info->ds));

              break;

            default:

					    fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
              break;
          }
        }
        else
          strncpy( (char *)d, ( char *)info->m_ptr, info->bit);

      }
      break;

    case C_BCD2:

      *( char *)d = 0;

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      for( i = 0, u16_var = 0; i < 2; i++)
      {

        u16_var |= (((u16)v.ana % (u16)10) << (i * 4));
        v.ana /= (u16)10;
      }

      *( char *)d = ( char)(u16_var);
      break;

    case C_BCD:
    case C_BCD4:

      *( u16 *)d = 0;

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      for( i = 0, u16_var = 0; i < 4; i++)
      {

        u16_var |= (((u16)v.ana % (u16)10) << (i * 4));
        v.ana /= (u16)10;
      }

      Mci_Set_Word( info->ds, (u16 *)d, (ANA *)&u16_var);
      break;

    case C_BCD8:

      *( u32 *)d = 0;

      tag_type_xlate( s, info->t_type, &v, FL_LANALOG);

      for( i = 0, u32_var = 0; i < 8; i++)
      {

        u32_var |= (((u32)v.lana % (u32)10) << (i * 4));
        v.lana /= (u32)10;
      }

      Mci_Set_Long( info->ds, ( u32 *)d, ( LANA *)&u32_var);
      break;

    case C_IBG:                             /*  Interbus-S Gain for analog in/out */

      /* check the direction of the data of this element */

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      if( *( i16 *)&v > 0) {

        *( u16 *)d = ( u16)( (( u16)log10( *( u16 *)&v) << 14) & 0xC000);

        Mci_Set_Word( info->ds, ( u16 *)d, ( ANA *)d);
      }
      else
        *( u16 *)d = 0;

      break;

    case C_TIME:

      /*
       * For msg tag: convert to SECTIME format
       */
      if( info->t_type == FL_MESSAGE)
        v.lana = get_fmt_time( ( char *)info->m_ptr, &time_conv);
      else 
        tag_type_xlate( s, info->t_type, &v, FL_LANALOG);

      /* adjust for SECTIME */
      v.lana += SECONDS_DIFF_SECTIME;

      Mci_Set_Long( info->ds, ( u32 *)d, ( LANA *)&v);
      break;

    case C_TIM0:                       /*  user defined time conversion */
    case C_TIM1:                       /*  user defined time conversion */
    case C_TIM2:                       /*  user defined time conversion */
    case C_TIM3:                       /*  user defined time conversion */
    case C_TIM4:                       /*  user defined time conversion */
    case C_TIM5:                       /*  user defined time conversion */
    case C_TIM6:                       /*  user defined time conversion */
    case C_TIM7:                       /*  user defined time conversion */
    case C_TIM8:                       /*  user defined time conversion */
    case C_TIM9:                       /*  user defined time conversion */

      /*
       * For msg tag: convert to SECTIME format
       */
      if( info->t_type == FL_MESSAGE)
        v.lana = get_fmt_time( ( char *)info->m_ptr, &time_conv);
      else 
        tag_type_xlate( s, info->t_type, &v, FL_LANALOG);

      if (!(swap_len = (uint)info->bit / Mci_Get_Boundary( info->ds)))
        break;
      
      if ((uint)info->bit % Mci_Get_Boundary( info->ds))
        swap_len++;

      /* allocate temporary array */
      if ((t = calloc(swap_len * Mci_Get_Boundary( info->ds), sizeof( char))) == NULL)
      {

        fl_status( &Task, "NOMEMORY", FLS_ERROR);
        break;
      }

      /* conversion to string */
      dec_long2time(t, v.lana, info->conv - C_TIM0);

      /* check for swapping */
      /*if (Mci_Get_Bnd_Dir( info->ds) == MCI_LOW_2_HIGH)*/

      if (Mci_Get_Bnd_Dir( info->ds) == MCI_HIGH_2_LOW)
      {

        switch (Mci_Get_Boundary( info->ds))
        {

          case MCI_BIT_BND:                   /*  just do nothing */
          case MCI_BYTE_BND:                  

            memcpy( (char *)d, t, info->bit);
            break;

          case MCI_WORD_BND:                  /*  get the bit out of a word */
          case MCI_LONG_BND:                  /*  get the bit out of a long */

            /* get last word/long and go to opposite direction */
            Mci_String_Swap((char *)d + (Mci_Get_Boundary( info->ds)*(swap_len - 1)), 
                            Mci_Get_Boundary( info->ds));

            memcpy( (char *)d, t, info->bit);

            /* swap the byte in a word */
            for (i = 0; i < swap_len; i++)
              Mci_String_Swap((char *)d + (Mci_Get_Boundary( info->ds)*i), 
              Mci_Get_Boundary( info->ds));

            break;

          default:

					  fl_status( &Task, "IMX_BOUND", FLS_ERROR, Mci_Get_Name( info->ds), Mci_Get_Boundary( info->ds));
            break;
        }
      }
      else
        memcpy( (char *)d, t, info->bit);

      /* free temporary space */
      free( t);
      break;

    default:
      break;
  }

  return;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: int  ex_convert( void *d , void *s, ushort conv, i16 t_type)
 *
 * PURPOSE : Converts an exception element for sending to a protocol driver.
 *
 -----------------------------------------------------------------------------*/
int ex_convert( void *d , void *s, INFO *info)
{

  VAL    v;
  int    i;
  u16    u16_var;


	/*
   * determine the conversion type of the element
	 */
	switch( info->conv) {

    /*
     * check for every conversion in which direction the data in the DataSet is ordered
     */
    case C_BIT:

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_BYTE_BND:

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          *( char *)d = ( char)( *( DIG *)&v);

          break;

        case MCI_WORD_BND:                  /*  put digital in a word */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          *( ushort *)d = ( ushort)( *( DIG *)&v);

					break;
        case MCI_LONG_BND:                  /*  put digital in a long */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          *( ulong *)d = ( ulong)( *( DIG *)&v);

          break;
        case MCI_DBL_BND:                  /*  put digital in double */

          tag_type_xlate( s, info->t_type, &v, FL_DIGITAL);
          *( double *)d = ( double)( *( DIG *)&v);

          break;

        default:
          return -1;
      }
      break;

    case C_BYTE:
    case C_BYTE_R:
    case C_BYTE_L:

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_WORD_BND:                  /*  put digital in a word */

          *( short *)d = ( short)( *( ANA *)&v);

          break;

        default:
          return -1;
      }
      break;

    case C_WORD:
    case C_IBAU:                      /*  Interbus-S Analog Input/Output 11 or 12 bits Unsigned */
    case C_IBAS:                      /*  Interbus-S Analog Input/Output 11 or 12 bits two's complement */

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      switch( Mci_Get_Boundary( info->ds))
      {

        case MCI_LONG_BND:                  /*  put digital in a long */

          *( long *)d = ( long)( *( ANA *)&v);
          break;

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)( *( ANA *)&v);
          break;

        default:
          return -1;
      }
      break;

    case C_LONG:
    case C_R_LONG:

      tag_type_xlate( s, info->t_type, &v, FL_LANALOG);

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)( *( LANA *)&v);

          break;

        default:
          return -1;
      }
      break;

    case C_IEEE_DOUBLE:             /*  IEEE double precision floating point 64 bits */
      break;

    case C_IEEE_FLT:                /*  IEEE single precision floating point 32 bits */
    case C_R_FLT:                /*  IEEE single precision floating point 32 bits */

      tag_type_xlate( s, info->t_type, &v, FL_FLOAT);

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)( *( FLP *)&v);

          break;

        default:
          return -1;
      }
      break;

    case C_SIE_FLT:               /*  32 bits */

      tag_type_xlate( s, info->t_type, &v, FL_FLOAT);

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)ieee2sie( *( FLP *)&v);

          break;

        default:
          return -1;
      }
      break;

    case C_IBG:                             /*  Interbus-S Gain for analog in/out */

      /* check the direction of the data of this element */

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_LONG_BND:                  /*  put digital in a long */

          *( ulong *)d = ( ulong)( *( ANA *)&v);
          if( *( i16 *)&v > 0)
            *( ulong *)d = (( ulong)log10( *( u16 *)&v) << 14) & 0xC000;
          else
            *( ulong *)d = 0;

          break;
        case MCI_DBL_BND:                  /*  put digital in double */

          if( *( i16 *)&v > 0)
            *( double *)d = ( double)( ((( long)log10( *( u16 *)&v) << 14)) & 0xC000);
          else
            *( double *)d = 0;

          break;

        default:
          return -1;
      }
      break;

    case C_BCD2:

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      for( i = 0, u16_var = 0; i < 2; i++)
      {

        u16_var |= (((u16)v.ana % (u16)10) << (i * 4));
        v.ana /= (u16)10;
      }

      switch( Mci_Get_Boundary( info->ds))
      {

        case MCI_WORD_BND:                  /*  put digital in a word */

          *( ushort *)d = ( ushort)( u16_var);

          break;

        default:
          return -1;
      }
      break;

    case C_BCD:
    case C_BCD4:

      *( u16 *)d = 0;

      tag_type_xlate( s, info->t_type, &v, FL_ANALOG);

      for( i = 0, u16_var = 0; i < 4; i++)
      {

        u16_var |= (((u16)v.ana % (u16)10) << (i * 4));
        (u16)v.ana /= (u16)10;
      }

      switch( Mci_Get_Boundary( info->ds))
      {

        case MCI_LONG_BND:                  /*  put digital in a long */

          *( ulong *)d = ( ulong)( *( ANA *)&u16_var);
          break;

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)( *( ANA *)&u16_var);
          break;

        default:
          return -1;
      }
      break;

    case C_TIME:

      /*
       * For msg tag: convert to SECTIME format
       */
      if( info->t_type == FL_MESSAGE)
        v.lana = get_fmt_time( ( char *)info->m_ptr, &time_conv);
      else 
        tag_type_xlate( s, info->t_type, &v, FL_LANALOG);

      /* adjust for SECTIME */
      v.lana += SECONDS_DIFF_SECTIME;

      switch( Mci_Get_Boundary( info->ds)) {

        case MCI_DBL_BND:                  /*  put digital in double */

          *( double *)d = ( double)( *( LANA *)&v);

          break;

        default:
          return -1;
      }
      break;
    default:
      return -1;
  }

  return GOOD;
}


/*-----------------------------------------------------------------------------
 *
 * FUNCTION:	int fixup( MCIDS *ds)
 *
 * PURPOSE :	calculate the bit addresses in case of an array for this dataset now
 *						the boundary is known and check if the size of the dataset has to be
 *						adjusted 
 *
 -----------------------------------------------------------------------------*/
int fixup( MCIDS *ds) {

  DS_INFO *ds_info;
  uint    i, factor;
  uint    max = 0;                 /* maximum io address */
  int     size;
  int     size_max = 1;
  int     size_i;
  ushort  bit;


  /* get the pointer to the additional dateset information */

  ds_info = Mci_Get_Udata_Ptr( ds);

  if (!ds_info->nr_tags)
    return GOOD;

  bit = ds_info->info[ 0].address;

  /* find a value for the size */
  switch (ds_info->info[ 0].conv)
  {

    case C_MSG:
    case C_TIM0:
    case C_TIM1:
    case C_TIM2:
    case C_TIM3:
    case C_TIM4:
    case C_TIM5:
    case C_TIM6:
    case C_TIM7:
    case C_TIM8:
    case C_TIM9:

      size_max = ds_info->info[ 0].bit;

      if (size_max % Mci_Get_Boundary( ds))
        size_max = (size_max / Mci_Get_Boundary( ds)) * Mci_Get_Boundary( ds) + Mci_Get_Boundary( ds);
      break;

    default:
      size_max = Mci_Get_CDE_Size( ds_info->info[ 0].operation);
      break;
  }

  /*
   * loop through all elements of this dataset 
   */
  for( i = 0; i < ds_info->nr_tags; i++)
  {

    /*
     * check first if this dataset has bit boundary
     */
    if (Mci_Get_Boundary( ds) == MCI_BIT_BND)
    {

      if (ds_info->info[ i].fix)
      {

        ds_info->info[ i].address = bit++;

        if ((ds_info->ct_start + ds_info->info[ i].address) > ds_info->ct_len)
          ds_info->ct_len++;
      }
      else
        bit = ds_info->info[ i].address +1;

      /* get max address index */
      if (ds_info->info[ i].address > ds_info->info[ 0].address)
        max = i;

      continue;
    }

    /*
     * check if this is a bit element
     */
    if( ds_info->info[ i].conv == C_BIT)
    {

      /*
       * check if the bit number is higher than the boundary size
       */
      if( ds_info->info[ i].bit < ((Mci_Get_Boundary( ds) * 8) + Mci_Get_Bit_Offset( ds)))
      {

        /* get max address index */
        if (ds_info->info[ i].address > ds_info->info[ 0].address)
          max = i;

        continue;
      }

      /*
       * calculate the boundary and bit address of this element
       */
      bit = ds_info->info[ i].bit;

			if ( Mci_Get_Bit_Offset( ds) )
			{
				ds_info->info[ i].bit -= Mci_Get_Bit_Offset( ds);
				ds_info->info[ i].bit %= Mci_Get_Boundary( ds) * 8;
				ds_info->info[ i].bit += Mci_Get_Bit_Offset( ds);
			}
			else
				ds_info->info[ i].bit %= (Mci_Get_Boundary( ds) * 8)  ;

      /*
       * set the operation
       */
      ds_info->info[ i].operation = ( u16)( ds_info->info[ i].bit + CDE_B0_15);

      /*
       * adjust the address
       */

			if ( Mci_Get_Bit_Offset( ds) )
			{
				bit -= Mci_Get_Bit_Offset( ds);
		    ds_info->info[ i].address += ( u16)( bit / ((Mci_Get_Boundary( ds) * 8)));
				bit += Mci_Get_Bit_Offset( ds);
			}
			else
	      ds_info->info[ i].address += ( u16)( bit / ((Mci_Get_Boundary( ds) * 8)));

      /*
       * adjust the length of the dataset
			 * calculate the new end address, and check if it exceeds the actual length
       */
      if ( !( ds_info->info[ i].bit % (Mci_Get_Boundary( ds) * 8) ) )
        ds_info->ct_len += 1;
    }
    else
    {

      /*
       * check if the address of this element has to be adjusted
       */
      if (ds_info->info[ i].fix)
      {

        size = Mci_Get_CDE_Size( ds_info->info[ i].operation);

        if( size != -1 && Mci_Get_Boundary( ds) < size)
        {

          /* adjust the address of this element */

          factor = size / Mci_Get_Boundary( ds);

          ds_info->info[ i].address =
            ( u16)( ds_info->info[ i].base + ( (ds_info->info[ i].address - ds_info->info[ i].base) * factor));
            /* ( u16)( ds_info->ct_start + ( (ds_info->info[ i].address - ds_info->ct_start) * factor)); */

          /* check if the size of the dataset length also has to be adjusted */

          if ((ushort)( ds_info->ct_start + ds_info->ct_len)
              <
              (ushort)( ds_info->info[ i].address + ( factor -1))
             )
          {


            ds_info->ct_len =  ( u16)( (( ds_info->info[ i].address + ( factor -1)) - ds_info->ct_start) +1);
          }
        }
      }
    }

    /* get max address index */
    if (ds_info->info[ i].address > ds_info->info[ max].address)
    {

      /* find a value for the size */
      switch (ds_info->info[ i].conv)
      {

        case C_MSG:
        case C_TIM0:
        case C_TIM1:
        case C_TIM2:
        case C_TIM3:
        case C_TIM4:
        case C_TIM5:
        case C_TIM6:
        case C_TIM7:
        case C_TIM8:
        case C_TIM9:

          size_i = ds_info->info[ i].bit;

          if (size_i % Mci_Get_Boundary( ds))
            size_i = (size_i / Mci_Get_Boundary( ds)) * Mci_Get_Boundary( ds) + Mci_Get_Boundary( ds);
          break;

        default:
          size_i = Mci_Get_CDE_Size( ds_info->info[ i].operation);
          break;
      }

      /* get the new max address */
      if ((ds_info->info[ i].address + size_i) > (ds_info->info[ max].address + size_max))
      {

        max = i;
        size_max = size_i;
      }
    }
  }

  /* get the last element */
  i = max;

  /* find a value for the size */
  switch (ds_info->info[ i].conv)
  {

    case C_MSG:
    case C_TIM0:
    case C_TIM1:
    case C_TIM2:
    case C_TIM3:
    case C_TIM4:
    case C_TIM5:
    case C_TIM6:
    case C_TIM7:
    case C_TIM8:
    case C_TIM9:

      size = ds_info->info[ i].bit;

      if (size % Mci_Get_Boundary( ds))
        size = (size / Mci_Get_Boundary( ds)) * Mci_Get_Boundary( ds) + Mci_Get_Boundary( ds);

      if( Mci_Get_Boundary( ds) != MCI_BIT_BND)
        factor = size / Mci_Get_Boundary( ds);
      else
        factor = 0;
      break;

    default:
      size = Mci_Get_CDE_Size( ds_info->info[ i].operation);

      if( Mci_Get_Boundary( ds) != MCI_BIT_BND)
        factor = Mci_Get_CDE_Size( ds_info->info[ i].operation) / Mci_Get_Boundary( ds);
      else
        factor = 0;
      break;
  }


  /*
   * check if the size of the dataset has to be adjusted at the end 
   */
  if( ( size != -1) && ( factor > 1) &&
      ( ( ushort)( ds_info->ct_start + ds_info->ct_len -1) < ( ushort)( ds_info->info[ i].address + ( factor -1))
        )
     ) {

    ds_info->ct_len += factor -1;
  }

  return GOOD;
}


/*-----------------------------------------------------------------------------
 *
 * FUNCTION:	VAL *tag_type_xlate( void *s, u16 type, VAL *v, u16 new_type)
 *
 * PURPOSE :	Translation from value (s) of specified tag type to type needed
 *            for conversion, the new 'tag type'.
 *            Type according to conversion is returned in v and a pointer to
 *            this structure as function return value.
 *
 -----------------------------------------------------------------------------*/
VAL *tag_type_xlate( void *s, u16 type, VAL *v, u16 new_type)
{


  /* 
   * prepare message element for new type 
   */

  if (new_type == FL_MESSAGE)
  {

    v->msg.m_ptr = msg_global;
    v->msg.m_ptr[ 0] = '\0';
    v->msg.m_len = 0;
    v->msg.m_max = MAX_MSG;
  }

  /* actual conversion */

  fl_convert( v, (i16)new_type, s, (i16) type);

  /* 
   * prepare message element for new type 
   */

  if (new_type == FL_MESSAGE)
    v->msg.m_len = strlen( v->msg.m_ptr);

  return v;
}


/*-----------------------------------------------------------------------------
 *
 * FUNCTION:	Mci_String_Swap( char *s, int len);
 *
 * PURPOSE :	Swap bytes iver specified length starting at beginning of string.
 *
 -----------------------------------------------------------------------------*/
char *Mci_String_Swap( char *s, int len)
{

  int     i,
          end = len/2;
  char    swap;


  /* correct end index */
  if (len % 2)
    end++;

  /* correct length for last position in array */
  len--;

  /* reverse the byte order, for specified length */
  for (i = 0; i < end; i++)
  {

    swap = s[ i];
    s[ i] = s[ len - i];
    s[ len - i] = swap;
  }

  return s;
}

/*-----------------------------------------------------------------------------
 * FUNCTION:	int read_disable( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables reading of a dataset.
 *
 -----------------------------------------------------------------------------*/
int read_disable( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;
  int         restart = 0;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( v.dig)
    ds_info->read_disable = 1;
  else    
  {

    if (ds_info->read_disable)
      restart = 1;

    ds_info->read_disable = 0;
  }

	/*
	 * check if this is a continuous read and if so trigger the dataset
	 */
	if ((ds_info->continuous) && (restart))
  {

    v.dig = 1;
		block_read( (void *)ds, v);
	}

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int write_disable( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables writing of a dataset.
 *
 -----------------------------------------------------------------------------*/
int write_disable( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( v.dig)
    ds_info->write_disable = 1;
  else    
    ds_info->write_disable = 0;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int rcv_disable( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables writing of a dataset.
 *
 -----------------------------------------------------------------------------*/
int rcv_disable( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( v.dig)
    ds_info->rcv_disable = 1;
  else    
    ds_info->rcv_disable = 0;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int read_sync( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables read synchronisation of a dataset.
 *
 -----------------------------------------------------------------------------*/
int read_sync( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( !v.dig)
  {

    ds_info->read_sync = 0;
    Mci_Set_Active( 0);
  }
  else
    ds_info->read_sync = 1;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int write_sync( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables write synchronisation of a dataset.
 *
 -----------------------------------------------------------------------------*/
int write_sync( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( !v.dig)
  {

    ds_info->write_sync = 0;
    Mci_Set_Active( 0);
  }
  else
    ds_info->write_sync = 1;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	int rcv_sync( void *data, VAL v)
 *
 * PURPOSE :	This function enables/disables receive synchronisation of a dataset.
 *
 -----------------------------------------------------------------------------*/
int rcv_sync( void *data, VAL v)
{

	MCIDS				*ds = ( MCIDS *)data;
	DS_INFO			*ds_info;


	/* get the additional dataset information */

	ds_info = Mci_Get_Udata_Ptr( ds);

	/* check if this was a real trigger */

  if( !v.dig)
  {

    ds_info->rcv_sync = 0;
    Mci_Set_Active( 0);
  }
  else
    ds_info->rcv_sync = 1;

  return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	void ds_write_sync( MCIDS *ds, int function)
 *
 * PURPOSE :	Write the sync. after completion.
 *
 -----------------------------------------------------------------------------*/
void ds_write_sync( MCIDS *ds, int function)
{

	DS_INFO			*ds_info=Mci_Get_Udata_Ptr( ds);
  VAL         v;


 	if ((fl_gettype( &ds_info->sync[ function]) == FL_ANALOG) || (fl_gettype( &ds_info->sync[ function]) == FL_DIGITAL))
  {

    /* set the tag value */
    v.ana = function + 1;

    /* write the tag */
	  if (fl_write(  Task.id,
	                 &ds_info->sync[ function],
	                 1,
	                 ( void *)&v.ana) != GOOD)
			fl_status( &Task, "FL_WRITE", FLS_ERROR, "'sync'", Mci_Get_Name( ds), fl_error( Task.id));

    /* execute the event functions for this task */
    fl_exec_event( &Task, &ds_info->sync[ function]);
  }

  return;
}


/*-----------------------------------------------------------------------------
 * FUNCTION:	void ds_completion( MCIDS *ds, u16 error)
 *
 * PURPOSE :	Complete operation on a dataset.
 *            Status tag of dataset is updated
 *            Completion trigger is forced to ON.
 *
 -----------------------------------------------------------------------------*/
void ds_completion( MCIDS *ds, int function, u16 error)
{

	DS_INFO			*ds_info=Mci_Get_Udata_Ptr( ds);
  VAL         v;


 	if (fl_gettype( &ds_info->status) == FL_ANALOG) 
  {

    v.ana = error;

	  if (fl_write(  Task.id,
	                 &ds_info->status,
	                 1,
	                 ( void *)&v.ana) != GOOD)
			fl_status( &Task, "FL_WRITE", FLS_ERROR, "'status'", Mci_Get_Name( ds), fl_error( Task.id));
  }

  /* update sync tags */
  ds_write_sync( ds, function);

	/*
	 * set the completion trigger of the receive function
	 */
	if (fl_gettype( &ds_info->ready[ function]) == FL_DIGITAL)
  {

		v.dig = 1;

	  if( fl_forced_write( Task.id,
	                       &ds_info->ready[ function],
	                       1,
	                       ( void *)&v.dig) != GOOD)
	    fl_status( &Task, "FL_F_WRITE", FLS_ERROR, ready_txt[ function], Mci_Get_Name( ds), fl_error( Task.id));
	}

  return;
}


