/**************************************************************************
**
** Name: WSCOM.C
**
** Description: Windows Serial Communication DLL
**
** Status: REVIEWED | TESTED
**
** $Log:   S:/tbird/mt2_186/sds/wscom.c_v  $
** 
**    Rev 1.0   16 Dec 1996 13:46:08   Judy
** Initial revision.
** 
**    Rev 1.0   27 Aug 1996 15:12:12   Judy
** Initial revision.
** 
**    Rev 1.0   14 Jun 1996 13:51:04   Judy
** Initial revision.
** 
**    Rev 1.37   11 Aug 1994 11:12:26   ernie
** Added code to initialize UART before calling OpenComm.  Some PC's
** hang in the OpenComm call if the UART has pending characters or
** other interrupts.  This code looks in system.ini for the COMxBase
** setting, and if not found, uses a default I/O address.  Note tha
** this code uses direct I/O and may not work in later Windows versions.
** 
**    Rev 1.36   16 Mar 1994 09:42:12   tom
** Cleaned up header; upload packet size function; standardized trailer.
** 
**    Rev 1.35   21 Dec 1993 15:33:18   tom
** Added WSComAckRequired.
** 
**    Rev 1.34   27 Jul 1993 16:22:02   ron
** added IDHELP processing to comm port selection dialog (again?)
** 
**    Rev 1.33   13 Jul 1993 09:09:20   ernie
** Increased number of bytes per call to WriteComm() to maximum
** 
**    Rev 1.32   22 Apr 1993 09:41:24   doug
** added flush function for shared data
** 
**    Rev 1.31.1.1   23 Feb 1993 11:56:54   ernie
** Added WSComAckRequired
** 
**    Rev 1.31.1.0   22 Feb 1993 13:38:20   ernie
** No change.
** 
**    Rev 1.31   07 Dec 1992 07:36:58   ernie
** Changed to always activate DTR signal, whether or not it is used for
** handshaking.
** 
**    Rev 1.30   03 Dec 1992 08:16:40   ernie
** Added parameter to WSComConfig to select DTR handshaking (for PwrScope)
** 
**    Rev 1.29   25 Nov 1992 17:22:16   john
** WsComInit now accepts an input parameter telling it what com port to
** connect to.  If it can't connect, it will query the user for the port
** to connect to.  Once the connection is made the port number is passed
** back to the boot up code for saving in the .ini file.
** 
**    Rev 1.28   21 Nov 1992 14:57:26   courtney
** Return appopriate error if user aborts from the com dialog box.
** 
**    Rev 1.27   23 Oct 1992 15:09:20   ernie
** Changed number of characters sent to Windows in each WriteComm call
** back to 1.  Greater than 1 character causes system reboot on SX
** machine for some reason.
** 
**    Rev 1.26   23 Oct 1992 12:55:28   ernie
** 1. Changed WSComWrite error check to look only at errors related
**    to writing, rather than both reading and writing.
** 2. Cleaned up warnings.
** 
**    Rev 1.25   24 Sep 1992 17:23:44   john
** Call to InvalidateRect was indescriminately blanking all windows.
** Since a call to UpdateWindow does not redraw the chunk eaten out by
** the dialog, just accept the missing chunk and wait for the screen to
** be redrawn after communications are established.
** 
**    Rev 1.24   18 Sep 1992 10:02:40   courtney
** Revised baud rate back to 57.6 (now standard).
** 
**    Rev 1.23   01 Sep 1992 14:16:52   ernie
** 1. Vastly simplified read function.  It is now reentrant and stateless.
**    It reads up to the number of bytes specified, but returns as soon
**    as the byte supply in Windows is exhausted.
** 2. Changed MapBadError to MapCommError and changed logic to handle
**    multiple bits set (using priority scheme).
** 
**    Rev 1.21   31 Aug 1992 09:25:50   courtney
** Back out changes so system will come up (needs more investigation):
** streaming reads, break out of read loop if not all bytes received.
** 
**    Rev 1.20   30 Aug 1992 16:35:54   courtney
** Remove unused static data.
** Stream reads (16 bytes).  Put in read semaphore.
** 
**    Rev 1.19   29 Aug 1992 14:24:42   courtney
** Report receive queue overflow as either software or hardware
** error.  Report generic read com port error with Windows hex
** error code.
** 
**    Rev 1.18   28 Aug 1992 18:49:50   courtney
** Back to 38.4K baud for more stable communications.
** 
**    Rev 1.16   27 Aug 1992 14:50:50   courtney
** Broke out general 'read com port' error to be more specific
** as to the true nature of the error (maps windows error codes
** to those contained in string resource).
** 
**    Rev 1.15   25 Aug 1992 11:09:26   courtney
** No timer yet for WSComRead, but returns RETCODE and doesn't popup
** errors here forever.
** 
**    Rev 1.14   25 Aug 1992 10:26:28   mindy
** Timer is no longer associated with a window. 
** 
**    Rev 1.13   14 Aug 1992 10:37:26   courtney
** Verify return code from SetCommState.
** 
**    Rev 1.12   07 Aug 1992 17:25:52   courtney
** Increased baud rate to 57.6K (with corresponding change in firmware),
** added DTR/DSR flow control.
** 
**    Rev 1.11   01 Aug 1992 16:52:00   courtney
** Removed configuration dialog box; added more error checking and
** return codes for open/close operations; use Windows 3.1 CBR_ indices
** for setting baud rate; added error return on timer install; remove
** calls to EscapeComm which do nothing for RS-232; remove state loop
** in CommError (but leave as wrapper function to API call); added 
** #defines for input/output queue ID's.
** 
**    Rev 1.8   24 Jun 1992 08:13:22   doug
** added break in case statement so it does not "slide through" the statement
** 
**    Rev 1.7   14 May 1992 12:29:42   mindy
** added error server registration and removed GetErrorText
** 
**    Rev 1.6   13 May 1992 16:45:58   mindy
** a) changed commenting structure to match tbird
** b) added WSComGetErrorText
** c) moved error numbering in here - no longer need wscomerr.*
** 
**    Rev 1.5   11 May 1992 12:03:54   nghia
** Renamed WSComDisconnect() to WSComClose() to match the network version.-
** 
**    Rev 1.4   25 Mar 1992 15:42:44   courtney
** Revised return type on WSComRead/Write to the number of bytes processed
** (was boolean).  Also, WSComRead is now blocking (except in the case of
** request for a single byte read - which is just a query to see if anything
** is waiting).
** 
**    Rev 1.3   05 Feb 1992 10:41:02   nghia
** Added WSComDisconect() to allow ShareData Server terminate Windows COM port.
** Changed default flow-control of diaglog box to None instead of Xon/xoff.
** 
**    Rev 1.2   22 Jan 1992 08:38:04   mindy
** make 19200 the default baud rate
** 
**    Rev 1.1   21 Jan 1992 10:28:50   mindy
** made changed needed to support an RS232 only version - moving the TCP/IP
** changes into wsnet.c.
** a) moved RS232 only local routine's prototypes from wscom.h to here.
** b) added a dummy version of SetOutputSize to allow common routines like
**    sharedat.c, etc to work in either mode with no changes needed.
** c) removed routines that were never referenced.
** 
**    Rev 1.0   29 Jul 1991 08:53:18   jim
** Initial revision.
**
**
** $Header:   S:/tbird/mt2_186/sds/wscom.c_v   1.0   16 Dec 1996 13:46:08   Judy  $
**
** Copyright (C) 1991 Microtek International, Inc.
**
**************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#ifndef _WINDOWS_
#include "windows.h"
#endif

#ifndef _WSCOM_
#include "wscom.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif

#ifndef _COMERR_
#include "comerr.h"
#endif

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

/* from 3.1 Windows.h !! */
#define CBR_19200    0xFF18
#define CBR_38400    0xFF1B
#define CBR_56000    0xFF1F
#define CBR_128000   0xFF23
/* end 3.1 Windows.h */

