Subversion Repositories factorylink.mb_plus

Rev

Blame | Last modification | View Log | Download

/*
 ******************************************************************************
 *  Copyright 1995 DeltaLink b.v. All Rights Reserved.
 ******************************************************************************
 *
 * DeltaLink Mailbox Communication Interface
 *
 * File         :   mci.c
 *
 * Version      :   3.00
 * 
 * Developed by :   Leon Westervoort.
 *
 * This file contains the MCI functions used by DeltaLink Protocol Drivers and
 * Decoders. 
 *
 * The MCI uses the FactoryLink mailbox types and functions for communication
 * between two tasks which exchange large amounts of data. 
 *
 */

#include  <stdlib.h>
#include  <string.h>
#include  <stdio.h>
#include  <stdarg.h>

#include  <flib.h>                  /* FactoryLink definitions */

#ifdef FLECS
#include  <objct.h>                 /* Object definitions */
#include  <flntag.h>                /* Normalized tag definitions */
#endif

#include  <mci3.h>                  /* Mailbox Communication Interface definitions */
#include  <mci3_int.h>              /* Mailbox Communication Interface definitions */
#include  <format3.h>               /* Mailbox Communication Interface definitions */

/* structure for searching tag id's */

#ifndef FLECS

typedef struct _ct_object
{
  char  tagname[ MAX_TAG_NAME];
  char  tagdomain[ MAX_USR_NAME];
  char  tagtype[ MAX_TYPE_NAME];
  char  tagdesc[ MAX_PROC_DESC];
  char  tagdimen[ MAX_DIM_LENGTH];
  u16   tagperwhen;
  u16   tagchgbits;
  TAG   tagno;
  
} CT_OBJECT;

#endif

/*
 * define a NULL tag which has type 0 (DIGITAL) and index 0 for uninitialized
 * MCI messages
 */
TAG null_tag;

/*
 * global variable for internal use of the MCI library
 */
uint        _Mci_q_num_active;            /* total number of active messages in the queue */

MCISYSTEM   _Mci_system;                  /* system information of the MCI */

uint        _Mci_num_ds;                  /* total number of registered datasets */

TAG         *_Mci_R_ds;                   /* array with remote dataset control tags */
char        *_Mci_R_station_id;           /* array with remote station id numbers */

TAG         *_Mci_L_ds;                   /* array with local dataset control tags */
MCIDS       **_Mci_ds;                    /* array with pointers to dataset information */

int         ( **_Mci_fnc)( void *, VAL);  /* array with function pointers to event functions */

/*
 * FUNCTION:  Mci_Init
 *
 * PURPOSE:   Initializes the Mailbox Communication Interface. 
 *
 */

int Mci_Init( MCISYSTEM *mci ) {

  int ret;

  /* register the local station id and the MCI mailbox*/

  memcpy( ( char *)&_Mci_system, mci, sizeof( MCISYSTEM ));

  /* initialize the MCI mailbox queue */

  if( ( ret = Mci_Q_Init( mci )) != GOOD)
    return ret;

  return GOOD;
}

/*
 * FUNCTION:  Mci_Exit
 *
 * PURPOSE:   Exits the Mailbox Communication Interface. 
 *
 */

int Mci_Exit( void) {

  uint i;

  _Mci_q_num_active = 0;              /* total number of active messages in the queue */

  free( _Mci_R_ds);                   /* array with remote dataset control tags */
  free( _Mci_R_station_id);           /* array with remote station id numbers */
  free( _Mci_L_ds);                   /* array with local dataset control tags */

  for( i = 0; i < _Mci_num_ds; i++)
    free( _Mci_ds[ i]);               /* array with pointers to dataset information */

  free( _Mci_fnc);                    /* array with function pointers to event functions */

  _Mci_num_ds = 0;                    /* total number of registered datasets */

  return GOOD;
}

/*
 * FUNCTION:  Mci_Buffer_Alloc
 *
 * PURPOSE:   This function allocates a buffer for transferring data using the MCI
 *            protocol. An additional header will be allocated for internal use.
 *
 */

int Mci_Buffer_Alloc( MSG *msg) {

  /* try to allocate space for a user buffer with internal MCI header */

  msg->m_max += sizeof( MCIHDR);

  if( ( msg->m_ptr = ( char *)calloc( msg->m_max, 1)) == NULL)
    return FLE_OUT_OF_MEMORY;

  return GOOD;
}

