/*
 ******************************************************************************
 *  Copyright 1994 DeltaLink bv. All Rights Reserved.
 ******************************************************************************
 *
 * DeltaLink Protocol Driver Task
 *
 * File: m_pluspd.c
 *
 * This file contains general Protocol Driver FactoryLink task.
 *
 */
#ifdef NT
#include <windef.h>
#endif

#include  <stdlib.h>
#include  <string.h>
#include  <stdio.h>
#include  <stdarg.h>
#include  <time.h>

#if !defined( OS2_32) && !defined( UNIX)
#include  <dos.h>

#ifdef _MSC_VER
#include  <direct.h>
#else
#include  <dir.h>
#endif
#endif

#include  <../../version.h>

#include  <netbios.h>             /* Communication definitions */

#include  <flib.h>                /* FactoryLink definitions */

#include  <fl_utils.h>            /* Protocol Driver protection */
#include  <modules.h>              /* Unique DeltaLink modules */

#include  <mbp.h>                 /* Communication definitions */
#include  <mbcom.h>                  /* Communication definitions */

#define MCI_OLD
#include  <mci.h>                  /* Mailbox Communication Interface definitions */
#include  <format.h>              /* Mailbox Communication Interface definitions */

#include  <m_plus.h>              /* Protocol Driver definitions */


#define  XBUFSIZE  1024            /* fl_xlate buffer size */

/*
 * Task global variables
 */
char nr_jobs;
int  e_x_option = OFF;                   /* Compl. trigger for enc. and exc. writes */
int  global = OFF;                       /* continuous global receive */

/*
 * Task global variables
 */
TASK_ID   task;

/*
 * the 'pd_ds' element is an array which contains the Rx Mailbox and all
 * MSG Control TAGs of all DataSets. The first element will always be the Rx Mailbox
 * TAG. The indexes and pointers into the array will be used in the Device Object.
 *
 * the 'ds_mbx' element  contains an index in the  Decoder Mailbox array. Every
 * DataSet is linked to a Decoder Mailbox which is used to write to
 *
 */
TAG  *ds_ctrl;
int  nr_ds;             /* number of DsCtrl, DsMbx and Rx Mailbox TAGS */

/*
 * device specific variables
 */
DEVICE  *dev;               /* array of used devices */
uint    cur;                /* current device for processing */

uint    nr_devices;         /* the number of used devices */

DC      *decoders;          /* array of decoders */
uint    nr_decoders;        /* number of decoders */

SYSTEM  sys;

uint    mci_max_msg;        /* maximum number of messages in the PD queue */
uint    nr_msg_active;      /* current number of messages in the queue */

char    *comfnc[ COM_MAX_FNC] = { "READ",
                                  "WRITE",
                                  "RECEIVE",
                                  "GLOBAL"
                                };

char    *comtype[ NR_MBP_COMMANDS ] =
                  { "connecting",
                    "disconnecting",
                    "listening",
                    "accepting",
                    "reading",
                    "writing",
                    "receiving",
                    "exception",
                    "cancel",
                    "global"
                  };

char switching;           /* variable for switching channels */

/* function prototypes of this module */

void       process              ( void);

int        init_mci_queue       ( void);
void       pd_connect              ( void);
void       connect_fnc          ( DEVICE *, char);
void       pd_disconnect           ( void);

void       chk_tags             ( void);
void       proc_devices         ( void);
int        initiate_rw          ( char);
int        initiate_rcv         ( char);
int        initiate_global      ( char);
void       complete             ( char);
void       complete_error       ( DATAOBJ *, char, ushort);
DATAOBJ *  srch_dsobj           ( TAG *, DEVICE **);
int        remove_jobs          ( DEVICE *, char);
int        pd_error             ( DEVICE *, char);

int        evaluate_write       ( MCIMSG *, JOB *);

/* array of functions pointer for initiating communication commands */

int ( *initiate[ COM_MAX_FNC])( char) = { initiate_rw,
                                          initiate_rw,
                                          initiate_rcv,
                                          initiate_global
                                        };

/*-----------------------------------------------------------------------------
 * 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, "MB_PLUS");
  strcpy( task.desc, "DeltaLink Modbus Plus Protocol Driver");

  task.dl_id    = MODBUS_P;
  task.version  = ( (rmj << 8) & 0xFF00) | ( rmm & 0x00FF);



  /* 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 */
      /*dl_exit_task( &task, 1);                         /* exit to OS */
    }
    else
      exit( 1);
  }


  /* Set up for message translation */
  
  fl_xlate_init( task.name, xlbuf, XBUFSIZE);

  #ifdef DEBUG
  printf("START of task %s: %s\n\n V%x running mode %x in %s domain, app_name %s user %s \n app_dir %s prg_dir %s\n",
         task.name,
         task.desc,
         task.version,
         task.mode,
         task.fl_domain,
         task.fl_name,
         task.fl_user,
         task.k.e_adir,
         task.k.e_pdir);
  #endif

  fl_status( &task, "START", FLS_STARTING);               /* indicate startup state */

  /*
   * Default: only compl. trigger on block writes.
   * Optional: compl. trigger on enc., exc. and block writes
   *           specify the command line parameter: -w
   */

  /* get the option "-g" for continuous global receive */

  if ( strstr( task.k.e_cmd, "-g") != NULL) {

    nr_jobs = COM_MAX_FNC;
    global = ON;
  }
  else {

    nr_jobs = COM_MAX_FNC -1;
  }

  /* get the option "-w" */

  if ( strstr( task.k.e_cmd, "-w") != NULL) e_x_option = ON;


  /* initialize the Mailbox Communication Interface (MCI) */

  if( ( ret = Mci_Init( task.id)) != GOOD) {

    fl_status( &task, "MCI_INIT", FLS_INACTIVE, ret);    /* indicate normal stop */
    dl_exit_task( &task, 1);                            /* exit to OS */
  }

  ctload();                                             /* load task configuration */

  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 */

  /*
   * empty the total PD mailbox except for INFO messages
   */
  if( ( ret = init_mci_queue()) != GOOD) {

    fl_status( &task, "MCI_INIT_QUEUE", FLS_INACTIVE, ret);  /* indicate normal stop */
    dl_exit_task( &task, 1);                                /* exit to OS */
  }

  /*
   * try to set up a virtual connectection to the remote systems  
   */
  pd_connect();

  /*
   * Continuously loop, processing all input until
   * a terminate message has been sent to this task.
   */
  while( dl_test_term_flag( &task) == OFF)
    process();                                              /* perform processing */


  fl_status( &task, "CLOSE", FLS_ACTIVE);                   /* indicate normal stop */

  /*
   * disconnect all active connections  
   */
  pd_disconnect();

  /*
   * exit the communication layer
   */
  Com_Exit();

  /*
   * exit the task
   */
  dl_exit_task( &task, 0);                           /* exit to OS */

  return 0;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void  process(void)
 *
 * PURPOSE :
 *
 * process is where the main processing takes place.
 *
 * the process consists of:
 *
 * 1. checking the FactoryLink Database elements on change: the
 *    Rx mailbox TAG and DataSet Control TAGs.
 *    On change mark the DataSet Object for processing.
 *
 * 2. Looping through the DataSet Object arrays for every Device and processing these
 *    objects.
 *
 * Every cycle of the program one Device will be processed which has something to do. All
 * functionalities of a Device (Read/Write/Receive) will be checked in one cycle.
 *
 -----------------------------------------------------------------------------*/
