/*-------------------------------------------------------------------------
                          S E R I A L S I N Q
   Implementation file of the functions for talking with a RS--232 port
   on a SINQ terminal server. This code has been adapted from code 
   provided by David Maden for the EL734 motor controller. A new version 
   became necessary as the Dornier velocity selector supports a 
   completely different protocoll than the EL734. The basics, however, are
   the same.
   
   Mark Koennecke, Juli 1997

       Copyright:

       Labor fuer Neutronenstreuung
       Paul Scherrer Institut
       CH-5423 Villigen-PSI


      The authors hereby grant permission to use, copy, modify, distribute,
      and license this software and its documentation for any purpose, provided
      that existing copyright notices are retained in all copies and that this
      notice is included verbatim in any distributions. No written agreement,
      license, or royalty fee is required for any of the authorized uses.
      Modifications to this software may be copyrighted by their authors
      and need not follow the licensing terms described here, provided that
      the new terms are clearly indicated on the first page of each file where
      they apply.

      IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
      FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
      ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
      DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.

      THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
      FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
      IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
      NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
      MODIFICATIONS.
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

#ifdef FORTIFY
#include "fortify.h"
#endif

#include    <stdarg.h>
#include    <errno.h>
#include    <signal.h>
#include    <netdb.h>
#include    <sys/socket.h>
#include    <netinet/in.h>

#include    <string.h>

#ifdef	__VMS
#include    <unixio.h>
#else
#include    <unistd.h>
#endif

/*-----------------------------------------------------------------*/
#include    "sinq_prototypes.h"
#include    "el734_def.h"
#include    "rs232c_def.h"
#include    "el734fix.h"
#include    "serialsinq.h"

#define True	1
#define False	0

struct SerialInfo {
  int skt;
  int iForce;
  int port;
  int chan;
  char host[20];
  int tmo;
  int msg_id;
  int n_replies, max_replies;
  char pTerms[4];
  char pSendTerm[10];
  struct RS__MsgStruct to_host;
  struct RS__RespStruct from_host;
  SerialSleep pFunc;
  void *pData;
  struct AsynSrv__info sAsync;
};
/*------------------- The default sleep function -----------------------*/
static int SerialNccrrrh(void *pData, int iTime)
{
  usleep(50);
  return 1;
}

/*-----------------------------------------------------------------------*/
int SerialOpen(void **pData, char *pHost, int iPort, int iChannel)
{
  int status;
  struct SerialInfo *my_info;
  void *my_hndl;
  struct hostent *rmt_hostent;
  struct in_addr *rmt_inet_addr_pntr;
  int rmt_sockname_len;
  struct sockaddr_in lcl_sockname;
  struct sockaddr_in rmt_sockname;
  char msr_cmnd[20];
  struct RS__RplyStruct *rply_ptr;

  *pData = NULL;

/*
** allocate memory first 
*/
  *pData = malloc(sizeof(struct SerialInfo));
  if (*pData == NULL) {
    return EL734__BAD_MALLOC;   /* malloc failed!! */
  }
  my_info = *pData;
  memset(my_info, 0, sizeof(struct SerialInfo));

/*
**-------------------------- Set up the connection
*/
  my_info->sAsync.port = iPort;
  strcpy(my_info->sAsync.host, pHost);
  my_info->sAsync.chan = iChannel;
  status = AsynSrv_Open(&(my_info->sAsync));
  if (status != 1) {
    return OPENFAILURE;
  }

  /* intialize  data structures */
  StrJoin(my_info->host, sizeof(my_info->host), pHost, "");
  my_info->skt = my_info->sAsync.skt;
  my_info->port = iPort;
  my_info->chan = iChannel;
  my_info->tmo = 100;
  my_info->msg_id = 0;
  my_info->pFunc = SerialNccrrrh;
  my_info->pData = NULL;
  strcpy(my_info->pTerms, "1\r\n\0");
  my_info->iForce = 0;
  memset(my_info->pSendTerm, 0, 9);
  strcpy(my_info->pSendTerm, "\r\n");

  return 1;
}