/*
 * FUNCTION:  Mci_Recv
 *
 * PURPOSE:   Receives a dataset which has been queried from the queue first.
 *            The header part will also be set in the wright data direction.
 *
 */
int Mci_Recv( MCISYSTEM *system , MCIDS *ds )
{

  /*
   *  check the origination of this dataset and if from a trigger then it does not have to be read
   *  from the mailbox queue
   */
  if( Mci_Get_Org( ds) == MCI_ORG_TRIGGER) {
  
    /* fake a read request message received here */

    Mci_Ds_Prepare( ds, Mci_Get_T_Buffer( ds));

    Mci_Set_Type( ds, MCI_READ_REQ);
    Mci_Set_CDE ( ds, CDE_BLOCK);

    /*return MCI_MSG_NOT_QUEUED;*/
    return GOOD;
  }
  else if( Mci_Get_Org( ds) == MCI_ORG_QUERY) {

    return GOOD;
  }

  /*
   * check if the index lies in the range of active MCI messages
   */
  if( Mci_Get_Index( ds) >= _Mci_q_num_active )
    return MCI_WRONG_INDEX;

  /* reinstate the buffer pointer and maximum length from tne MCI message */

  if( !ds->msg.mbx.mm_msg.m_ptr || !ds->msg.mbx.mm_msg.m_max) {

    if( ds->msg.buffer.m_ptr && ds->msg.buffer.m_max) {

      ds->msg.mbx.mm_msg.m_ptr = ds->msg.buffer.m_ptr;
      ds->msg.mbx.mm_msg.m_max = ds->msg.buffer.m_max;
    }
    else
      return MCI_MSG_NOT_QUEUED;
  }

  /*
   * do the reading from the receive mailbox which is specified in the MCI
   * message 
   */
  if( fl_read_app_mbx( Mci_Sys_Get_Task_Id( system ),
                       Mci_Sys_Get_Task_Id( system ),/* task id of owner receive mailbox */
                       Mci_Sys_Get_Mbx( system ),    /* mailbox tag to receive from */
                       &ds->msg.mbx,                      /* mailbox message to receive in */
                       ds->msg.i) != GOOD) {              /* index in receive queue */

    return fl_errno( Mci_Sys_Get_Task_Id( system ) );
  }

  /* update the number of active messages in the queue */

  _Mci_q_num_active--;

  /* check the data direction of the data and if necessary adjust the header information */

  if( (Mci_Get_Version( ds) >= 3) && Mci_Get_Org_Host_Dir( ds) != HOST_DIRECTION)
    Mci_Swap_Hdr( ds);

  /* check the validity of the MBXCTRL Tag */

  /*if( fl_get_tag_info( Mci_Get_Ds_Ctrl( ds),
                       1,
                       NULL,
                       NULL) != GOOD) 
    return MCI_BAD_CTRL;*/

  return GOOD;
}

/*
 * FUNCTION: Mci_Send
 *
 * PURPOSE:  This function sends a message through the mailbox to the other task.
 *            Always the local dataset control tag will used. Depeneding on the type
 *            of the message the length of the MBXMSG will be set. The user must
 *            specify the length of its user data first using Mci_Set_Len.
 *
 */