void process(void) {


  /* first sleep to give control to the FactoryLink Kernel */

  fl_sleep( 1);


  /* check the TAGs on change and mark DataSet Objects */

  chk_tags();

  /* process one device (READ, WRITE and RECEIVE) */

  proc_devices();

  return;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int init_mci_queue( void)
 *
 * PUPRPOSE:
 *
 * empties the PD queue from messages except for INFO messages 
 -----------------------------------------------------------------------------*/
int init_mci_queue( void) {

  int    ret;
  uint   i;
  uint   nr_msg;           /* number of messages in the receive mailbox */
  MCIMSG msg;
  MCIHDR hdr;
  
  /*
   * reset first the msg structure
   */
  memset( &msg, 0x00, sizeof( MCIMSG));
  
  /*
   * set first the PD mailbox and index on the temporary message
   */
  Mci_Set_Rcv_Mbx( &msg, ds_ctrl[ PD_MBX]);
  Mci_Set_Index( &msg, 0);

  Mci_Set_Buffer( &msg, &hdr);
  Mci_Set_BufLen( &msg, sizeof( MCIHDR));

  /*
   * determine first the number of messages in the queue
   */
  if( Mci_Nr_Msg( task.id, ds_ctrl[ PD_MBX], &nr_msg) != GOOD) {

    fl_status( &task, "MCI_NR_MSG", FLS_ERROR, "PD init mailbox");
    return -1;
  }
  
  for( i = 0; i < nr_msg; i++) {
    /*
     * query the message first
     */
    if( ( ret = Mci_Query_Msg( task.id, &msg)) != GOOD) {

      fl_status( &task, "FL_QUERY_MBX", FLS_ERROR, "PD init mailbox", ret);
      continue;
    }

    /*
     * check the type of the message
     */
    if( Mci_Get_Type( &msg) == MCI_QUERY_CMD) {

      /*
       * this message remains in the queue so increment the index
       */
      Mci_Set_Index( &msg, Mci_Get_Index( &msg) +1);

      continue;
    }

    /*
     * remove this message from the queue
     */ 
    if( ( ret = Mci_Recv( task.id, &msg)) != GOOD)
      fl_status( &task, "MCI_RCV", FLS_ERROR, ret);
  }

  return 0;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: void  pd_connect( void)
 *
 * PUPRPOSE:
 *
 * tries to connect every device with remote system 
 -----------------------------------------------------------------------------*/
void pd_connect( void) {

  uint i;
  i16  info;
  DIG  val = 0;

  /* process every device for connecting with remote device */

  for( i = 0; i < nr_devices; i++) {

    /* try to connect the READ functionality */

    connect_fnc( &dev[ i], COM_READ);

    /* try to connect the WRITE functionality */

    connect_fnc( &dev[ i], COM_WRITE);


    /* get information about the RECEIVE disable tag */

    fl_get_tag_info( &dev[ i].dis_fnc[ COM_RCV], 1, ( i16 *)&info, ( u16 *)0);

    /* read the values of the disable tags */

    if( info == FL_DIGITAL) {

      if( fl_read( task.id,
                   &dev[ i].dis_fnc[ COM_RCV],
                   1,
                   &val) != GOOD)
        fl_status( &task, "FLREAD", FLS_ERROR, "disable tag receive", dev[ i].phys_dev.name, fl_error( task.id));
    }

    /* set out a job for accepting incoming connection requests */

    if( !val) {

      #ifndef NORCV

      if( dev[ i].phys_dev.ds) {

        if( Com_Listen( &dev[ i].job[ COM_RCV]) != 0)
          fl_status( &task, "COM_LISTEN", FLS_ERROR, dev[ i].job[ COM_RCV].error);
      }
      #endif
    }
  }

  return;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void  connect_fnc( void)
 *
 * PUPRPOSE:
 *
 * tries to connect every device with remote system 
 -----------------------------------------------------------------------------*/
void connect_fnc( DEVICE *d, char fnc) {

  int  ret;
  i16  info;
  ANA  pd_status;
  DIG  val = 0;

  /*
   * set the status of the PLC first to OK
   */
  pd_status = 0;
  fl_write( task.id, &d->status, 1, &pd_status);


  /* get information about the disable tags */

  fl_get_tag_info( &d->dis_fnc[ fnc], 1, ( i16 *)&info, ( u16 *)0);

  /* read the values of the disable tags */

  if( info == FL_DIGITAL) {

    if( fl_read( task.id,
                 &d->dis_fnc[ fnc],
                 1,
                 &val) != GOOD)
      fl_status( &task, "FLREAD", FLS_ERROR, "disable tag", d->phys_dev.name, fl_error( task.id));
  }

  /* try to connect the READ functionality */

  if( !val) {

    /*
     * check first if this connection must be maintained dynamically
     */
    if( Com_Conn_Dynamic( &d->phys_dev, fnc)) {

      /* set the state of this job to READY to keep checking commands */

      Com_Set_Job_State( &d->job[ fnc], JOB_READY);
    }
    else {

      if( ( ret = Com_Connect( &d->job[ fnc])) != 0)
        fl_status( &task, "COM_CONNECT", FLS_ERROR, d->phys_dev.name, ret);

    }
  }
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void  pd_disconnect( void)
 *
 * PUPRPOSE:
 *
 * tries to connect every device with remote system 
 -----------------------------------------------------------------------------*/
void pd_disconnect( void) {

  int  ret;
  uint i;
  ANA  val = 0;

  /*
   * close every connection on every job
   */
  for( i = 0; i < nr_devices; i++) {

    /* disconnect from READ functionality */

    if( Com_Connection( &dev[ i].phys_dev, COM_READ)) {

      /* disconnect from READ functionality */

      if( ( ret = Com_Disconnect( &dev[ i].job[ COM_READ])) != 0)
        fl_status( &task, "COM_DIS", FLS_ERROR, "READ", dev[ i].phys_dev.name, ret);
    }

    /* disconnect from WRITE functionality */

    if( Com_Connection( &dev[ i].phys_dev, COM_WRITE)) {

      if( ( ret = Com_Disconnect( &dev[ i].job[ COM_WRITE])) != 0)
        fl_status( &task, "COM_DIS", FLS_ERROR, "RCV", dev[ i].phys_dev.name, ret);
    }

    /* disconnect from receive functionality */

    if( Com_Connection( &dev[ i].phys_dev, COM_RCV)) {

      /* wait for the receive to complete */

      if( ( ret = Com_Disconnect( &dev[ i].job[ COM_RCV])) != 0)
        fl_status( &task, "COM_DIS", FLS_ERROR, "RCV direct", dev[ i].phys_dev.name, ret);
    }

    /*
     * set the status of the PLC to error
     */
    val = PD_CLOSED;
    fl_write( task.id, &dev[ i].status, 1, &val);

  }
  return;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void  chk_tags( void)
 *
 * PUPRPOSE:
 *
 * checks the TAG array and marks the DataSet Objects for processing 
 -----------------------------------------------------------------------------*/
void chk_tags( void) {

  uint    nr_msg;           /* number of messages in the receive mailbox */
  int     count = 0;
  int     ret;
  uint    i = 0,            /* start with first tag */
          index,            /* index in mailbox queue */
          j;
  TAG     snd;              /* send to the mailbox received */
  MCIMSG  val;              /* dummy variable to receive changed value */
  VAL     dummy;
  DATAOBJ *obj;             /* pointer to the DataObject containing DataSet information */
  MCIHDR  info;             /* buffer to hold info message from decoder */
  DEVICE  *act_dev;         /* pointer to device */


  UNDEF_TAG( &snd);

  /*
   * check if a control tag has changed. Always start with index 0 with the fl_change_read
   * because otherwise it generates an error
   */
  while( fl_change_read( task.id,
                         ds_ctrl + count,
                         nr_ds - count,
                         &i,
                         &dummy) == GOOD) {

    /* adjust the index in the tag array and reset the start index */

    count += i;
    i = 0;

    /* check which functionality triggered */

    if( count == PD_MBX) {  /* Write function triggered, Protocol Driver Mailbox changed */

      /* read the message data of the last message */

      if( Mci_Nr_Msg( task.id, ds_ctrl[ PD_MBX], &nr_msg) != GOOD) {

        fl_status( &task, "MCI_NR_MSG", FLS_ERROR, "PD mailbox");
        return;
      }

      /* initialise the index in the queue */

      index = nr_msg_active;

      /* process all messages that came in */

      for( j = nr_msg_active; j < nr_msg; j++) { 

        Mci_Set_Buffer( &val, &info);
        Mci_Set_BufLen( &val, sizeof( MCIHDR));

        Mci_Set_Rcv_Mbx( &val, ds_ctrl[ PD_MBX]);        /* set the receive mailbox tag */
        Mci_Set_Snd_Mbx( &val, snd);                     /* use mbx received */
        Mci_Set_Index( &val, index);                     /* set the index in the queue of message */

        if( ( ret = Mci_Query_Msg( task.id, &val)) != GOOD) {
                         
          fl_status( &task, "FL_QUERY_MBX", FLS_ERROR, "PD mailbox", ret);
          continue;
        }

        /*
         * check first if the identification of dataset is on TAGs 
         */
        if( Mci_Get_Id( &val) != MCI_TAG) {

          fl_status( &task, "MCI_ID", FLS_ERROR, Mci_Get_Id( &val));
          continue;
        }

        /* search for the DataSet Object */

        if( ( obj = srch_dsobj( Mci_Get_MsgCtrl( &val), &act_dev)) == NULL) {

          fl_status( &task, "SRCH_DSOBJ", FLS_ACTIVE, "Write from DC", *Mci_Get_MsgCtrl( &val));

          /*
           * remove message from queue and continue
           */
          if( ( ret = Mci_Recv( task.id, &val)) != GOOD)
            fl_status( &task, "MCI_RCV", FLS_ERROR, ret);

          continue;
        }

        /* check if a request to write or request for info message came in */

        switch( Mci_Get_Type( &val)) {

        case MCI_QUERY_CMD:

          /* read the info message and reply */

          if( ( ret = Mci_Recv( task.id, &val)) != GOOD) {

            /*
             * it could be possible that a message is underway when starting up
             */
            if( ret != FLE_NO_MESSAGES)
              fl_status( &task, "MCI_RCV", FLS_ERROR, ret);

            continue;
          }

          Mci_Set_Type        ( &val, MCI_QUERY_RSP);
          Mci_Set_Version     ( &val, MCI_VERSION);

          Mci_Set_Start       ( &val, obj->dataset.start);
          Mci_Set_Len         ( &val, obj->dataset.len);

          Mci_Set_Bit_Dir     ( &val, obj->dataset.bit_direct);
          Mci_Set_Bound_Dir   ( &val, obj->dataset.direction);
          Mci_Set_Bit_Start   ( &val, obj->dataset.bit_offset);
          Mci_Set_Bound_Start ( &val, obj->dataset.data_offset);

          Mci_Set_Boundary    ( &val, obj->dataset.boundary);

          Mci_Set_CDE         ( &val, 0);

          Mci_Set_Snd_Mbx     ( &val, decoders[ obj->dc_index ].mbx);

          /* send the information packet to the Decoder */

          if( ( ret = Mci_Send( task.id, &val)) != GOOD)
            fl_status( &task, "MCI_SEND", FLS_ERROR, ret);

          continue;

        case MCI_READ_REQ:

          /* Show wich job is activated for logging and/or debugging */

          debug_log( &task, "Buffered read: DS %d:%d start %d length %d", 2,
                    (( TAG *)Mci_Get_MsgCtrl( &val))->t_type,
                    (( TAG *)Mci_Get_MsgCtrl( &val))->t_data,
                    obj->dataset.start,
                    obj->dataset.len);

          /*
           * remove the READ request from the queue
           */

          Mci_Set_Index( &obj->mci[ COM_READ], index);

          if( ( ret = Mci_Recv( task.id, &obj->mci[ COM_READ])) != GOOD)
            fl_status( &task, "MCI_RCV", FLS_ERROR, ret);

          /*
           * in case error on device, discard the incoming message
           */
          if( Com_Get_Dev_Error( &act_dev->phys_dev, COM_READ)) {

            /* send an error message to the decoder */

            complete_error( obj, COM_READ, Com_Get_Dev_Error( &act_dev->phys_dev, COM_READ));

            break;
          }

          /* mark the WRITE function to be processed of this dataset */

          obj->process[ COM_READ]   = 1;
          obj->triggered[ COM_READ] = 1;

          break;
        case MCI_WRITE_REQ:

          /* Show wich job is activated for logging and/or debugging */

          debug_log( &task, "Buffered write: DS %d:%d start %d length %d", 2,
                     (( TAG *)Mci_Get_MsgCtrl( &val))->t_type,
                     (( TAG *)Mci_Get_MsgCtrl( &val))->t_data,
                     obj->dataset.start,
                     obj->dataset.len);

          /*
           * in case error on device, discard the incoming message
           */
          if( Com_Get_Dev_Error( &act_dev->phys_dev, COM_WRITE)) {

            /* set first the index on the MCI object before reading*/

            Mci_Set_Index( &obj->mci[ COM_WRITE], index);

            if( ( ret = Mci_Recv( task.id, &obj->mci[ COM_WRITE])) != GOOD)
              fl_status( &task, "MCI_RCV", FLS_ERROR, ret);

            /* send an error message to the decoder */

            complete_error( obj, COM_WRITE, Com_Get_Dev_Error( &act_dev->phys_dev, COM_WRITE));

            break;
          }

          obj->nr_rxmsg++;

          index++;                          /* Select next message in the queue */
          
          /* mark the WRITE function to be processed of this dataset */

          obj->process[ COM_WRITE]   = 1;
          obj->triggered[ COM_WRITE] = 1;

          /* update the number of message in the queue at the moment */

          nr_msg_active++;
          break;

        default:

          /* Unknown type of message */      
          fl_status( &task, "MCI_UNKNOWN_TYPE", FLS_ERROR, Mci_Get_Type( &val));

          /*
           * remove message from queue and continue
           */
          if( ( ret = Mci_Recv( task.id, &val)) != GOOD)
            fl_status( &task, "MCI_RCV", FLS_ERROR, ret);
          break;
        }
      }
    }
    else {        /* Read function triggered */

      /* search for the DataSet Object and mark a READ */

      if( ( obj = srch_dsobj( &ds_ctrl[ count], &act_dev)) == NULL) {

        fl_status( &task, "SRCH_DSOBJ", FLS_ACTIVE, "Read from DC", ds_ctrl[ i]);
      }
      else {

        if( !dummy.dig) continue;

        /* Show wich job is activated for logging and/or debugging */

        debug_log( &task, "Buffered read (tag): DS %d:%d start %d length %d", 2,
                   ds_ctrl[ count].t_type,
                   ds_ctrl[ count].t_data,
                   obj->dataset.start,
                   obj->dataset.len);

        /*
         * in case error on device, discard the incoming message
         */
        if( !Com_Get_Dev_Error( &act_dev->phys_dev, COM_READ)) {

          /* mark the DataSet Object for reading */

          obj->process[ COM_READ]   = 1;
          obj->triggered[ COM_READ] = 1;
        }
        else {

          /* send an error message to the decoder */

          complete_error( obj, COM_READ, Com_Get_Dev_Error( &act_dev->phys_dev, COM_READ));
        }
      }
    }

    count++;

    if( count == nr_ds)
      break;
  }

  /* check if the change read generated an error */

  if( (count != nr_ds) && ( ret = fl_errno( task.id)) != FLE_NO_CHANGE)
    fl_status( &task, "FL_CHANGE_READ", FLS_ERROR, "", "DataSet Ctrl TAGS", ret);

  return;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: proc_devices()
 *
 * PUPRPOSE:
 *
 * processes all DataObjects for every device and functionality.
 *
 -----------------------------------------------------------------------------*/
void proc_devices( void) {

  uint no_sleep = nr_devices;
  int  ret;
  char i;           /* indicates the function processed: Read, Write and Receive */


  /* do the processing until a sleep is wanted */

  while( no_sleep) {

    /* process every functionality of the current device */

    for( i = 0; i < nr_jobs; i++) {

      /* check first the jobs on the current device */

      switch( Com_Get_Job_State( &dev[ cur].job[ i])) {

      case JOB_READY:

        /* inititiate a new JOB for the specific functionality */

        switch( initiate[ i]( i))
        {
        case GOOD:

          /* if there is no job to be done next complete should be avoided */
          if( Com_Get_Job_State( &dev[ cur].job[ i]) == JOB_READY)
          {

            /* do the handling of the command completion here */

            complete( i);

            no_sleep = 1;    /* go to sleep after this device */
          }
          break;

        case INACTIVE:
          break;

        default:

          pd_error( &dev[ cur], i);
          break;
        }
/*
        if( initiate[ i]( i) != GOOD)  {

          pd_error( &dev[ cur], i);
          break;
        }
        else {
*/
          /* if there is no job to be done next complete should be avoided */
/*          if( Com_Get_Job_State( &dev[ cur].job[ i]) == JOB_READY) {
*/
            /* do the handling of the command completion here */
/*
            complete( i);
          }

          no_sleep = 1;  */  /* go to sleep after this device */
/*        }
*/
      break;

      case JOB_PENDING:

        /* check the outstanding communication command */

        if( Com_Check( &dev[ cur].job[ i]) != JOB_READY)
          break;

        /* check first if there is an error on the job */

        if( !Com_Get_Job_Error( &dev[ cur].job[ i])) {

          /* do the handling of the command completion here */

          complete( i);
        }

       no_sleep = 1;    /* go to sleep after this device */

      break;

      case JOB_WAITING:
        /*
         * A Job becomes this state when it has to wait on an event before it can be
         * executed such as: device is busy with another Job. The Job is already constructed
         * after completion of the previous Job.
         */
        /* try to set out the command for this function */

        if( Com_Get_Function( &dev[ cur].job[ i]) == COM_WRITE) {
          /*
           * exception WRITE on this job pending ?
           */
          if( Com_Get_Type( &dev[ cur].job[ i]) == COM_EXC_WRITE_TYPE) {
            /*
             * Check if the READ has completed yet.
             */

            if( !dev[ cur].ds_obj[ dev[ cur].job_ind[ COM_WRITE]].exc_reading) {
              /*
               * the READ has completed. Patch the value
               */

              Com_Exc_Patch( &dev[ cur].job[ COM_WRITE]);

              /*
               * start the exception write
               */
              Com_Write( &dev[ cur].job[ i]);

              /* do the handling of the command completion here */

              if( Com_Get_Job_State( &dev[ cur].job[ i]) == JOB_READY)
                complete( i);
            }
          }
          else {

            /*
             * this write command is waiting on an event to happen in the
             * communication layer. Try again to set out the command.
             */
            Com_Write( &dev[ cur].job[ i]);

              /* do the handling of the command completion here */

              if( Com_Get_Job_State( &dev[ cur].job[ i]) == JOB_READY)
                complete( i);
          }
        }
        else if( Com_Get_Function( &dev[ cur].job[ i]) == COM_READ) {

          /*
           * this write command is waiting on an event to happen in the
           * communication layer. Try again to set out the command.
           */
          Com_Read( &dev[ cur].job[ i]);

            /* do the handling of the command completion here */

            if( Com_Get_Job_State( &dev[ cur].job[ i]) == JOB_READY)
              complete( i);
        }

      break;

      case JOB_IDLE:
      case JOB_ERROR:              /* job error, try to remove the error */

        /* check if there is an error on this connection */

        if( Com_Get_Job_Error( &dev[ cur].job[ i]))
          pd_error( &dev[ cur], i);

      break;

      default:
        fl_status( &task, "PD_UNKNOWN_STATE", FLS_ERROR, dev[ cur].phys_dev.name);
      break;
      }

      /*
       * check the switching path tag
       */
      if( i > COM_WRITE) continue;

      if( !switching) {

        if( fl_get_tag_info( &dev[ cur].alternate, 1, ( i16 *)0, ( u16 *)0) == GOOD) {

           u16  val = 0;          /* TAG information */
           uint index = 0;

           if( fl_change_read( task.id,
                               &dev[ cur].alternate,
                               1,
                               &index,
                               ( u16 *)&val) == GOOD) {


            /*
             * update the current active path
             */
            dev[ cur].phys_dev.path_nr = val;
            switching = 1;

            if( Com_Get_Job_State( &dev[ cur].job[ i]) != JOB_PENDING &&
                Com_Get_Job_State( &dev[ cur].job[ i]) != JOB_WAITING
              ) {

              Com_Set_Job_State( &dev[ cur].job[ i], JOB_IDLE);
            }
          }
          else {

            if( fl_errno( task.id) != FLE_NO_CHANGE)
              fl_status( &task, "FL_CHANGE_READ", FLS_ERROR, "switch path", dev[ cur].phys_dev.name, fl_error( task.id));
          }
        }
      }
      else {

        if( Com_Get_Job_State( &dev[ cur].job[ i]) != JOB_PENDING &&
            Com_Get_Job_State( &dev[ cur].job[ i]) != JOB_WAITING
          ) {

          Com_Set_Job_State( &dev[ cur].job[ i], JOB_IDLE);
        }

        if( Com_Get_Job_State( &dev[ cur].job[ COM_READ]) == JOB_IDLE &&
            Com_Get_Job_State( &dev[ cur].job[ COM_WRITE]) == JOB_IDLE) {

          /*
           * close the previous path and switch to new one
           */
          if( ( ret = Com_Disconnect( &dev[ cur].job[ i])) != 0)
            fl_status( &task, "COM_DIS", FLS_ERROR, "READ", dev[ cur].phys_dev.name, ret);

          connect_fnc( &dev[ cur], i);

          Com_Set_Job_State( &dev[ cur].job[ COM_READ], JOB_READY);
          Com_Set_Job_State( &dev[ cur].job[ COM_WRITE], JOB_READY);

          switching = 0;
        }
      }
    }

    /* check the status of this device */

    if( dev[ cur].error[ COM_READ] ||
        dev[ cur].error[ COM_WRITE] ||
        dev[ cur].error[ COM_RCV] ||
        dev[ cur].error[ COM_GLOBAL] 
      ) {

      /* reset the error flag of this device */

      if( !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_READ))
        dev[ cur].error[ COM_READ] = 0;

      if( !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_WRITE))
        dev[ cur].error[ COM_WRITE] = 0;

      if( !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_RCV))
        dev[ cur].error[ COM_RCV] = 0;

      if( !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_GLOBAL))
        dev[ cur].error[ COM_GLOBAL] = 0;

      /* check if the error has been resolved on the device */

      if( !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_READ) &&
          !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_WRITE) &&
          !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_RCV) &&
          !Com_Get_Dev_Error( &dev[ cur].phys_dev, COM_GLOBAL)
        ) {

        i16 val = 0;

        if( fl_get_tag_info( &dev[ cur].status, 1, ( i16 *)0, (u16 *)0) == GOOD)
          fl_write( task.id,
                    &dev[ cur].status,
                    1,
                    &val);

        fl_status( &task, "RUN", FLS_ACTIVE);
      }
    }
    /* go to the next device */

    if( cur == nr_devices -1)
      cur = 0;              /* wrap around to first device */
    else
      cur++;                /* next device */

    /* when every device processed go always to sleep */

    no_sleep--;
  }

  return;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int initiate_rw( JOB *)
 *
 * PUPRPOSE:
 *
 * scans the DataSet Objects for new jobs for the read and write functionality
 *
 -----------------------------------------------------------------------------*/