/* parameter values for FlushComm API */
#define Q_INPUT     0
#define Q_OUTPUT    1

/* use with Borland C++ compiler */
#ifndef _BORLANDC_
#define _BORLANDC_
#endif

/* List of COM ports may be available */
COM_PORT_TYPE comPort[4] = {
   {UNINIT, "COM1"}, {UNINIT, "COM2"},
   {UNINIT, "COM3"}, {UNINIT, "COM4"},
};

S16 ioBase[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };

/* Global data of DLL */
HANDLE  libInst;                 /* handle of the DLL */
WORD    dlgIndx = COM2;          /* default COM port */
int     checkButton = IDD_COM2;  /* check button of InitCom dialog */
FARPROC lpfnReceiveCallBack;     /* application Receive Callback */
COMSTAT commStat;                /* global communication status */
DCB     comDcb;                  /* global Device Control Block of COM port */
U16     timerId;                 /* timer ID for SdatInput callback */
BOOL    inReadSemaphore=FALSE;   /* already in read when called again */
BOOL    cancelConn=FALSE;        /* user request to cancel connection */

                        /****************************
                         *                          *
                         *    EXTERNAL VARIABLES    *
                         *                          *
                         ****************************/

                        /****************************
                         *                          *
                         *     LOCAL PROTOTYPES     *
                         *                          *
                         ****************************/