int Mci_Send( MCISYSTEM *system , MCIDS *ds )
{
  i16    data;
  u16    type;
  uint   count;


  /* check the validity of the CTRL Tag */

  if( fl_get_tag_info( Mci_Get_Ds_Ctrl( ds ),
                       1,
                       NULL,
                       NULL) != GOOD)
  {
    return MCI_BAD_CTRL;
  }

  /* check the validity of the mailbox to write to */

  if( fl_get_tag_info( ( TAG *)&Mci_Get_Snd_Mbx( ds),
                       1,
                       &data,
                       &type) != GOOD) {

    return MCI_BAD_SNDMBX;
  }

  /* check the number of datasets in the mailbox to write to */

  if( fl_count_mbx( Mci_Sys_Get_Task_Id( system ),
                    Mci_Get_Snd_Mbx( ds ),
                    &count ) != GOOD )
  {
    return MCI_BAD_SNDMBX;
  }

  if ( count > MCI_MAX_MSG_COUNT )
  {
    return MCI_MAX_MESSAGE;
  }

  /* set the buffer parameters on the MBXMSG mailbox message */

  if( ds->msg.mbx.mm_msg.m_ptr == NULL)
  {
    ds->msg.mbx.mm_msg.m_ptr = ds->msg.buffer.m_ptr;
    ds->msg.mbx.mm_msg.m_max = ds->msg.buffer.m_max;
  }

  /* set initial the response mailbox to undefined */

  ds->msg.mbx.mm_mbx.t_type = ( u16)FL_BAD_TYPE;                    /* set our mailbox to undefined */
  ds->msg.mbx.mm_mbx.t_data = ( u16)FL_BAD_TYPE;                    /* set our mailbox to undefined */

  /* set the length parameters depending on which type of MCI message */

  switch( Mci_Get_Type( ds)) {

    case MCI_QUERY_CMD  :                                 /* request for dataset information */

      /* set the data length to the tag name plus header always in characters */

      ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds));

      /* set response mailbox for compatibilty reasons */

      /*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( &_Mci_system);*/

      /* Set the source station identification of this system */

      Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));

      break;
    case MCI_QUERY_RSP  :                                 /* request for dataset information */

      /* set the data length to the tag name plus header always in characters */

      ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds));

      /* reinstate the defined length of the registered dataset */

      Mci_Set_Len( ds, ds->len);

      break;

    case MCI_READ_REQ  :                                  /* request for dataset information */

      /* set response mailbox for compatibilty reasons */

      /*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( system );*/

      /* Set the source station identification of this system */

      Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));

    case MCI_WRITE_RSP  :                                 /* write response */

      ds->msg.mbx.mm_msg.m_len = ( u16)sizeof( MCIHDR);

      break;

    case MCI_WRITE_REQ    :                               /* write request */

      /* set response mailbox for compatibilty reasons */

      /*ds->msg.mbx.mm_mbx = Mci_Sys_Get_Mbx( system );*/

      /* Set the source station identification of this system */

      Mci_Set_Src_Station( ds, Mci_Sys_Get_Station_Id( system ));

    case MCI_QUERY_ERROR  :                               /* MCI info message feed back */
    case MCI_READ_ERROR   :                               /* read feed back */
    case MCI_RCV_ERROR    :                               /* unsolicited receive ready */
    case MCI_WRITE_ERROR  :                               /* write feed back */

    case MCI_RCV_RDY      :                               /* unsolicited receive ready */
    case MCI_READ_RSP     :                               /* read feed back */

      if( Mci_Get_Boundary( ds) == MCI_BIT_BND)
        ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + ( Mci_Get_Len( ds) / 8) +1);
      else
        ds->msg.mbx.mm_msg.m_len = ( u16)( sizeof( MCIHDR) + Mci_Get_Len( ds) * Mci_Get_Boundary( ds));
      break;

    default:
      return MCI_BAD_TYPE;
  }

  ds->msg.mbx.mm_sendid = Mci_Sys_Get_Task_Id( system );

  /* set the host parameters of this machine */

  Mci_Set_Version       ( ds, MCI_VERSION);

  Mci_Set_Org_Host_Dir  ( ds, HOST_DIRECTION);
  Mci_Set_Station_Id    ( ds, Mci_Sys_Get_Station_Id( system ));

  /* do the actual writing in the mailbox */

  if( fl_write_app_mbx( Mci_Sys_Get_Task_Id( system ),            /* our own task task id */
                        Mci_Sys_Get_Task_Id( system ),            /* Task_id of owner write mailbox */
                        ds->msg.snd_mbx,                              /* mailbox tag to write to */
                        &ds->msg.mbx) != GOOD) {
                         
    return fl_errno( Mci_Sys_Get_Task_Id( system ));
  }

  return GOOD;
}

/*
 * FUNCTION: Mci_Send_Error
 *
 * PURPOSE:  Sends an error MCI message with the type depending on the current message
 *
 */