int initiate_rw( char fnc) {

  DIG    disable = 0;
  i16    t_info,
         c600_index;
  uint   j;
  int    ret = 0,
         start,
         len;

  /* search for new DataSet Object to be processed for this function */

  for( j = 0; j < dev[ cur].nr_data; j++) {

    /*
     * loop through total number of DataSets
     */
    if( dev[ cur].job_ind[ fnc] == dev[ cur].nr_data -1)
      dev[ cur].job_ind[ fnc] = 0;               /* wrap around in DataSet Object array */
    else
      dev[ cur].job_ind[ fnc]++;                 /* next DataSet Object */

    /* check if DataSet Object has to be processed */

    c600_index = dev[ cur].job_ind[ fnc];

    if( dev[ cur].ds_obj[ c600_index].process[ fnc]) {

      /* check first if functionality still enabled */

      if( !( ( fnc == COM_READ) && dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].exc_reading)) {

        if( fl_get_tag_info( &dev[ cur].dis_fnc[ fnc], 1, &t_info, NULL) == GOOD) {

          if( fl_read( task.id, &dev[ cur].dis_fnc[ fnc], 1, &disable) != GOOD)
            fl_status( &task, "FL_READ", FLS_ERROR, dev[ cur].phys_dev.name, fl_error( task.id));
        }
        else {

          if( t_info != FL_UNDEFINED)
            fl_status( &task, "FL_GET_TAG_INFO", FLS_ERROR, "disable tag", dev[ cur].phys_dev.name,
                      fl_error( task.id));
        }
      }

      /*
       * check if this connection is maintained dynamically. If so maybe a
       * connection must be set up first
       */
      if( !disable && Com_Conn_Dynamic( dev[ cur].job[ fnc].dev, fnc)) {

        /* check if the connection is already established */

        if( !Com_Connection( dev[ cur].job[ fnc].dev, fnc)) {

          /*
           * set out a connect command and don't reset the process flag. A connect command
           * with Modbus Plus will always execute synchronous.
           */
          if( ( ret = Com_Connect( &dev[ cur].job[ fnc])) != GOOD)
            return ret;
        }
      }

      /*
       * no dynamic connections so process normally but reset the process flag first
       */
      dev[ cur].ds_obj[ c600_index].process[ fnc] = 0;

      /* construct the communication job */

      if( fnc == COM_WRITE) {

        /* set the index in the Protocol Driver Mailbox queue */

        Mci_Set_Index( &dev[ cur].ds_obj[ c600_index].mci[ fnc], 0);

        if( ( ret = Mci_Srch_Msg( task.id,
                                  &dev[ cur].ds_obj[ c600_index].mci[ fnc])) != GOOD) {

          fl_status( &task, "MCI_SRCH", FLS_ERROR,
                     Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc])->t_type,
                     Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc])->t_data,
                     dev[ cur].phys_dev.name,
                     ret);

          /* update queue information */

          if( nr_msg_active >= dev[ cur].ds_obj[ c600_index].nr_rxmsg)
            nr_msg_active -= dev[ cur].ds_obj[ c600_index].nr_rxmsg;

          else nr_msg_active = 0;

          dev[ cur].ds_obj[ c600_index].nr_rxmsg = 0;

          continue;
        }

        /* read the message from the receive mailbox */
        /* update the number of message active in the queue */

        nr_msg_active--;

        /* decrement the number of message with this Control Tag in the PD Mailbox */

        dev[ cur].ds_obj[ c600_index].nr_rxmsg--;

        if( ( ret = Mci_Recv( task.id,
                              &dev[ cur].ds_obj[ c600_index].mci[ fnc])) != GOOD) {

          fl_status( &task, "MCI_RCV", FLS_ERROR, ret);
          continue;
        }

        /*
         * check on the number of message of this DS object in the queue
         */
        if( dev[ cur].ds_obj[ c600_index].nr_rxmsg) {

          /* mark this DataSet Object to be processed the next time */

          dev[ cur].ds_obj[ c600_index].process[ fnc] = 1;
        }

        /* check if the write function is still enabled */

        if( disable) continue;

        /* check what kind of MCI message received */

        if( Mci_Get_Handling( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) == MCI_NORMAL_WRITE) {

          switch( Mci_Get_CDE( &dev[ cur].ds_obj[ c600_index].mci[ fnc])) {

          default:         /* Exception Write function */
            /*
             * check first the MCI message received if it is possible to write it. The CDE
             * code against the MCI message itself and against the dataset will be checked.
             * This will be done by the following two functions.
             */
            if( ( ret = Mci_Adjust_Msg( &dev[ cur].ds_obj[ c600_index].mci[ fnc])) != 0) {

              fl_status( &task, "MCI_ADJUST", FLS_ERROR,
                         dev[ cur].phys_dev.name,
                         (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc]))->t_type,
                         (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc]))->t_data,
                         ret);
              continue;
            }

            /* copy the physical dataset to write to the job */

            memcpy( &dev[ cur].job[ COM_WRITE].dataset,
                    &dev[ cur].ds_obj[ c600_index].dataset,
                    sizeof( DS));

            /* check the minimalized MCI message against the dataset */

            if( ( ret = evaluate_write( &dev[ cur].ds_obj[ c600_index].mci[ fnc],
                                        &dev[ cur].job[ COM_WRITE])) != 0) {

              fl_status( &task, "PD_EVAL_WRITE", FLS_ERROR,
                    dev[ cur].phys_dev.name,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc]))->t_type,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ c600_index].mci[ fnc]))->t_data,
                    ret);
              continue;
            }

            /*
             * The mci message can be adjusted to a block write.
             */
            if( Mci_Get_CDE( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) != CDE_BLOCK) {

              /* set the type to exception write */

              Com_Set_Type      ( &dev[ cur].job[ COM_WRITE], COM_EXC_WRITE_TYPE);
              Com_Set_Ds_Start  ( &dev[ cur].job[ COM_WRITE],
                                  Mci_Get_Start( &dev[ cur].ds_obj[ c600_index].mci[ fnc]));
              Com_Set_Ds_Length ( &dev[ cur].job[ COM_WRITE],
                                  Mci_Get_Len( &dev[ cur].ds_obj[ c600_index].mci[ fnc]));
              Com_Set_Tx_Len    ( &dev[ cur].job[ COM_WRITE],
                                  Mci_Get_Len( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) *
                                  Mci_Get_Boundary( &dev[ cur].ds_obj[ c600_index].mci[ fnc]));

              /* set the exception value received from decoder */

              dev[ cur].job[ COM_WRITE].exc_operation = Mci_Get_CDE( &dev[ cur].ds_obj[ c600_index].mci[ fnc]);

              memcpy( &dev[ cur].job[ COM_WRITE].exc_value,
                      Mci_Get_Buffer( &dev[ cur].ds_obj[ c600_index].mci[ fnc]),
                      Mci_Get_Len( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) *
                      Mci_Get_Boundary( &dev[ cur].ds_obj[ c600_index].mci[ fnc])
                    );

              /*
               * check the start and length of the block to send
               */
              if( Com_Cmp_Ds( &dev[ cur].ds_obj[ c600_index].dataset,
                              &dev[ cur].job[ COM_WRITE].dataset,
                              &start,
                              &len) == -1) {

                fl_status( &task, "PD_W_RANGE", FLS_ERROR, dev[ cur].phys_dev.name,
                                      dev[ cur].job[ COM_WRITE].dataset.type,
                                      dev[ cur].job[ COM_WRITE].dataset.start,
                                      dev[ cur].job[ COM_WRITE].dataset.len);
                break;
              }

              /* mark the READ job to read the data of this dataset */

              dev[ cur].ds_obj[ c600_index].process[ COM_READ] = 1;

              /* mark the READ job that it is busy with FECTH for a WRITE */

              dev[ cur].ds_obj[ dev[ cur].job_ind[ COM_WRITE]].exc_reading = 1;

              /* set the state of the WRITE job to waiting on READ */

              Com_Set_Job_State( &dev[ cur].job[ COM_WRITE], JOB_WAITING);

              return 0;
            }

          case CDE_BLOCK:         /* Block Write function */

            /*
             * copy the DataSet in the current Job
             */
            memcpy( &dev[ cur].job[ COM_WRITE].dataset,
                    &dev[ cur].ds_obj[ c600_index].dataset,
                    sizeof( DS));

            /*
             * check if the start address doesn't lie before dataset start address
             */
            if( Mci_Get_Start( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) <
                Com_Get_Ds_Start( &dev[ cur].job[ COM_WRITE])) {

              fl_status( &task, "PD_W_RANGE", FLS_ERROR, dev[ cur].phys_dev.name,
                                    dev[ cur].job[ COM_WRITE].dataset.type,
                                    dev[ cur].job[ COM_WRITE].dataset.start,
                                    dev[ cur].job[ COM_WRITE].dataset.len);
              break;
            }

            /*
             * set the start and length according to MCI message
             */
            Com_Set_Ds_Start( &dev[ cur].job[ COM_WRITE],
                              Mci_Get_Start( &dev[ cur].ds_obj[ c600_index].mci[ fnc]));

            Com_Set_Ds_Length( &dev[ cur].job[ COM_WRITE],
                     Mci_Get_Len( &dev[ cur].ds_obj[ c600_index].mci[ fnc]));
            /*
             * adjust if necessary the start and length of the block to send
             */
            if( Com_Cmp_Ds( &dev[ cur].ds_obj[ c600_index].dataset,
                            &dev[ cur].job[ COM_WRITE].dataset,
                            &start,
                            &len) == -1) {

              fl_status( &task, "PD_W_RANGE", FLS_ERROR, dev[ cur].phys_dev.name,
                                    dev[ cur].job[ COM_WRITE].dataset.type,
                                    dev[ cur].job[ COM_WRITE].dataset.start,
                                    dev[ cur].job[ COM_WRITE].dataset.len);
              break;
            }

            Com_Set_Ds_Start( &dev[ cur].job[ COM_WRITE], start);
            Com_Set_Ds_Length( &dev[ cur].job[ COM_WRITE], len);

            return Com_Write( &dev[ cur].job[ fnc]);
          }
        }
        else {
          /*
           * process encode write function
           */
          /*
           * set the length for the communication protocol
           */
          Com_Set_Tx_Len( &dev[ cur].job[ COM_WRITE],
                          Mci_Get_Len( &dev[ cur].ds_obj[ c600_index].mci[ fnc]) *
                          Mci_Get_Boundary( &dev[ cur].ds_obj[ c600_index].mci[ fnc])
                       );
          /*
           * call the encoding function which fills in the encoded data
           */
          Com_Encode(

            &dev[ cur].job[ fnc],
            &dev[ cur].encode,
            &dev[ cur].ds_obj[ c600_index].dataset,
            Mci_Get_CDE( &dev[ cur].ds_obj[ c600_index].mci[ fnc]),
            Mci_Get_Start( &dev[ cur].ds_obj[ c600_index].mci[ fnc]),
            Mci_Get_Buffer( &dev[ cur].ds_obj[ c600_index].mci[ fnc])
                    );

          return  Com_Write( &dev[ cur].job[ fnc]);
        }
      }
      /*
       * set out a READ command
       */
      else if( fnc == COM_READ) {

        /* check if READ function is still enable */

        if( disable) continue;

        /* set the DataSet in the current Job */

        memcpy( &dev[ cur].job[ fnc].dataset,
                &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].dataset,
                sizeof( DS));

        return Com_Read( &dev[ cur].job[ fnc]);
      }
    }
  }
  
  /* no job initiated return this info !!! */
  return INACTIVE; /* return 0; originall!!! */
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int initiate_rcv( char *)
 *
 * PUPRPOSE:
 *
 * Sets out a new receive JOB for unsolicited data to receive
 *
 -----------------------------------------------------------------------------*/