BOOL FAR PASCAL WSComInitDlgProc(HWND hWnd, WORD iMsg, WORD wParam,
   LONG lParam);
RETCODE CommError(int curComId, COMSTAT *comStatus);
RETCODE MapCommError(S16 commErr);
RETCODE MakeSerialPortConnection(HWND hWnd, S16 *portNumber, BOOL *InitOk);
RETCODE QueryForSerialPort(HWND hWnd, S16 *dlgIndex, BOOL *InitOk);
BOOL FAR PASCAL WSComClearQ(WORD cId);

                        /****************************
                         *                          *
                         *      EXECUTABLE CODE     *
                         *                          *
                         ****************************/
/**************************************************************************
**
** LibMain
**
** Description: Main entry point of the DLL.
**
** Parameters:
**    input:
**       hInstance:     Handle of the library instance
**       wDataSeg:      Value of Data Segment register
**       cbHeapSize:    Heap Size of the heap defined in the DEF
**       lpszCmdLine:   Command Line information (rarely used)
**
**    output:
**
***************************************************************************/
#pragma argsused
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize,
    LPSTR lpszCmdLine) {
    /* the LIBENTRY initializes the local heap by calling LocalInit() and
       then calls LibMain.  The LocalInit() locks the data segment of the
       library when initialize; therefore LibMain needs to unlock */
    if (cbHeapSize != 0) /* the DLL Data segment is MOVEABLE */
       UnlockData(0);
    libInst = hInstance;  /* Save the handle */
    ErrInitDLL(MODULE_WSCOM,"wscom.dll");
    /* initialize successfully */
    return(TRUE);

}