int Mci_Send_Error( MCISYSTEM *system , MCIDS *ds, u16 error){

  MSG   hdr;
  int   ret;

  /*
   * allocate a MCI header with the size of the error
   */
  hdr.m_max = sizeof( MCIHDR) + sizeof( u16);

  if( ( hdr.m_ptr = ( char *)calloc( 1, hdr.m_max)) == NULL) 
    return FLE_OUT_OF_MEMORY;

  /*
   * set the buffer on the message to the temporary error
   */

  Mci_Set_T_Buffer( ds, &hdr);

  /* return a function not supported error to the other task */

  Mci_Set_Len         ( ds, sizeof( u16));
  Mci_Set_Boundary    ( ds, MCI_WORD_BND);

  *( u16 *)Mci_Get_U_Buffer( ds) = error;

  /* determine what kind of error message type to send back */

  switch( Mci_Get_Type( ds) ) {

    case MCI_QUERY_CMD:            /* MCI info message */

      Mci_Set_Type( ds, MCI_QUERY_ERROR);

      ret = Mci_Send( system, ds );

      break;
    case MCI_READ_REQ:            /* read request */

      Mci_Set_Type( ds, MCI_READ_ERROR);

      ret = Mci_Send( system, ds );

      break;
    case MCI_WRITE_REQ:            /* write request */

      Mci_Set_Type( ds, MCI_WRITE_ERROR);

      ret = Mci_Send( system, ds );

      break;

    default:

      ret = GOOD;
      break;
  }

  /* reset the buffer pointers in case this message will be used in the future */

  Mci_Reset_Buffer( ds);

  free( hdr.m_ptr);

  return ret;
}

/*
 * FUNCTION: Mci_Array_Ds_Srch
 *
 * PURPOSE:  Search the internal arrays and check if the dataset is registered
 *
 */
MCIDS *Mci_Array_Ds_Srch( MCIDS *srch) {

  uint    i;

  /*
   * check first which array to search, the remote or local
   */

  if( Mci_Get_Station_Id( srch) != Mci_Sys_Get_Station_Id( &_Mci_system ) )
  {
    for( i = 0; i < _Mci_num_ds; i++)
      if( MCI_CMPTAG( Mci_Get_Ds_Ctrl( srch), &_Mci_R_ds[ i]) == GOOD) {

        Mci_Set_Ds_Ctrl( srch, _Mci_L_ds[ i]);
      
        return _Mci_ds[ i];
      }
  }
  else {

    for( i = 0; i < _Mci_num_ds; i++)
      if( MCI_CMPTAG( Mci_Get_Ds_Ctrl( srch), &_Mci_L_ds[ i]) == GOOD) return _Mci_ds[ i];
  }

  return NULL;
}

/*
 * FUNCTION:  Mci_Get_Fnc
 *
 * PURPOSE:   Returns the group of functions depending on the dataset type
 *
 */
int Mci_Get_Fnc( MCIDS *ds) {

  switch( Mci_Get_Type( ds) ) {

    case MCI_QUERY_CMD    :           /* MCI info message */
    case MCI_QUERY_RSP    :           /* MCI info message feed back */
    case MCI_QUERY_ERROR  :           /* MCI info message feed back */

      return MCI_QUERY_FNC;           /* Query functionality */

    case MCI_READ_REQ     :           /* read request */
    case MCI_READ_RSP     :           /* read feed back */
    case MCI_READ_ERROR   :           /* read feed back */

      return MCI_READ_FNC;            /* Read functionality */

    case MCI_WRITE_REQ    :           /* write request */
    case MCI_WRITE_RSP    :           /* write feed back */
    case MCI_WRITE_ERROR  :           /* write feed back */

      return MCI_WRITE_FNC;           /* Query functionality */

    case MCI_RCV_ACT      :           /* unsolicited receive active */
    case MCI_RCV_RDY      :           /* unsolicited receive ready */
    case MCI_RCV_ERROR    :           /* unsolicited receive ready */

      return MCI_RCV_FNC;             /* Unsolicited Receive functionality */

    default:
      return ERROR;
  }
}

/*
 * FUNCTION: Mci_Get_CDE_Size()
 *
 * PURPOSE:  This function returns the size of the data on which the operation will be
 *           performed
 */
int Mci_Get_CDE_Size( ushort cde) {

  if( cde < CDE_SIGNED)           return 1;
  if( cde < CDE_LONG)             return 2;
  if( cde < CDE_DOUBLE_IEEE_FLT)  return 4;
  if( cde == CDE_DOUBLE_IEEE_FLT) return 8;

  return ERROR;
}

/*
 * FUNCTION: Mci_Cast( MCIDS *ds, char new_boundary)
 *
 * PURPOSE:  Cast data from old boundary to new boundary
 */