int initiate_rcv( char fnc) {

  DIG    disable = 0;
  i16    t_info;

  /* check first if RECEIVE functionality still enabled */

  if( fl_get_tag_info( &dev[ cur].dis_fnc[ fnc], 1, &t_info, NULL) == GOOD) {

    if( fl_read( task.id, &dev[ cur].dis_fnc[ fnc], 1, &disable) != GOOD)
      fl_status( &task, "FL_READ", FLS_ERROR, dev[ cur].phys_dev.name, fl_error( task.id));

    if( disable) return 0;
  }
  else {

    if( t_info != FL_UNDEFINED)
      fl_status( &task, "FL_GET_TAG_INFO", FLS_ERROR, "disable tag receive", dev[ cur].phys_dev.name,
                    fl_error( task.id));
  }

  /* set out the new unsolicited receive command */

  return Com_Receive( &dev[ cur].job[ fnc]);
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int initiate_global( char *)
 *
 * PUPRPOSE:
 *
 * Sets out a new receive JOB for unsolicited global data to receive
 *
 -----------------------------------------------------------------------------*/
int initiate_global( char fnc) {

  return Com_Global_Rx( &dev[ cur].job[ fnc]);
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void complete( char fnc)
 *
 * PUPRPOSE:
 *
 * handles the completion of every different functionality of a device
 * ( READ and RECEIVE)
 -----------------------------------------------------------------------------*/
void complete( char fnc) {

  DIG     disable = 0;
  VAL     val;
  i16     t_info;
  uint    count,
          i;
  int     ret,
          accept = 0,
          start,
          len;

  /*
   * check first what kind of job completed. A connect job doesn't have to be processed.
   */
  if( Com_Get_Type( &dev[ cur].job[ fnc]) != COM_READ_TYPE &&
      Com_Get_Type( &dev[ cur].job[ fnc]) != COM_RECEIVE_TYPE &&
      Com_Get_Type( &dev[ cur].job[ fnc]) != COM_WRITE_TYPE &&
      Com_Get_Type( &dev[ cur].job[ fnc]) != COM_GLOBAL_TYPE
     )
    return;

  /*
   * check if unsolicited data or read data came in
   */
  if( fnc == COM_RCV || fnc == COM_GLOBAL)
  {

    /*
     * check if global has changed
     */
    if( fnc == COM_GLOBAL) 
    {

      if( !memcmp( Com_Get_Buf( &dev[ cur].job[ COM_GLOBAL]),
                   Com_Get_Buf( &dev[ cur].job[ COM_GLOBAL]) + MAX_GLOBAL_DATA,
                   Com_Get_Ds_Length( &dev[ cur].job[ COM_GLOBAL]) *
                   Com_Get_Ds_Boundary( &dev[ cur].job[ COM_GLOBAL])
                 )
        )
      {

        return;
      }
      else 
      {

        memmove( Com_Get_Buf( &dev[ cur].job[ COM_GLOBAL]) + MAX_GLOBAL_DATA,
                 Com_Get_Buf( &dev[ cur].job[ COM_GLOBAL]),
                 Com_Get_Ds_Length( &dev[ cur].job[ COM_GLOBAL]) *
                 Com_Get_Ds_Boundary( &dev[ cur].job[ COM_GLOBAL])
               );
      }
    }

    /* search for the matching DataSet Object */

    for( i = 0; i < dev[ cur].nr_data; i++)
    {

      /* compare the DataSets on communication level */

      if( !Com_Cmp_Ds( &dev[ cur].ds_obj[ i].dataset,
                       &dev[ cur].job[ fnc].dataset,
                       &start,
                       &len))
      {

         debug_log( &task, "Unsolicited receive: DS %d:%d start %d length %d", 1,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ i].mci[ COM_RCV]))->t_type,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ i].mci[ COM_RCV]))->t_data,
                    dev[ cur].job[ fnc].dataset.start,
                    dev[ cur].job[ fnc].dataset.len);

        accept = 1;

        /*
         * check the number of messages actual in the Mailbox queue
         */
        if( Mci_Nr_Msg( task.id,
                        Mci_Get_Snd_Mbx( &dev[ cur].ds_obj[ i].mci[ COM_RCV]),
                        &count) != GOOD)
        {

          fl_status( &task, "MCI_NR_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
          return;
        }

        if( count >= decoders[ dev[ cur].ds_obj[ i].dc_index].max_msg)
        {

          fl_status( &task, "MCI_MAX_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
          return;
        }

        /*
         * set the buffer to the right postion for the MCI message. If the data received
         * has a different offset than the dataset specified move the pointer to the offset
         * of the receive data
         */
        Mci_Set_Buffer( &dev[ cur].ds_obj[ i].mci[ COM_RCV],
                        ( char *)dev[ cur].phys_dev.buf[ fnc]
                                     + USER_BUF_START
                                     + ( (start - dev[ cur].job[ fnc].dataset.start) *
                                         Com_Get_Ds_Boundary( &dev[ cur].job[ fnc]))
                                     - sizeof( MCIHDR));

        /* set the type of the message */

        Mci_Set_Type( &dev[ cur].ds_obj[ i].mci[ COM_RCV], MCI_RCV_RDY);
        Mci_Set_Version( &dev[ cur].ds_obj[ i].mci[ COM_RCV], MCI_VERSION);
        Mci_Set_CDE( &dev[ cur].ds_obj[ i].mci[ COM_RCV], CDE_BLOCK);

        /* set the start address of the received data */

        Mci_Set_Start( &dev[ cur].ds_obj[ i].mci[ COM_RCV], start);

        /* set the length of the received data */

        Mci_Set_Len( &dev[ cur].ds_obj[ i].mci[ COM_RCV], len);

        /* set the address boundary type of this DataSet */

        Mci_Set_Boundary( &dev[ cur].ds_obj[ i].mci[ COM_RCV],
                          Com_Get_Ds_Boundary( &dev[ cur].job[ fnc]));

        /* set the direction of the data in the DataSet */

        Mci_Set_Bound_Dir( &dev[ cur].ds_obj[ i].mci[ COM_RCV],
                           Com_Get_Ds_Direction( &dev[ cur].job[ fnc]));

        Mci_Set_Bit_Dir( &dev[ cur].ds_obj[ i].mci[ COM_RCV],
                         Com_Get_Ds_Bit_Direct( &dev[ cur].job[ fnc]));

        Mci_Set_Bound_Start( &dev[ cur].ds_obj[ i].mci[ COM_RCV], 1);
        Mci_Set_Bit_Start( &dev[ cur].ds_obj[ i].mci[ COM_RCV], 1);


        /* do the real sending of the data to Protocol Decoder */

        if( ( ret = Mci_Send( task.id,
                              &dev[ cur].ds_obj[ i].mci[ COM_RCV])) != GOOD)
          fl_status( &task, "MCI_SEND", FLS_ERROR, ret);

        /*
         * set the completion trigger for this dataset
         */
        if( fl_get_tag_info( &dev[ cur].ds_obj[ i].completion[ COM_RCV],
                         1,
                         &t_info,
                         NULL) == GOOD)
        {

          val.dig = 1;                              /* set value to 1 to simulate trigger */

          /* set the completion TAG of this DataSet Object */

          if( fl_forced_write( task.id,
                               &dev[ cur].ds_obj[ i].completion[ COM_RCV],
                               1,
                               &val) != GOOD)
            fl_status( &task, "FL_FORCED_WRITE", FLS_ERROR, dev[ cur].phys_dev.name, fl_error( task.id));

        }
      }
    }

    if( !accept) fl_status( &task, "PD_R_REJECT", FLS_ERROR,
                           dev[ cur].job[ fnc].dataset.type,
                           dev[ cur].job[ fnc].dataset.start,
                           dev[ cur].job[ fnc].dataset.len
                        );

    /*
     * check if this was a continuous global receive
     */
    if( fnc == COM_GLOBAL) return;
  }
  else if( fnc == COM_READ) {

    /*
     * check first if this is a normal READ or READ for exception write
     */
    if( dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].exc_reading)
    {

      /* get the value from the READ buffer for the WRITE command */

      memcpy( Com_Get_Buf( &dev[ cur].job[ COM_WRITE]),
              Com_Get_Buf( &dev[ cur].job[ COM_READ]) +
                    ( ( Com_Get_Ds_Start( &dev[ cur].job[ COM_WRITE]) -
                        Com_Get_Ds_Start( &dev[ cur].job[ COM_READ])
                      ) *
                      Com_Get_Ds_Boundary( &dev[ cur].job[ COM_WRITE])),
              sizeof( long));

      /* mark the READ ready for the waiting WRITE command */

      dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].exc_reading = 0;

      debug_log( &task, "Exception write: %s", 2, dev[ cur].phys_dev.name);

      /*
       * check if connection is maintained dynamically. If so then disconnect the connection
       */
      if( Com_Conn_Dynamic( dev[ cur].job[ fnc].dev, fnc))
        Com_Disconnect( &dev[ cur].job[ fnc]);

      /* check if this dataset was triggerd */

      if( !dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].triggered[ COM_READ])
        return;
    }

    /* reset the triggered flag */

    dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].triggered[ fnc] = 0;

    /*
     * check first if the triggered read function is enabled in case of read for
     * execption write
     */
    if( fl_get_tag_info( &dev[ cur].dis_fnc[ fnc], 1, &t_info, NULL) == GOOD) {

      if( fl_read( task.id, &dev[ cur].dis_fnc[ fnc], 1, &disable) != GOOD)
        fl_status( &task, "FL_READ", FLS_ERROR, dev[ cur].phys_dev.name, fl_error( task.id));
    }
    else {

      if( t_info != FL_UNDEFINED)
        fl_status( &task, "FL_GET_TAG_INFO", FLS_ERROR, "disable tag", dev[ cur].phys_dev.name,
                   fl_error( task.id));
    }

    if( disable) return;

    /*
     * check the number of messages actual in the Mailbox queue
     */
    if( Mci_Nr_Msg( task.id,
                    Mci_Get_Snd_Mbx( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]),
                    &count) != GOOD) {

      fl_status( &task, "MCI_NR_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
      return;
    }

    if( count >= decoders[ dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].dc_index ].max_msg) {

      fl_status( &task, "MCI_MAX_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
      return;
    }

    /* set the type of the message */

    debug_log( &task, "Read completed %s: DS %d:%d start %d length %d", 1,
                    dev[ cur].phys_dev.name,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]))->t_type,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]))->t_data,
                    Com_Get_Ds_Start( &dev[ cur].job[ fnc]),
                    Com_Get_Rx_Len( &dev[ cur].job[ fnc]));


    /* set the start address of the received data */

    Mci_Set_Start( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc],
                   Com_Get_Ds_Start( &dev[ cur].job[ fnc]));

    /* set the length of the received data */

    Mci_Set_Len( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc],
                 Com_Get_Rx_Len( &dev[ cur].job[ fnc]));

    Mci_Set_Type( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], MCI_READ_RSP);
    Mci_Set_Version( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], MCI_VERSION);
    Mci_Set_CDE( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], CDE_BLOCK);

    /* set the address boundary type of this DataSet */

    Mci_Set_Boundary( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc],
                      Com_Get_Ds_Boundary( &dev[ cur].job[ fnc]));

    /* set the direction of the data in the DataSet */

    Mci_Set_Bound_Dir( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc],
                       Com_Get_Ds_Direction( &dev[ cur].job[ fnc]));

    Mci_Set_Bit_Dir( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc],
                     Com_Get_Ds_Bit_Direct( &dev[ cur].job[ fnc]));

    Mci_Set_Bound_Start( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc], 1);
    Mci_Set_Bit_Start( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc], 1);

    /* do the real sending of the data to Protocol Decoder */

    if( ( ret = Mci_Send( task.id,
                          &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc])) != GOOD)
      fl_status( &task, "MCI_SEND", FLS_ERROR, ret);
  }
  else if( fnc == COM_WRITE) {

    /*
     * check the number of messages actual in the Mailbox queue
     */
    if( Mci_Nr_Msg( task.id,
                    Mci_Get_Snd_Mbx( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]),
                    &count) != GOOD) {

      fl_status( &task, "MCI_NR_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
      return;
    }

    if( count >= decoders[ dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].dc_index ].max_msg) {

      fl_status( &task, "MCI_MAX_MSG", FLS_ERROR, dev[ cur].phys_dev.name);
      return;
    }

    /*
     * send a MCI MCI_WRITE_RSP message to the decoder
     */
    Mci_Set_Type( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], MCI_WRITE_RSP);
    Mci_Set_Version( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], MCI_VERSION);
    Mci_Set_Nr_Commands( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc] ].mci[ fnc], 0);

    debug_log( &task, "Write completed %s: DS %d:%d", 1,
                    dev[ cur].phys_dev.name,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]))->t_type,
                    (( TAG *)Mci_Get_MsgCtrl( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]))->t_data
             );

    if( ( ret = Mci_Send( task.id,
                          &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc])) != GOOD)
      fl_status( &task, "MCI_SEND", FLS_ERROR, ret);

    /* reset the triggered flag */

    dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].triggered[ fnc] = 0;
    dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].state[ fnc] = IDLE;
  }

  /* check first if a completion tag has been specified */

  if(  fnc == COM_READ ||
       e_x_option == ON ||
       ( fnc == COM_WRITE &&
         Mci_Get_CDE( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]) == CDE_BLOCK)
    ) {

    if( fl_get_tag_info( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].completion[ fnc],
                         1,
                         &t_info,
                         NULL) == GOOD) {

      val.dig = 1;                              /* set value to 1 to simulate trigger */

      if( fl_forced_write( task.id,
                           &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].completion[ fnc],
                           1,
                           &val) != GOOD)
        fl_status( &task, "FL_FORCED_WRITE", FLS_ERROR, dev[ cur].phys_dev.name, fl_error( task.id));
    }
  }

  /*
   * check if connection is maintained dynamically. If so then disconnect the connection
   */
  if( Com_Conn_Dynamic( dev[ cur].job[ fnc].dev, fnc))
    Com_Disconnect( &dev[ cur].job[ fnc]);
}