/**************************************************************************
**
** MakeSerialPortConnection
**
** Description: Attempts to open/establish a connection to the requested 
**              serial port.
**
** Parameters:
**    input:
**       hWnd:          the handle to the PV application
**       portNumber:    the port to open (1=com1, 2=com2,...)
**
**    output:
**       InitOk:        flag to signal port was initialized 
**
***************************************************************************/
#pragma argsused
RETCODE MakeSerialPortConnection(HWND hWnd, S16 *portNumber, BOOL *InitOk) {
   S16 cId;
   COMSTAT tmp;
   RETCODE wserrCode;
   S16 portNum, comBase;
   
   *InitOk = FALSE;
   portNum = *portNumber - 1;
   /* Determine I/O address of uart */
   {
      S8 buf[64];
      S8 basestr[64];
      sprintf(basestr,"COM%1dbase",*portNumber);
      GetPrivateProfileString("386Enh",basestr,"default",buf,sizeof(buf),
         "system.ini");
      if (stricmp(buf,"default") == 0) comBase = ioBase[portNum];
      else comBase = strtoul(buf,NULL,16);
   }
   /* Clear out any possible chars in uart input FIFO */
   inp(comBase+0);inp(comBase+0);   /* Read RHR to guarantee FIFO is empty */
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   inp(comBase+0);inp(comBase+0);
   /* Clear any error conditions and other interrupt sources */
   inp(comBase+5);inp(comBase+5);   /* Reading LSR clears bits */
   inp(comBase+6);inp(comBase+6);   /* Reading MSR clears bits */
   /* Connect COM port */
   if ((cId = OpenComm(comPort[portNum].cName,
      MAX_QSIZE, MAX_QSIZE)) >= 0) {
      comPort[portNum].cId = cId;
      *InitOk = TRUE;
      if (!WSComClearQ(comPort[portNum].cId)) {
         /* cannot clear queues */
         CommError(comPort[portNum].cId, &tmp);
      }
   }
   else {
      /* Error open communication device */
      switch(cId) {
         case IE_BADID:       wserrCode = ER_BADID; break;
         case IE_BAUDRATE:    wserrCode = ER_BAUDRATE; break;
         case IE_BYTESIZE:    wserrCode = ER_BYTESIZE; break;
         case IE_DEFAULT:     wserrCode = ER_DEFAULT; break;
         case IE_HARDWARE:    wserrCode = ER_HARDWARE; break;
         case IE_MEMORY:      wserrCode = ER_OUT_OF_MEMORY; break;
         case IE_NOPEN:       wserrCode = ER_NOPEN; break;
         case IE_OPEN:        wserrCode = ER_OPEN; break;
         default:             wserrCode = ER_UNKNOWN; break;
      }
      ErrDisplayError(wserrCode, FORCE_POPUP);
      return(wserrCode);
   }
   return(GOOD);
}

/**************************************************************************
**
** QueryForSerialPort
**
** Description: Queries user for the serial port the system is connected
**              to and attempts to connect to that port.
**
** Parameters:
**    input:
**       hWnd:       the handle to the PV application
**       dlgIndx:    the port to open (1=com1, 2=com2,...)
**
**    output:
**       InitOk:        flag to signal port was initialized 
**
***************************************************************************/
RETCODE QueryForSerialPort(HWND hWnd, S16 *dlgIndx, BOOL *InitOk) {
   FARPROC lpfnWSComInitDlgProc;
   RETCODE err;
   
   *InitOk = FALSE;
   if ((lpfnWSComInitDlgProc =
      MakeProcInstance(WSComInitDlgProc, libInst)) != NULL) {
      /* Loop through COM ports list to connect */
      while (!*InitOk && DialogBox(libInst,"ComInit",
         hWnd, lpfnWSComInitDlgProc)) {
         /* fill in call to open serial port here */
         err = MakeSerialPortConnection(hWnd,(S16 *) dlgIndx, InitOk);
         if (err == ER_OPEN) { /* device already opened */
            break;
         }
         if (err) {
            if (*dlgIndx == (MAX_COM))
               *dlgIndx = COM1 + 1;  /* the #define for com1 is one */
            /* too small */
            else
               (*dlgIndx)++;  /* check next com port */
            checkButton = *dlgIndx + IDD_COM1 - 1;
         }
      }
      /* release memory */
      FreeProcInstance(lpfnWSComInitDlgProc);
   }
   return(err);
}

#ifndef _BORLANDC_
/**************************************************************************
**
** WEP {Windows Exit Procedure}
**
** Description: DLL exit procedure to perform cleanup for the DLL before it
**              is unloaded. - not to be used with BC++
**
** Parameters:
**    input:
**       nParam:     Message to indicate whether all of Windows is shutting
**                   down or just the DLL itself.
**
**    output:
**       return a 1 to indicate success
**
***************************************************************************/
void FAR PASCAL WEP(int nParam) {

   switch(nParam) {
      case WEP_SYSTEMEXIT:
         /* System shutdown in progress. Respond accordingly */
         break;
      case WEP_FREE_DLL:
         /* The DLL use count is 0 */
         break;
      default:
         /* Undefined value - ignore */
         break;
   }
   return(TRUE);
}