void Mci_Cast( MCIDS *ds, char new_boundary)
{
  void *data = ( void *)Mci_Get_U_Buffer( ds);

  /* check if the direction of the data is in host format */

  if ( Mci_Get_Bnd_Dir( ds) != HOST_DIRECTION )
  {
    Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));
    Mci_Set_Bnd_Dir( ds, HOST_DIRECTION);
  }

  /*
   * The data in the MCI message represents always a valid value and at this point is always
   * in the host sirection. This means that the data is not placed on the final position where
   * it must be written because the MCI message can be in any data direction and boundary. This
   * way it is allowed here to let the compiler do some conversions   
   */
  switch ( Mci_Get_Boundary( ds) )
  {
    case MCI_BYTE_BND:

      switch( new_boundary)
      {
        case MCI_WORD_BND:

          *( ushort *)data = *( uchar *)data;

          Mci_Set_Boundary( ds, MCI_WORD_BND);

          break;
        case MCI_LONG_BND:

          *( ulong *)data = *( uchar *)data;

          Mci_Set_Boundary( ds, MCI_LONG_BND);

          break;
        case MCI_DBL_BND:

          *( double *)data = *( uchar *)data;

          Mci_Set_Boundary( ds, MCI_DBL_BND);

          break;
      }
      break;
    case MCI_WORD_BND:
      switch( new_boundary)
      {
        case MCI_BYTE_BND:

          *( uchar *)data = ( uchar)*( ushort *)data;

          Mci_Set_Boundary( ds, MCI_BYTE_BND);

          break;
        case MCI_LONG_BND:

          *( ulong *)data = *( ushort *)data;

          Mci_Set_Boundary( ds, MCI_LONG_BND);

          break;
        case MCI_DBL_BND:

          *( double *)data = *( ushort *)data;

          Mci_Set_Boundary( ds, MCI_DBL_BND);

          break;
      }
      break;
    case MCI_LONG_BND:

      switch( new_boundary)
      {
        case MCI_BYTE_BND:

          *( uchar *)data = ( uchar)*( ulong *)data;

          Mci_Set_Boundary( ds, MCI_BYTE_BND);

          break;
        case MCI_WORD_BND:

          *( ushort *)data = ( ushort)*( ulong *)data;

          Mci_Set_Boundary( ds, MCI_WORD_BND);

          break;
        case MCI_DBL_BND:

          *( double *)data = *( ulong *)data;

          Mci_Set_Boundary( ds, MCI_DBL_BND);

          break;
      }
      break;
    case MCI_DBL_BND:

      switch( new_boundary)
      {
        case MCI_BYTE_BND:

          *( uchar *)data = ( uchar)*( double *)data;

          Mci_Set_Boundary( ds, MCI_BYTE_BND);

          break;
        case MCI_WORD_BND:

          *( ushort *)data = ( ushort)*( double *)data;

          Mci_Set_Boundary( ds, MCI_WORD_BND);

          break;
        case MCI_LONG_BND:

          *( ulong *)data = ( ulong)*( double *)data;

          Mci_Set_Boundary( ds, MCI_LONG_BND);

          break;
      }
      break;
  }
}

/*
 * FUNCTION: Mci_Place_Data( MCIDS *ds, char bnd_dir)
 *
 * PURPOSE:  places the data on the wright spot depending on the direction of the data.
 *           It is assumed that the data will be deliverd in the whrigt format which means
 *           that the type is big enough.
 *
 *           The original value of the data won't be preserved but shifted out.
 *
 *           The bit operation don't mirror the bits but take the numbering of the bits
 *           in account (start from left or right side numbering).       
 */