/*-----------------------------------------------------------------------*/
int SerialForceOpen(void **pData, char *pHost, int iPort, int iChannel)
{
  int status;
  struct SerialInfo *my_info;
  void *my_hndl;
  struct hostent *rmt_hostent;
  struct in_addr *rmt_inet_addr_pntr;
  int rmt_sockname_len;
  struct sockaddr_in lcl_sockname;
  struct sockaddr_in rmt_sockname;
  char msr_cmnd[20];
  struct RS__RplyStruct *rply_ptr;

  *pData = NULL;

  /* create pData */
  *pData = malloc(sizeof(struct SerialInfo));
  if (*pData == NULL) {
    return EL734__BAD_MALLOC;   /* malloc failed!! */
  }
  my_info = *pData;
  memset(my_info, 0, sizeof(struct SerialInfo));


/*
**-------------------------- Set up the connection
*/
  my_info->sAsync.port = iPort;
  strcpy(my_info->sAsync.host, pHost);
  my_info->sAsync.chan = iChannel;
  status = AsynSrv_OpenNew(&(my_info->sAsync));
  if (status != 1) {
    return OPENFAILURE;
  }

  /* intialize  data structures */
  StrJoin(my_info->host, sizeof(my_info->host), pHost, "");
  my_info->skt = my_info->sAsync.skt;
  my_info->port = iPort;
  my_info->chan = iChannel;
  my_info->tmo = 100;
  my_info->msg_id = 0;
  my_info->pFunc = SerialNccrrrh;
  my_info->pData = NULL;
  strcpy(my_info->pTerms, "1\r\n\0");
  my_info->iForce = 1;
  memset(my_info->pSendTerm, 0, 9);
  strcpy(my_info->pSendTerm, "\r\n");

  return 1;
}