#endif

/**************************************************************************
**
** WSComClearQ
**
** Description: Clear out the specified COM port's I/O queues
**
** Parameters:
**    input:
**       cId            COM port ID
**    output:
**       Success/Fail to flush the queues
**
***************************************************************************/
BOOL FAR PASCAL WSComClearQ(WORD cId) {
   /* Flushes the Input/Output queues */
   if (FlushComm(cId, Q_INPUT) < 0) {
      ErrDisplayError(ER_FLUSH_IN_QUEUE, FORCE_POPUP);
      return(FALSE);
   }
   if (FlushComm(cId, Q_OUTPUT) < 0) {
      ErrDisplayError(ER_FLUSH_OUT_QUEUE, FORCE_POPUP);
      return(FALSE);
   }
   return(TRUE);
}

/**************************************************************************
**
** WSComInitDlgProc
**
** Description: Get the index of COM port to connect
**
** Parameters:
**    input:
**       hDlg          - The window handle for this message
**       iMsg          - The message number
**       wParam        - The WORD parmater for this message
**       lParam        - The LONG parmater for this message
**
**    output:
**       depend on message
**
***************************************************************************/
#pragma argsused
BOOL FAR PASCAL WSComInitDlgProc(HWND hDlg, WORD iMsg, WORD wParam,
   LONG lParam) {

   switch(iMsg) {

      case WM_INITDIALOG:
         CheckRadioButton(hDlg, IDD_COM1, IDD_COM4, checkButton);
         SetFocus(GetDlgItem(hDlg, IDD_CONNECT));
         return(FALSE);

      case WM_COMMAND:
         switch(wParam) {
            case IDD_COM1:
            case IDD_COM2:
            case IDD_COM3:
            case IDD_COM4:
               dlgIndx = wParam - IDD_COM1 + 1; /* Get COM port index */
               checkButton = wParam;
               CheckRadioButton(hDlg, IDD_COM1, IDD_COM4, wParam);
               return(TRUE);

            case IDD_CONNECT:
               EndDialog(hDlg, TRUE);
               return(TRUE);

            case IDD_CANCEL:
               EndDialog(hDlg, FALSE);
#if 1
               cancelConn = TRUE;
               return(FALSE);
#else
               return(TRUE);
#endif
            case IDHELP:
               WinHelp(hDlg, "PWRVIEWS.HLP",
                  HELP_CONTEXT, HE_SDS_WSCOM_1);
               return (FALSE);

            default:
               return(FALSE);
         } /* end of switch(wParam) */
   }
   return(FALSE);
}

/**************************************************************************
**
** WSComInit
**
** Description: Initialize a serial communication port.
**
** Parameters:
**    input:
**       hWnd:        Handle to the application window
**       cIndx:       Long pointer to Index of the selected COM port.
**    output:
**       TRUE/FALSE
**
***************************************************************************/
BOOL FAR PASCAL WSComInit(HWND hWnd, LPWORD cIndx) {
   BOOL InitOk = FALSE;

   dlgIndx = *cIndx; /* work with the copy of the global COM port index */

   /* quiery user for the com port he wishes to connect to only if the */
   /* com port index passed in is zero, otherwise just try to connect */
   if (!dlgIndx) {
      if (libInst != NULL) {
         dlgIndx = COM2 + 1;
         if (QueryForSerialPort(hWnd,(S16 *) &dlgIndx, &InitOk)) 
            return(FALSE);
      }
   } else if (dlgIndx != 0x7fff) {
      /* !!! reserve 0x7fff to be used to indicate an ethernet connection */
      if (MakeSerialPortConnection(hWnd,(S16 *) &dlgIndx, &InitOk)) {
         if (QueryForSerialPort(hWnd,(S16 *) &dlgIndx, &InitOk)) 
            return(FALSE);
      }
   }
   if (cancelConn) {
      cancelConn=FALSE;
      /* return is boolean, but need to indicate to caller that
         user abort was requested */
      *cIndx = -1;
      return (FALSE);
   } else {
      *cIndx = dlgIndx;
      dlgIndx--;
   }
   /* Reset Device Control Block to be reconfigured */
   comDcb.Id = UNINIT;
   return(InitOk);
}