void Mci_Place_Data( MCIDS *ds, char bnd_dir) {

  u16 shift;

  /* check if the direction of the data is in host format */

  if( Mci_Get_Bnd_Dir( ds) != HOST_DIRECTION)
  {
    Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));
    Mci_Set_Bnd_Dir( ds, HOST_DIRECTION);
  }

  /*
   * shift the value which is in host format the number of times
   */
  if( Mci_Get_CDE( ds) < CDE_NIBBLE)                 /* do a bit operation */
  {
    /*
     * check in this case the numbering of the bits
     */
    if( Mci_Get_Bit_Dir( ds) != HOST_DIRECTION)
      shift = ( u16) (((Mci_Get_Boundary( ds) * 8) -1) - (Mci_Get_CDE( ds) - CDE_B0_15  - Mci_Get_Bit_Offset( ds)));
    else
      shift = ( u16)( Mci_Get_CDE( ds) - CDE_B0_15 - Mci_Get_Bit_Offset( ds) );
  }
  else if( Mci_Get_CDE( ds) < CDE_BYTE)              /* do a nibble operation */
    shift = ( u16)( Mci_Get_CDE( ds) - CDE_NIBBLE - Mci_Get_Bit_Offset( ds));
  else if( Mci_Get_CDE( ds) < CDE_SIGNED)            /* do a byte operation */
    shift = ( u16)( Mci_Get_CDE( ds) - CDE_BYTE - Mci_Get_Bit_Offset( ds));
  else if( Mci_Get_CDE( ds) < CDE_LONG)              /* do a word operation */
    shift = ( u16)( Mci_Get_CDE( ds) - CDE_SIGNED - Mci_Get_Bit_Offset( ds));
  else if( Mci_Get_CDE( ds) < CDE_DOUBLE_IEEE_FLT)   /* do a long operation */
    shift = ( u16)( Mci_Get_CDE( ds) - CDE_LONG - Mci_Get_Bit_Offset( ds));
  else                                                /* do a double operation */
    shift = ( u16)( Mci_Get_CDE( ds) - CDE_DOUBLE_IEEE_FLT - Mci_Get_Bit_Offset( ds));

  /*
   * set the actual value to the wright place 
   */
  switch( Mci_Get_Boundary( ds)) {

    case MCI_BIT_BND:                             /* bit boundary already on place ! */
      break;
    case MCI_BYTE_BND:                            /* shift a nibble in a byte */
      *( char *)Mci_Get_U_Buffer( ds) <<= shift;
      break;
    case MCI_WORD_BND:                            /* shift a nibble in a word */
      *( ushort *)Mci_Get_U_Buffer( ds) <<= shift;
      break;
    case MCI_LONG_BND:                            /* shift a nibble in a long */
      *( ulong *)Mci_Get_U_Buffer( ds) <<= shift;
      break;
    case MCI_DBL_BND:                             /* shift a nibble in a double */
      /* *( double *)Mci_Get_U_Buffer( ds) <<= shift; */
      break;
  }

  /*
   * check the data direction of the data
   */
  if( bnd_dir != HOST_DIRECTION)
    Mci_Mirror( Mci_Get_U_Buffer( ds), Mci_Get_Boundary( ds));

  return;
}

/*
 * FUNCTION:  Mci_Swap_Hdr( MCIHDR *hdr)
 *
 * PURPOSE:   Swaps the elements in the header
 */
void Mci_Swap_Hdr( MCIDS *ds) {

  MCIHDR  hdr;

  swab( ds->msg.mbx.mm_msg.m_ptr, ( char *)&hdr, sizeof( MCIHDR));
  memcpy( ds->msg.mbx.mm_msg.m_ptr, ( char *)&hdr, sizeof( MCIHDR));

  return;
}
/*-----------------------------------------------------------------------------
 * FUNCTION:  void Mci_Mirror( char *buf, ushort length)
 *
 * PURPOSE:   reverses a character buffer with length 'length'
 -----------------------------------------------------------------------------*/
void Mci_Mirror( char *buf, ushort length) {

  uchar  temp;
  ushort i;

  /*
   * of start = 1 then the real start of the data is on index 0
   */
  for( i = 0; i < length/2; i++) {

    temp = buf[ (length -i) -1];
    buf[ (length -i) -1] = buf[ i];
    buf[ i] = temp;
  }

  return;
}
/*-----------------------------------------------------------------------------
 *
 * FUNCTION: char * Mci_Reverse( char *to, char *from, char nr)
 * PURPOSE : reverse a string of bytes
 *
 -----------------------------------------------------------------------------*/
char  * Mci_Reverse( char *to, char *from, char nr) {

  char i;

  for( i = 0; i < nr; i++) to[ i] = from[ (nr - i) -1];

  return to;
}

/*-----------------------------------------------------------------------------
 *
 * FUNCTION:                 mci_fl_name2tag
 *   
 *
 * arguments:
 *   char *name              Name of first tag in array
 *   char *dimension         Dimension of array. Format like '12,3,5'
 *
 * purpose:
 *   Calculate the number of tags left in the array based on the
 *   name and the dimension.
 * 
 *-----------------------------------------------------------------------------
 */