/*-----------------------------------------------------------------------------
 * FUNCTION: void complete_error( char fnc)
 *
 * PUPRPOSE:
 *
 -----------------------------------------------------------------------------*/
void complete_error( DATAOBJ *ds_obj, char fnc, ushort error) {

  int ret;
  VAL val;
  i16 t_info;

  /* check first if a completion tag has been specified */

  if( fl_get_tag_info( &ds_obj->completion[ fnc],
                       1,
                       &t_info,
                       NULL) == GOOD) {

    val.dig = 1;                              /* set value to 1 to simulate trigger */

    /* set the completion TAG of this DataSet Object */

    if(  fnc == COM_READ ||
         (e_x_option == ON) ||
         ( fnc == COM_WRITE &&
           Mci_Get_CDE( &dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].mci[ fnc]) == CDE_BLOCK)
      ) {

      if( fl_forced_write( task.id,
                           &ds_obj->completion[ fnc],
                           1,
                           &val) != GOOD)
        fl_status( &task, "FL_FORCED_WRITE", FLS_ERROR, "error completion", fl_error( task.id));
    }
  }

  /*
   * set the length of the error message
   */
  Mci_Set_Len( &ds_obj->mci[ fnc], sizeof( error));
  Mci_Set_Boundary( &ds_obj->mci[ fnc], MCI_WORD_BND);

  /*
   * check if unsolicited data or read data came in
   */
  if( fnc == COM_RCV) {

    Mci_Set_Type( &ds_obj->mci[ fnc], MCI_RCV_ERROR);

    /**( ushort *)Mci_Get_Buffer( &ds_obj->mci[ fnc]) = error;*/
    memcpy( Mci_Get_Buffer( &ds_obj->mci[ fnc]), &error, sizeof( error));

    debug_log( &task, "Unsolicited error: DS %d:%d error# %d", 1,
                (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_type,
               (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_data,
               error);
  }
  else if( fnc == COM_READ) {

    Mci_Set_Type( &ds_obj->mci[ fnc], MCI_READ_ERROR);

    /**( ushort *)Mci_Get_Buffer( &ds_obj->mci[ fnc]) = error;*/
    memcpy( Mci_Get_Buffer( &ds_obj->mci[ fnc]), &error, sizeof( error));

    debug_log( &task, "Read error: DS %d:%d error# %d", 1,
                (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_type,
               (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_data,
               error);
  }
  else if( fnc == COM_WRITE) {

    Mci_Set_Type( &ds_obj->mci[ fnc], MCI_WRITE_ERROR);

    /**( ushort *)Mci_Get_Buffer( &ds_obj->mci[ fnc]) = error;*/
    memcpy( Mci_Get_Buffer( &ds_obj->mci[ fnc]), &error, sizeof( error));

    debug_log( &task, "Write error: DS %d:%d error# %d", 1,
                (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_type,
               (( TAG *)Mci_Get_MsgCtrl( &ds_obj->mci[ fnc]))->t_data,
               error);
  }

  /* do the real sending of the data to Protocol Decoder */

  if( ( ret = Mci_Send( task.id,
                        &ds_obj->mci[ fnc])) != GOOD) {

    fl_status( &task, "MCI_SEND", FLS_ERROR, ret);
  }
}


/*-----------------------------------------------------------------------------
 * FUNCTION: int evaluate_write( MCIMSG *, JOB *)
 *
 * PUPRPOSE: Evaluates the MCI message if it is possible to write this message
 *           and if this is the case if it will be a real exception write (reading
 *           before writing) or a block write.
 *
 *           This function must be called after Mci_Adjust_Msg 
 *
 -----------------------------------------------------------------------------*/
int evaluate_write( MCIMSG *mci, JOB *j) {

  /*
   * check first if the destination boundary (of dataset) is a bit boundary. If this
   * is the case then the data is already in the correct state (Mci_Adjust_Msg) and
   * only the length needs to be adjusted
   */
  if( Com_Get_Ds_Boundary( j) == MCI_BIT_BND) {

    /*
     * this will always be a block write. In this case only the lowest CDE is possible
     * of every type
     */
    switch( Mci_Get_CDE( mci)) {

      case CDE_B0_15:                     /* write one bit */

        Mci_Set_Len( mci, 1);
        break;

      case CDE_BYTE:                      /* convert to block write of 8 bits */
      case CDE_UBYTE:

        Mci_Set_Len( mci, 8);
        break;

      case CDE_SIGNED:                    /* convert to block write of 16 bits */
      case CDE_UNSIGNED:

        Mci_Set_Len( mci, 16);
        break;

      case CDE_LONG:                      /* convert to block write of 32 bits */
      case CDE_ULONG:

        Mci_Set_Len( mci, 32);
        break;

      case CDE_DOUBLE_IEEE_FLT:           /* convert to block write of 64 bits */

        Mci_Set_Len( mci, 64);
        break;

      default:

        /* another addressing type on bit boundary is not possible */

        return PD_BIT_ADDRESSING;
    }

    /* set the type to block write */

    Mci_Set_CDE( mci, CDE_BLOCK);

    return 0;
  }

  /*
   * in case the MCI boundary is greater than the dataset boundary then it is not
   * possible to write the element.
   */
  if( Mci_Get_Boundary( mci) > Com_Get_Ds_Boundary( j)) {

    return PD_EXWRITE;
  }

  /*
   * MCI boundary <= dataset boundary
   *
   * in this case check the CDE against the dataset boundary. If the CDE is smaller
   * then this is a real exception write, otherwise it will be a block write
   */
  switch( Com_Get_Ds_Boundary( j)) {

    case MCI_BYTE_BND:

      if( Mci_Get_CDE( mci) >= CDE_BYTE) {             /* exception write ? */

        /* adjust the boundary and length to the dataset */

        Mci_Set_CDE( mci, CDE_BLOCK);
      }

      break;
    case MCI_WORD_BND:

      if( Mci_Get_CDE( mci) < CDE_SIGNED) {             /* exception write ? */
        /*
         * expand the data of the mci message to the dataset boundary if 
         * boundary is not equal
         */
        if( Mci_Get_Boundary( mci) < MCI_WORD_BND)
          Mci_Cast( mci, MCI_WORD_BND);
      }
      else                                              /* adjust the CDE code to block write */
        Mci_Set_CDE( mci, CDE_BLOCK);

      break;

    case MCI_LONG_BND:

      if( Mci_Get_CDE( mci) < CDE_LONG) {               /* exception write ? */
        /*
         * expand the data of the mci message to the dataset boundary if
         * boundary is not equal
         */
        if( Mci_Get_Boundary( mci) < MCI_LONG_BND) 
          Mci_Cast( mci, MCI_LONG_BND);
      }
      else                                              /* adjust the CDE code to block write */ 
        Mci_Set_CDE( mci, CDE_BLOCK);
      break;

    case MCI_DBL_BND:

      if( Mci_Get_CDE( mci) < CDE_DOUBLE_IEEE_FLT) {    /* exception write ? */
        /*
         * expand the data of the mci message to the dataset boundary if
         * boundary is not equal
         */
        if( Mci_Get_Boundary( mci) < MCI_DBL_BND) 
          Mci_Cast( mci, MCI_DBL_BND);

      }
      else                                              /* adjust the CDE code to block write */ 
        Mci_Set_CDE( mci, CDE_BLOCK);

      break;
  }

  if( Mci_Get_CDE( mci) == CDE_BLOCK) {

    /*Mci_Set_Len( mci, (Mci_Get_Len( mci) * Mci_Get_Boundary( mci)) / Com_Get_Ds_Boundary( j));*/

     /* set the boundary to the dataset boundary */

    Mci_Set_Boundary( mci, Com_Get_Ds_Boundary( j));

    /*
      * check the direction of the data
      */
    if( Mci_Get_Bound_Dir( mci) != Com_Get_Ds_Direction( j)) {

       mirror( Mci_Get_Buffer( mci), Mci_Get_Len( mci) * Mci_Get_Boundary( mci));
       Mci_Set_Bound_Dir( mci, Com_Get_Ds_Direction( j));

     }
  }
  else {

    Mci_Set_Len( mci, 1);                           /* set to the length of one element */

    /* set the boundary to the dataset boundary */

    Mci_Set_Boundary( mci, Com_Get_Ds_Boundary( j));

    /*
     * set the data on the right place in case of an exception write
     */
    Mci_Place_Data( mci, Com_Get_Ds_Direction( j));

    Mci_Set_Bound_Dir( mci, Com_Get_Ds_Direction( j));

  }

  return 0;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: DATAOBJ  *srch_dsobj( TAG *ds_ctrl, DEVICE **dev)
 *
 * PUPRPOSE:
 *
 * searches for a unique DataSet Object which contains the specified DataSet Control
 * TAG. In dev the device will be returned if a pointer is specified. 
 -----------------------------------------------------------------------------*/
DATAOBJ  *srch_dsobj( TAG *ds_ctrl, DEVICE **ret_dev) {

  uint i,            /* counter for devices */
       d;            /* counter for Data Objects */

  /* search the DataSet Object list for every device */

  for( i = 0; i < nr_devices; i++) {

    /* search the DataSet Object list of specific device */

    for( d = 0; d < dev[i].nr_data; d++) {

      /* compare the DataSet Control TAGs */

      if( CMPTAG( ds_ctrl, Mci_Get_MsgCtrl( &dev[ i].ds_obj[ d].mci[ 0]))) {

        if( ret_dev) *ret_dev = &dev[ i];

        return &dev[ i].ds_obj[ d];
      }
    }
  }

  return ( DATAOBJ  *)0;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int remove_jobs( DEVICE *d, char fnc)
 *
 * PUPRPOSE: Removes all possible jobs of this functionality of this device
 *           which remain currently in the MCI queue
 -----------------------------------------------------------------------------*/
int remove_jobs( DEVICE *d, char fnc) {

  int    ret;
  TAG    *c, ctrl;
  uint   i;
  MCIMSG msg;


  /*
   * check first if there are active messages
   */
  if( !nr_msg_active)  return 0;

  /*
   * walk the total dataset object list and check if present in queue
   */
  for( i = 0; i < d->nr_data; i++) {

    c = Mci_Get_MsgCtrl( &d->ds_obj[ i].mci[ fnc]);
    ctrl = *c;

    Mci_Set_MsgCtrl ( &msg, ctrl);
    Mci_Set_Rcv_Mbx ( &msg, ds_ctrl[ PD_MBX]);
    Mci_Set_Index   ( &msg, 0);
    Mci_Set_Id      ( &msg, Mci_Get_Id( &d->ds_obj[ i].mci[ fnc]));

    do {

      if( ( ret = Mci_Srch_Msg( task.id, &msg)) == GOOD) {

        /*
         * check if this message falls within the range of active messages
         */
        if( Mci_Get_Index( &msg) > nr_msg_active -1)
          break;

        /*
         * check if this message is of the same function
         */
        if( Mci_Query_Msg( task.id, &msg) == GOOD) {

          switch( Mci_Get_Type( &msg)) {

            case MCI_QUERY_CMD:

              Mci_Set_Index( &msg, Mci_Get_Index( &msg) + 1);

              break;
            case MCI_WRITE_REQ:

              if( fnc == COM_WRITE) {

                /*
                 * remove the message
                 */
                Mci_Set_Index( &d->ds_obj[ i].mci[ fnc], Mci_Get_Index( &msg));

                if( ( ret = Mci_Recv( task.id,
                                      &d->ds_obj[ i].mci[ fnc])) != GOOD)
                  fl_status( &task, "MCI_RCV", FLS_ERROR, ret);

                /*
                 * reply error message to the decoder
                 */
                complete_error( &d->ds_obj[ i],
                                fnc,
                                Com_Get_Dev_Error( &d->phys_dev, fnc));


                /*
                 * update number of message active in queue
                 */
                nr_msg_active--;
                d->ds_obj[ i].nr_rxmsg--;

                if( !d->ds_obj[ i].nr_rxmsg)
                  d->ds_obj[ i].process[ fnc] = 0;

                /*
                 * in case no more active message left, stop remove
                 */
                if( nr_msg_active == 0) return 0;
              }
              else {

                Mci_Set_Index( &msg, Mci_Get_Index( &msg) + 1);
              }

              break;
            default:

              Mci_Set_Index( &msg, Mci_Get_Index( &msg) + 1);

              break;
          }
        }
      }

    } while( ret == GOOD);
  }

  return 0;
}

/*-----------------------------------------------------------------------------
 * FUNCTION: int pd_error( DEVICE *d, char fnc)
 *
 * PUPRPOSE:
 -----------------------------------------------------------------------------*/
int pd_error( DEVICE *d, char fnc) {

  i16   info;          /* TAG information */
  uint index = 0;

  /* check if error occured for the first time */

  if( !d->error[ fnc]) {

    fl_get_tag_info( &d->status, 1, &info, ( u16 *)0);

    if( info != FL_UNDEFINED)
      fl_write( task.id,
                &d->status,
                1,
                &d->job[ fnc].error);

    fl_status( &task, "COM_ERROR",
            FLS_ERROR,
            comfnc[ fnc],
            d->job[ fnc].dev->name,
            comtype[ d->job[ fnc].type],
            d->job[ fnc].error);

    /*
     * reply error message to the decoder
     */
    if( fnc != COM_GLOBAL) {

      complete_error( &d->ds_obj[ d->job_ind[ fnc]],
                      fnc,
                      d->job[ fnc].error);
    }

    /*
     * error on read and exception read is specified
     */
    if( fnc == COM_READ && d->ds_obj[ d->job_ind[ fnc]].exc_reading) {

      /* Now we have a failed read, and a exc. write waiting for the read */

      dev[ cur].ds_obj[ dev[ cur].job_ind[ fnc]].exc_reading = 0; /* reset the exc. reading */

      /* Set the job state for the exc. write to JOB_READY, throw away the job !!!! */
         
      Com_Set_Job_State( &dev[ cur].job[ COM_WRITE], JOB_ERROR);

      d->job[ COM_WRITE].error = d->job[ fnc].error;
    }

    d->error[ fnc] = 1;

    /*
     * check if the error is severe, if so then remove all pending jobs from the queue
     */
    if( Com_Error_Type( &d->job[ fnc])) {

      /*
       * remove all pending messages from the queue
       */
      remove_jobs( d, fnc);
    }
    else {                                /* error is not severe, clear and return */

      Com_Error( &d->job[ fnc]);

      return 0;
    }
  }

  /* check if the rebuild trigger has been fired for the READ and WRITE functions */

  if( (fnc != COM_RCV) && (fnc != COM_GLOBAL)) {

    /* check on the status and rebuild trigger */

    fl_get_tag_info( &d->rebuild, 1, &info, (u16 *)0);

     if( info != FL_UNDEFINED) {

      if( fl_change_read( task.id,
                          &d->rebuild,
                          1,
                          &index,
                          ( u16 *)&info) != GOOD) {

        if( fl_errno( task.id) != FLE_NO_CHANGE)
          fl_status( &task,
                     "FL_CHANGE_READ",
                     FLS_ERROR,
                     "rebuild",
                     d->job[ fnc].dev->name,
                     fl_error( task.id));

        return 0;
      }
    }
  }

  /*
   * try to fix the error
   */
  Com_Error( &d->job[ fnc]);

  /*
   * Give the system some time to rehabilitate
   */
  fl_sleep( 1);

  return 0;
}