/*--------------------------------------------------------------------------*/
int SerialConfig(void **pData, int iTmo)
{
  struct SerialInfo *my_info = NULL;

  my_info = (struct SerialInfo *) *pData;
  assert(my_info);

  if (iTmo < 100) {
    my_info->tmo = 1;
    return 1;
  } else {
    my_info->tmo = iTmo / 100;  /* convert to deci seconds */
    if (my_info->tmo > 9999)
      my_info->tmo = 9999;
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
int GetSerialTmo(void **pData)
{
  struct SerialInfo *my_info = NULL;
  int iTmo;

  my_info = (struct SerialInfo *) *pData;
  assert(my_info);

  iTmo = my_info->tmo * 100 - 99;       /* convert back to milli seconds */

  return iTmo;
}
int SerialGetTmo(void **pData)
{
  return GetSerialTmo(pData);
}

/*--------------------------------------------------------------------------*/
int SerialGetSocket(void **pData)
{
  struct SerialInfo *my_info = NULL;
  int iTmo;

  my_info = (struct SerialInfo *) *pData;
  assert(my_info);

  return my_info->skt;

  return 1;
}

/*--------------------------------------------------------------------------*/
int SerialClose(void **pData)
{

  struct SerialInfo *info_ptr;
  char buff[4];

  info_ptr = (struct SerialInfo *) *pData;
  if (info_ptr == NULL)
    return True;

  if (info_ptr->skt != 0) {
    AsynSrv_Close(&(info_ptr->sAsync), 0);
    info_ptr->skt = 0;
  }
  free(*pData);
  *pData = NULL;
  return True;
}

/*--------------------------------------------------------------------------*/
int SerialForceClose(void **pData)
{

  struct SerialInfo *info_ptr;
  char buff[4];

  info_ptr = (struct SerialInfo *) *pData;
  if (info_ptr == NULL)
    return True;

  if (info_ptr->skt != 0) {
    AsynSrv_Close(&(info_ptr->sAsync), 1);
    info_ptr->skt = 0;
  }
  free(*pData);
  *pData = NULL;
  return True;
}

/*--------------------------------------------------------------------------*/
int SerialATerm(void **pData, char *pTerm)
{
  struct SerialInfo *my_info = NULL;

  my_info = (struct SerialInfo *) *pData;
  if (my_info == NULL) {
    printf("Serious Programming problem: data = NULL\n");
    return 0;
  }

  /* only three characters in this field */
  if (strlen(pTerm) > 4) {
    return 0;
  }
  memset(my_info->pTerms, 0, 4);
  strcpy(my_info->pTerms, pTerm);

  return 1;
}

/*--------------------------------------------------------------------------*/
int SerialAGetTerm(void **pData, char *pTerm, int iTermLen)
{
  struct SerialInfo *my_info = NULL;

  my_info = (struct SerialInfo *) *pData;
  assert(my_info);

  strncpy(pTerm, my_info->pTerms, iTermLen);

  return 1;
}

/*-------------------------------------------------------------------------*/
int SerialSendTerm(void **pData, char *pTerm)
{
  struct SerialInfo *my_info = NULL;

  my_info = (struct SerialInfo *) *pData;
  assert(my_info);

  /* only 0 characters in this field */
  if (strlen(pTerm) > 9) {
    return 0;
  }
  strcpy(my_info->pSendTerm, pTerm);

  return 1;
}

/*---------------------------------------------------------------------------*/

int SerialSend(void **pData, char *pCommand)
{
  struct SerialInfo *info_ptr;
  int status, c_len, size, max_size, ncmnds;
  int bytes_to_come, bytes_left;
  int iResult;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  char *txt_ptr;
  char *cmnd_lst_ptr;
  char *pComCom = NULL;

  /*
   ** Do nothing if no connection - the connection gets
   ** closed if an error is detected.
   */
  info_ptr = (struct SerialInfo *) *pData;
  if (info_ptr == NULL)
    return NOCONNECTION;
  if (info_ptr->skt == 0) {
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return NOCONNECTION;
  }

  info_ptr->msg_id++;           /* Set up an incrementing message id */
  if (info_ptr->msg_id > 9999)
    info_ptr->msg_id = 1;
  sprintf(info_ptr->to_host.msg_id, "%4.4d", info_ptr->msg_id);

  memcpy(info_ptr->to_host.c_pcol_lvl, RS__PROTOCOL_ID_V01B,
         sizeof(info_ptr->to_host.c_pcol_lvl));
  sprintf(info_ptr->to_host.serial_port, "%4.4d", info_ptr->chan);
  sprintf(info_ptr->to_host.tmo, "%04d", info_ptr->tmo);

  strncpy(info_ptr->sAsync.eot, info_ptr->pTerms, 4);
  memcpy(info_ptr->to_host.terms, info_ptr->pTerms,
         sizeof(info_ptr->to_host.terms));
  memcpy(info_ptr->to_host.n_cmnds, "0000",
         sizeof(info_ptr->to_host.n_cmnds));


  txt_ptr = pCommand;           /* Get pntr to cmnd string */
  ncmnds = 0;
  cmnd_lst_ptr = &info_ptr->to_host.cmnds[0];
  bytes_left = sizeof(info_ptr->to_host) -
      OffsetOf(struct RS__MsgStruct, cmnds[0]);

  size = strlen(txt_ptr) + strlen(info_ptr->pSendTerm);
  if (size > bytes_left) {
    return EL734__BAD_SENDLEN;  /* Too much to send */
  } else {
    strcpy(cmnd_lst_ptr + 4, txt_ptr);
    /* make sure that the string is properly terminated */
    if ((strstr(txt_ptr, info_ptr->pSendTerm) == 0) &&
        (strlen(txt_ptr) > 0)) {
      strcpy(cmnd_lst_ptr + 4 + strlen(txt_ptr), info_ptr->pSendTerm);
      c_len = strlen(txt_ptr) + strlen(info_ptr->pSendTerm);
    } else {
      c_len = strlen(txt_ptr);
    }
    sprintf(text, "%4.4d", c_len);
    memcpy(cmnd_lst_ptr, text, 4);
    cmnd_lst_ptr = cmnd_lst_ptr + c_len + 4;
    ncmnds++;
    bytes_left = bytes_left - size;
  }

  sprintf(text, "%4.4d", ncmnds);
  memcpy(info_ptr->to_host.n_cmnds,
         text, sizeof(info_ptr->to_host.n_cmnds));

  size = cmnd_lst_ptr - info_ptr->to_host.msg_id;
  size = (size + 3) & (~3);     /* Round up to multiple of 4 */
  sprintf(text, "%4.4d", size);
  memcpy(info_ptr->to_host.msg_size, text, 4);

  status = send(info_ptr->skt, (char *) &info_ptr->to_host, size + 4, 0);
  if (status != (size + 4)) {
    if (status == 0) {
      iResult = EL734__BAD_SEND;        /* Server exited (probably) */
    } else if (status == -1) {
      iResult = EL734__BAD_SEND_PIPE;   /* Server exited (probably) */
    }
    AsynSrv_Close(&(info_ptr->sAsync), 1);
    info_ptr->skt = 0;
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return iResult;
  }
  return 1;
}

/*-------------------------------------------------------------------------*/
int SerialReceive(void **pData, char *pBuffer, int iBufLen)
{
  struct SerialInfo *info_ptr;
  int status, c_len, size, max_size, ncmnds;
  int bytes_to_come, bytes_left;
  int iResult;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  char *txt_ptr;
  char *cmnd_lst_ptr;
  struct RS__RplyStruct_V01B *ptr = NULL;
  long lMask = 0L;
  struct timeval tmo = { 0, 1 };


  /*
   ** Do nothing if no connection - the connection gets
   ** closed if an error is detected.
   */
  info_ptr = (struct SerialInfo *) *pData;
  if (info_ptr == NULL)
    return NOCONNECTION;
  if (info_ptr->skt == 0) {
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return NOCONNECTION;
  }

  /* try with select if there is data */
/*    lMask = (1 << info_ptr->skt);
    tmo.tv_usec = 10;
    status = select((info_ptr->skt +1), (fd_set *)&lMask, NULL,NULL,&tmo);
    if(status <= 0)
    {
       return SELECTFAIL;
    } 
*/

  /* try read the message length to come */
  size = sizeof(info_ptr->from_host.msg_size);
  status = recv(info_ptr->skt, info_ptr->from_host.msg_size, size, 0);
  if (status != size) {
    if (status > 0) {
      iResult = EL734__BAD_RECV;        /* Server exited (probably) */
    } else if (status == -1) {
      iResult = EL734__BAD_RECV_NET;    /* It's some other net problem */
    } else {
      iResult = EL734__BAD_RECV_NET;
    }
    AsynSrv_Close(&(info_ptr->sAsync), 1);
    info_ptr->skt = 0;
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return iResult;
  }
  if (sscanf(info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) {
    return EL734__BAD_NOT_BCD;  /* Header not an ASCII BCD integer */
  }

  max_size = sizeof(info_ptr->from_host) -
      sizeof(info_ptr->from_host.msg_size);
  if (bytes_to_come > max_size) {
    iResult = EL734__BAD_RECVLEN;
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    while (bytes_to_come > 0) { /* Flush out the incoming message */
      bytes_left = bytes_to_come;
      if (bytes_left > max_size)
        bytes_left = max_size;
      status = recv(info_ptr->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        iResult = EL734__BAD_FLUSH;     /* TCP/IP problem whilst flushing */
        AsynSrv_Close(&(info_ptr->sAsync), 1);
        info_ptr->skt = 0;
        memset(info_ptr->from_host.msg_size,
               '0', sizeof(info_ptr->from_host.msg_size));
        return iResult;
      }
      bytes_to_come = bytes_to_come - status;
    }
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return iResult;
  } else {
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    bytes_left = bytes_to_come;
    while (bytes_left > 0) {    /* Read the rest of the response */
      status = recv(info_ptr->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        if (status == 0) {
          iResult = EL734__BAD_RECV1;   /* Server exited (probably) */
        } else {
          iResult = EL734__BAD_RECV1_NET;       /* It's some other net fault */
        }
        AsynSrv_Close(&(info_ptr->sAsync), 1);
        info_ptr->skt = 0;
        return iResult;
      }
      bytes_left = bytes_left - status;
      nxt_byte_ptr = nxt_byte_ptr + status;
    }
  }
  /* well, we got data, make it available */
  if (sscanf(info_ptr->from_host.n_rply, "%4d",
             &info_ptr->max_replies) != 1)
    info_ptr->max_replies = 0;
  if (info_ptr->max_replies > 0)
    ptr = (struct RS__RplyStruct_V01B *) info_ptr->from_host.u.rplys;
  info_ptr->n_replies = 1;
  if (ptr) {
    strncpy(pBuffer, ptr->rply, iBufLen);
  } else {
    return NOREPLY;
  }
  return True;
}

/*-------------------------------------------------------------------------*/
int SerialReceiveWithTerm(void **pData, char *pBuffer,
                          int iBufLen, char *cTerm)
{
  struct SerialInfo *info_ptr;
  int status, c_len, size, max_size, ncmnds;
  int bytes_to_come, bytes_left;
  int iResult;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  char *txt_ptr;
  char *cmnd_lst_ptr;
  struct RS__RplyStruct_V01B *ptr = NULL;
  long lMask = 0L;
  struct timeval tmo = { 0, 1 };


  /*
   ** Do nothing if no connection - the connection gets
   ** closed if an error is detected.
   */
  info_ptr = (struct SerialInfo *) *pData;
  if (info_ptr == NULL)
    return NOCONNECTION;
  if (info_ptr->skt == 0) {
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return NOCONNECTION;
  }

  /* try with select if there is data */
/*    lMask = (1 << info_ptr->skt);
    tmo.tv_usec = 10;
    status = select((info_ptr->skt +1), (fd_set *)&lMask, NULL,NULL,&tmo);
    if(status <= 0)
    {
       return SELECTFAIL;
    } 
*/

  /* try read the message length to come */
  size = sizeof(info_ptr->from_host.msg_size);
  status = recv(info_ptr->skt, info_ptr->from_host.msg_size, size, 0);
  if (status != size) {
    if (status > 0) {
      iResult = EL734__BAD_RECV;        /* Server exited (probably) */
    } else if (status == -1) {
      iResult = EL734__BAD_RECV_NET;    /* It's some other net problem */
    } else {
      iResult = EL734__BAD_RECV_NET;
    }
    AsynSrv_Close(&(info_ptr->sAsync), 1);
    info_ptr->skt = 0;
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return iResult;
  }
  if (sscanf(info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) {
    return EL734__BAD_NOT_BCD;  /* Header not an ASCII BCD integer */
  }

  max_size = sizeof(info_ptr->from_host) -
      sizeof(info_ptr->from_host.msg_size);
  if (bytes_to_come > max_size) {
    iResult = EL734__BAD_RECVLEN;
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    while (bytes_to_come > 0) { /* Flush out the incoming message */
      bytes_left = bytes_to_come;
      if (bytes_left > max_size)
        bytes_left = max_size;
      status = recv(info_ptr->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        iResult = EL734__BAD_FLUSH;     /* TCP/IP problem whilst flushing */
        AsynSrv_Close(&(info_ptr->sAsync), 1);
        info_ptr->skt = 0;
        memset(info_ptr->from_host.msg_size,
               '0', sizeof(info_ptr->from_host.msg_size));
        return iResult;
      }
      bytes_to_come = bytes_to_come - status;
    }
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return iResult;
  } else {
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    bytes_left = bytes_to_come;
    while (bytes_left > 0) {    /* Read the rest of the response */
      status = recv(info_ptr->skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        if (status == 0) {
          iResult = EL734__BAD_RECV1;   /* Server exited (probably) */
        } else {
          iResult = EL734__BAD_RECV1_NET;       /* It's some other net fault */
        }
        AsynSrv_Close(&(info_ptr->sAsync), 1);
        info_ptr->skt = 0;
        return iResult;
      }
      bytes_left = bytes_left - status;
      nxt_byte_ptr = nxt_byte_ptr + status;
    }
  }
  /* well, we got data, make it available */
  if (sscanf(info_ptr->from_host.n_rply, "%4d",
             &info_ptr->max_replies) != 1)
    info_ptr->max_replies = 0;
  if (info_ptr->max_replies > 0)
    ptr = (struct RS__RplyStruct_V01B *) info_ptr->from_host.u.rplys;
  info_ptr->n_replies = 1;
  if (ptr) {
    strncpy(pBuffer, ptr->rply, iBufLen);
    *cTerm = ptr->term;
  } else {
    return NOREPLY;
  }
  return True;
}

/*---------------------------------------------------------------------------*/
int SerialError(int iErr, char *pBuffer, int iBufLen)
{
  switch (iErr) {
  case -320:
    strncpy(pBuffer, "Select failed to find data", iBufLen);
    break;
  case -300:
  case NOCONNECTION:
    strncpy(pBuffer, "Not connected", iBufLen);
    break;
  case -301:
    strncpy(pBuffer, "No reply found", iBufLen);
    break;
  case -100:
    strncpy(pBuffer, "No reply found", iBufLen);
    break;
  case EL734__BAD_ADR:
    strncpy(pBuffer, "SERIAL__BAD_ADR", iBufLen);
    break;
  case EL734__BAD_BIND:
    strncpy(pBuffer, "SERIAL__BAD_BIND", iBufLen);
    break;
  case EL734__BAD_CMD:
    strncpy(pBuffer, "SERIAL__BAD_CMD", iBufLen);
    break;
  case EL734__BAD_CONNECT:
    strncpy(pBuffer, "SERIAL__BAD_CONNECT", iBufLen);
    break;
  case EL734__BAD_FLUSH:
    strncpy(pBuffer, "SERIAL__BAD_FLUSH", iBufLen);
    break;
  case EL734__BAD_HOST:
    strncpy(pBuffer, "SERIAL__BAD_HOST", iBufLen);
    break;
  case EL734__BAD_ID:
    strncpy(pBuffer, "SERIAL__BAD_ID", iBufLen);
    break;
  case EL734__BAD_ILLG:
    strncpy(pBuffer, "SERIAL__BAD_ILLG", iBufLen);
    break;
  case EL734__BAD_LOC:
    strncpy(pBuffer, "SERIAL__BAD_LOC", iBufLen);
    break;
  case EL734__BAD_MALLOC:
    strncpy(pBuffer, "SERIAL__BAD_MALLOC", iBufLen);
    break;
  case EL734__BAD_NOT_BCD:
    strncpy(pBuffer, "SERIAL__BAD_NOT_BCD", iBufLen);
    break;
  case EL734__BAD_OFL:
    strncpy(pBuffer, "SERIAL__BAD_OFL", iBufLen);
    break;
  case EL734__BAD_PAR:
    strncpy(pBuffer, "SERIAL__BAD_PAR", iBufLen);
    break;

  case EL734__BAD_RECV:
    strncpy(pBuffer, "SERIAL__BAD_RECV", iBufLen);
    break;
  case EL734__BAD_RECV_NET:
    strncpy(pBuffer, "SERIAL__BAD_RECV_NET", iBufLen);
    break;
  case EL734__BAD_RECV_PIPE:
    strncpy(pBuffer, "SERIAL__BAD_RECV_PIPE", iBufLen);
    break;
  case EL734__BAD_RECV_UNKN:
    strncpy(pBuffer, "SERIAL__BAD_RECV_UNKN", iBufLen);
    break;
  case EL734__BAD_RECVLEN:
    strncpy(pBuffer, "SERIAL__BAD_RECVLEN", iBufLen);
    break;
  case EL734__BAD_RECV1:
    strncpy(pBuffer, "SERIAL__BAD_RECV1", iBufLen);
    break;
  case EL734__BAD_RECV1_NET:
    strncpy(pBuffer, "SERIAL__BAD_RECV1_NET", iBufLen);
    break;
  case EL734__BAD_RECV1_PIPE:
    strncpy(pBuffer, "SERIAL__BAD_RECV1_PIPE", iBufLen);
    break;
  case EL734__BAD_RNG:
    strncpy(pBuffer, "SERIAL__BAD_RNG", iBufLen);
    break;
  case EL734__BAD_SEND:
    strncpy(pBuffer, "SERIAL__BAD_SEND", iBufLen);
    break;
  case EL734__BAD_SEND_PIPE:
    strncpy(pBuffer, "SERIAL__BAD_SEND_PIPE", iBufLen);
    break;
  case EL734__BAD_SEND_NET:
    strncpy(pBuffer, "SERIAL__BAD_SEND_NET", iBufLen);
    break;
  case EL734__BAD_SEND_UNKN:
    strncpy(pBuffer, "SERIAL__BAD_SEND_UNKN", iBufLen);
    break;
  case EL734__BAD_SENDLEN:
    strncpy(pBuffer, "SERIAL__BAD_SENDLEN", iBufLen);
    break;
  case EL734__BAD_SOCKET:
    strncpy(pBuffer, "SERIAL__BAD_SOCKET", iBufLen);
    break;
  case EL734__BAD_TMO:
    strncpy(pBuffer, "SERIAL__BAD_TMO", iBufLen);
    break;
  case EL734__FORCED_CLOSED:
    strncpy(pBuffer, "SERIAL__FORCED_CLOSED", iBufLen);
    break;
  case OPENFAILURE:
    strncpy(pBuffer,
            "FAILED to open connection to serial port server", iBufLen);
    break;
  default:
    strcpy(pBuffer, "Unknown SERIAL error");
    break;
  }
  return 1;
}

/*---------------------------------------------------------------------------*/
int SerialWriteRead(void **pData, char *pCommand,
                    char *pBuffer, int iBufLen)
{

  struct SerialInfo *pInfo = NULL;
  int iRet;
  time_t tTarget, tCurrent;

  pInfo = (struct SerialInfo *) *pData;

  /* write */
  iRet = SerialSend(pData, pCommand);
  if (iRet != 1) {
    SerialError(iRet, pBuffer, iBufLen);
    return iRet;
  }

  /* check for answers for maximum time out */
  tTarget = tCurrent = time(&tCurrent);
  tTarget += pInfo->tmo * 100 - 90;

  while (tCurrent < tTarget) {
    pInfo->pFunc(pInfo->pData, 100);
    iRet = SerialReceive(pData, pBuffer, iBufLen);
    if (iRet != 1) {
      if (iRet != SELECTFAIL) {
        /* error ! */
        SerialError(iRet, pBuffer, iBufLen);
        return iRet;
      }
    } else {
      return 1;                 /* there is data read, we are done */
    }
    tCurrent = time(&tCurrent);
  }
  return TIMEOUT;
}

/*---------------------------------------------------------------------------*/
int SerialNoReply(void **pData, char *pCommand)
{

  struct SerialInfo *pInfo = NULL;
  int iRet, iOld, i;
  char pBuffer[30];

  pInfo = (struct SerialInfo *) *pData;

  iOld = pInfo->tmo;
  pInfo->tmo = 0;

  /* write */
  iRet = SerialSend(pData, pCommand);
  if (iRet != 1) {
    pInfo->tmo = iOld;
    return iRet;
  }

  /* try some time to find a TMO */
  for (i = 0; i < 10; i++) {
    usleep(50);
    SerialReceive(pData, pBuffer, 29);
    if (strcmp(pBuffer, "?TMO") == 0) {
      break;
    }
  }
  if (i > 7) {
    printf("TMO received after %d cycles \n", i);
  }
  pInfo->tmo = iOld;
  return 1;
}

/*-------------------------------------------------------------------------*/
void SetSerialSleep(void **pData, SerialSleep pFun, void *pUserData)
{
  struct SerialInfo *pInfo = NULL;
  int iRet;

  pInfo = (struct SerialInfo *) *pData;
  pInfo->pFunc = pFun;
  pInfo->pData = pUserData;

}