#ifndef FLECS
int Mci_name2tag( char *name, TAG *tag) {

  CT          ct_buf;
  uint        num_tags,
              t, m, b;
  uint        i, 
              depth, 
              offset, 
              len[5];
  CT_OBJECT   t_info,
              b_info, 
              m_info;
  char        tagname[48];
  char        *index, 
              *dim;

  tag->t_type = 0xFFFF;
  tag->t_data = 0xFFFF;
  
  if( ct_open( &ct_buf, "{FLAPP}", "ct/object.ct") != GOOD)
    return ERROR;
    
  if( ct_read_index( &ct_buf, 0) != GOOD ) {

    ct_close( &ct_buf);
    return ERROR;
  }
  
  num_tags = ct_get_nrecs( &ct_buf);
 
  /*
   * Separate the tagname form the index
   */   
  strcpy( tagname, name); 
  index = strstr( tagname, "[");
  if( index) *index++ = '\0';

  /*
   * Check to see if the tagname is a array element
   * If so skip the part after the first '['
   */

  t = 0;
  b = num_tags - 1;
  m = ( t + b) / 2;
    
  if( ( ct_read_rec( &ct_buf, &t_info, t) != GOOD) ||
      ( ct_read_rec( &ct_buf, &m_info, m) != GOOD) ||
      ( ct_read_rec( &ct_buf, &b_info, b) != GOOD)) {

    tag->t_type = 0xFFFF;
    tag->t_data = 0xFFFF;

    ct_close( &ct_buf);

    return ERROR;
  }
  
  /*
   * Check the Top and Bottom records, if not then 
   * perform a binary search until found
   */
  if ( strcmp( t_info.tagname, tagname) == 0)
    memcpy( &m_info, &t_info, sizeof( CT_OBJECT));

  if ( strcmp( b_info.tagname, tagname) == 0)
    memcpy( &m_info, &b_info, sizeof( CT_OBJECT));
  
  while (( m != t) && ( strcmp(m_info.tagname, tagname) != 0))
  {

    if ( ct_read_rec( &ct_buf, &m_info, m) != GOOD) {

      ct_close( &ct_buf);
      return ERROR;
    }

    if ( strcmp(m_info.tagname, tagname) < 0)
      t = m;
    else
      b = m;

    m = ( t + b) / 2;
  }
 
  if ( strcmp( m_info.tagname, tagname) != 0) {

    ct_close( &ct_buf);
    return ERROR;
  }

  memcpy( tag, &m_info.tagno, sizeof( TAG));

  if( index != NULL) 
  {
    /*
     * Get the dimension of the total array based on 'dimension'
     * Format like '12,3,5'
     */
    dim = m_info.tagdimen;
    i   = 0;

    dim--;
    do
    {
      dim++;
      
      len[ i++] = atoi( dim);

    } while (( dim = strstr( dim, ",")) != NULL);

    /*
     * Calculate the offset of this particalr tag on top of the 
     * general tag value
     */
    offset = 0;
    depth  = i - 1;
    i      = 0;
    
    index--;
    
    do
    {
      index++;

      offset += atoi( index);

      if( i < depth)
        offset *= len[ i + 1];

      i++;

    } while( ( index = strstr( index, "[")) != NULL);
    
    tag->t_data += offset;
  }
  
  ct_close( &ct_buf);

  return GOOD;
}
#else
int Mci_name2tag( char *name, TAG *tag) {

  CT          ct_buf;
  FLNTAG      *ntag;
  int         result = GOOD;


  /*
   * Open the object CT file
   */
  if( ct_open_obj( &ct_buf,"{FLAPP}") != GOOD)
    return ERROR;

  /*
   * Create a normailzed tag
   */
  if( ( ntag = flntag_create()) == NULL)
    return ERROR;

  /*
   * Initialize the ntag with the correct values
   */
  if( flntag_parse_ref( ntag, name) != FLNTAG_E_GOOD)
    return ERROR;

  /* 
   * Find the the in the object CT
   */
  if( flntag_find_tag( ntag, &ct_buf, tag) != FLNTAG_E_GOOD)
    result = ERROR;

  /* 
   * Destroy the normalized tag
   */
  flntag_destroy( ntag);

  /*
   * Close the object CT file
   */
  ct_close_obj( &ct_buf);


  return result;
}
#endif