/**************************************************************************
**
** WSComConfig
**
** Description: This function provides the serial communication
**              configuration services.
**
** Parameters:
**    input:
**       hWnd        Handle of the main window
**       cIndx       Index to a COM port ID
**    output:
**
**
***************************************************************************/
#pragma argsused
BOOL FAR PASCAL WSComConfig(HWND hWnd, WORD cIndx, BOOLEAN useDTRHandshake) {
   /* Get DCB state of cIndx */
   if (comPort[cIndx].cId != comDcb.Id) { /* DCB has not been set */
      if (GetCommState(comPort[cIndx].cId, (DCB FAR *) &comDcb) < 0) {
         ErrDisplayError(ER_COMM_STATE, FORCE_POPUP);
         return(FALSE);
      }
      /* Set configuration to default value */
      comDcb.BaudRate = CBR_56000;
      comDcb.ByteSize = 8;
      comDcb.Parity = NOPARITY;
      comDcb.StopBits = ONESTOPBIT;
      comDcb.fBinary = 1;
      comDcb.fDtrDisable = 0;   /* use DTR signal */

      if (useDTRHandshake) {
         comDcb.fOutxDsrFlow = 1;  /* monitor DSR signal for output flow */
         comDcb.fDtrflow = 1;      /* use DTR signal with queue monitoring */
      } else {
         comDcb.fOutxDsrFlow = 0;  /* Do not use DTR or DSR */
         comDcb.fDtrflow = 0;
      }
      comDcb.XonLim = INQ_LIMIT;   /* input q threshold before DTR sent */
      comDcb.XoffLim = OUTQ_LIMIT; /* output q limit before DTR turned off */
   }
   /* Reset the Device Control Block and set it into a new state */
   comDcb.Id = comPort[cIndx].cId;
   if (SetCommState((DCB FAR *) &comDcb) < 0) {
      ErrDisplayError(ER_RESET_DCB_STATE, FORCE_POPUP);
      return(FALSE);
   }
   return(TRUE);
}

/*****************************************************************************
**
** WSComAckRequired
**
** Report that ACK/NAK required for serial comm
**
*****************************************************************************/
BOOL FAR PASCAL WSComAckRequired(VOID) {
    return(TRUE);
}

/**************************************************************************
**
** CommError
**
** Description: Get Communication status of a COM port ID
**
** Parameters:
**    input:
**       curComId    Current com port ID
**       comStatus   Pointer to the Com port status
**
**    output:
**       0 if no error else error code
**
***************************************************************************/
RETCODE CommError(int curComId, COMSTAT *comStatus) {
    return(MapCommError(GetCommError(curComId, (COMSTAT FAR *) comStatus)));
}

/**************************************************************************
**
** WSComRead
**
** Description: This function read numBytes of data from the COM port in
**              a nonblocking mode.
**
** Parameters:
**    input:
**       hWnd           Handle of the Main window.
**       rxBuff         Long pointer to the receiving buffer
**       numBytes       Pointer to number of bytes request to read
**    output:
**       nbytes         Number of bytes read in
**
***************************************************************************/
RETCODE FAR PASCAL WSComRead(U8 FAR *rxBuff, U16 FAR *numBytes) {
   S16 num;
   if ((num = ReadComm(comPort[dlgIndx].cId, (LPSTR)rxBuff, *numBytes)) >= 0)
      *numBytes = (U16)num;
   return(CommError(comPort[dlgIndx].cId,NULL));
}

/**************************************************************************
**
** WSComWrite
**
** Description: This function send numBytes of data to the COM port.
**
** Parameters:
**    input:
**       txBuff         Long pointer to the transmitting buffer
**       numBytes       Number of bytes in the transmitting buffer
**    output:
**       numBytes       Number of bytes written
**
***************************************************************************/
RETCODE FAR PASCAL WSComWrite(U8 FAR *txBuff, U16 *numBytes) {
   RETCODE err;
   S16 actualSent;
   U16 bytesSent;
   for (bytesSent=0; bytesSent < *numBytes; bytesSent+=abs(actualSent)) {
      do {
         err = CommError(comPort[dlgIndx].cId, &commStat);
      } while (err == ER_COM_TXFULL);
      err = GOOD;   /* No other errors pertain to transmission */
      actualSent = min(*numBytes-bytesSent, (MAX_QSIZE - commStat.cbOutQue));
      actualSent = WriteComm(comPort[dlgIndx].cId, (LPSTR)txBuff,actualSent);
      txBuff += abs(actualSent);
      if (actualSent < 0)         /* Clear error by reading error in driver */
         CommError(comPort[dlgIndx].cId, &commStat);
   }
   return(GOOD);
}

/**************************************************************************
**
** WSComRecvTimer
**
** Description:  Function to setup the timer to read data from COM port
**
**
** Parameters:
**    input:
**       timeEllapse    Timer interval to ellapse
**       lpfnAppRecv    Long pointer to a Callback function of the application
**                      to process the received data.
**    output:
**       none
**
***************************************************************************/
BOOL FAR PASCAL WSComRecvTimer(WORD timeEllapse,  FARPROC lpfnAppRecv)
{
   /* Set timer to run and register callback function to receive data */
   if (!(timerId=SetTimer(NULL, 0, timeEllapse, lpfnAppRecv))) {
      ErrDisplayError(ER_NO_TIMER_AVAIL, FORCE_POPUP);
      return(FALSE);
   }
   return(TRUE);
}

/**************************************************************************
**
** WSComClose
**
** Description:  Close the connection of the COM port ID
**
** Parameters:
**    input:
**       none
**
**    output:
**       GOOD if no error - error code
**
***************************************************************************/
BOOL FAR PASCAL WSComClose(VOID) {
   COMSTAT tmp;

   KillTimer(NULL,timerId);
   /* Check COM port ID then disconnect */
   if (comPort[dlgIndx].cId != UNINIT) {
      /* Error if result < 0 */
      if (CloseComm(comPort[dlgIndx].cId) < 0) {
         /* Reset to clean up then try again */
         CommError(comPort[dlgIndx].cId, &tmp);
         return (FALSE);
      }
      return(TRUE);
   }
   return(FALSE);
}

/*
** MapCommError
** Convert error returned from GetCommError to Tbird error number
*/
RETCODE MapCommError(S16 commErr) {
   if (commErr == 0) return(GOOD);
   else if (commErr & CE_OVERRUN) return(ER_COM_HWRCV);    /* uart overrun */
   else if (commErr & CE_RXOVER ) return(ER_COM_RCVOVER);  /* rcv queue full*/
   else if (commErr & CE_DSRTO  ) return(ER_COM_TIMEDOUT); /* dsr timeout */
   else if (commErr & CE_TXFULL ) return(ER_COM_TXFULL);   /* xmt queue full*/
   else if (commErr & CE_CTSTO  ) return(ER_COM_TIMEDOUT); /* dsr timeout */
   else if (commErr & CE_RLSDTO ) return(ER_COM_TIMEDOUT); /* dsr timeout */
   else if (commErr & CE_FRAME  ) return(ER_COM_FRAME);    /* framing error */
   else return(GOOD);     /* Other errors impossible or benign */
}

/**************************************************************************
**
** WSComFlush
**
***************************************************************************/
RETCODE FAR PASCAL WSComFlush(VOID) {
   
   WSComClearQ(comPort[dlgIndx].cId); /* error reported by routine */
   return(GOOD);
}

/****************************************************************************
**
** WSComUploadPacketSize
**
*****************************************************************************/
RETCODE FAR PASCAL WSComUploadPacketSize(U32 *packetSize)
{
   *packetSize = 256L;
   return(GOOD);
}

/******************************** E O F ***********************************/
