/*--------------------------------------------------------------------*/
/*    u l i b o s 2 . c                                               */
/*                                                                    */
/*    OS/2 serial port support for UUCICO                             */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*    Changes Copyright (c) 1989-1993 by Kendra Electronic            */
/*    Wonderworks.                                                    */
/*                                                                    */
/*    All rights reserved except those explicitly granted by the      */
/*    UUPC/extended license agreement.                                */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                          RCS Information                           */
/*--------------------------------------------------------------------*/

/*
 *       $Id: ulibos2.c 1.23 1993/10/03 22:09:09 ahd Exp $
 *       $Log: ulibos2.c $
 * Revision 1.23  1993/10/03  22:09:09  ahd
 * Use unsigned long to display speed
 *
 * Revision 1.22  1993/10/01  01:17:44  ahd
 * Drop unneeded special case for port in use message
 *
 * Revision 1.21  1993/09/29  05:25:21  ahd
 * Don't die after duplicate port close
 *
 * Revision 1.20  1993/09/29  04:52:03  ahd
 * Use unique handler for port suspending
 *
 * Revision 1.19  1993/09/27  04:04:06  ahd
 * Normalize references to modem speed to avoid incorrect displays
 *
 * Revision 1.18  1993/09/25  03:07:56  ahd
 * Convert to standard OS/2 error message call
 *
 * Revision 1.17  1993/09/24  03:43:27  ahd
 * Use OS/2 error message routine
 *
 * Revision 1.16  1993/09/21  01:42:13  ahd
 * Use standard MAXPACK limit for save buffer size
 *
 * Revision 1.15  1993/09/20  04:46:34  ahd
 * OS/2 2.x support (BC++ 1.0 support)
 * TCP/IP support from Dave Watt
 * 't' protocol support
 *
 * Revision 1.14  1993/05/30  15:25:50  ahd
 * Multiple driver support
 *
 * Revision 1.13  1993/05/30  00:08:03  ahd
 * Multiple communications driver support
 * Delete trace functions
 *
 * Revision 1.12  1993/05/09  03:41:47  ahd
 * Make swrite accept constant input strings
 *
 * Revision 1.11  1993/04/11  00:34:11  ahd
 * Global edits for year, TEXT, etc.
 *
 * Revision 1.10  1993/04/10  21:25:16  dmwatt
 * Add Windows/NT support
 *
 * Revision 1.9  1993/04/05  04:32:19  ahd
 * Additional traps for modem dropping out
 *
 * Revision 1.8  1993/04/04  04:57:01  ahd
 * Add configurable OS/2 priority values
 *
 * Revision 1.7  1992/12/30  13:02:55  dmwatt
 * Dual path for Windows/NT and OS/2
 *
 * Revision 1.6  1992/12/11  12:45:11  ahd
 * Correct RTS handshake
 *
 * Revision 1.5  1992/12/04  01:00:27  ahd
 * Add copyright message, reblock other comments
 *
 * Revision 1.4  1992/11/29  22:09:10  ahd
 * Add new define for BC++ OS/2 build
 *
 * Revision 1.3  1992/11/19  03:00:39  ahd
 * drop rcsid
 *
 * Revision 1.2  1992/11/15  20:11:48  ahd
 * Add English display of modem status and error bits
 *
 */

/*--------------------------------------------------------------------*/
/*                        System include files                        */
/*--------------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

/*--------------------------------------------------------------------*/
/*                         OS/2 include files                         */
/*--------------------------------------------------------------------*/

#define INCL_DOSDEVIOCTL
#define INCL_BASE
#define INCL_NOPMAPI

#include <os2.h>
#include <limits.h>

#ifndef __OS2__
typedef USHORT APIRET ;  // Define older API return type
#endif

/*--------------------------------------------------------------------*/
/*                    UUPC/extended include files                     */
/*--------------------------------------------------------------------*/

#include "lib.h"
#include "ulib.h"
#include "ssleep.h"
#include "catcher.h"
#include "pos2err.h"

#include "commlib.h"
#include "usrcatch.h"

/*--------------------------------------------------------------------*/
/*                          Global variables                          */
/*--------------------------------------------------------------------*/

currentfile();

static boolean   carrierDetect = FALSE;  /* Modem is not connected     */

static boolean hangupNeeded = FALSE;

static unsigned short currentSpeed = 0;

#define FAR_NULL ((PVOID) 0L)

/*--------------------------------------------------------------------*/
/*           Definitions of control structures for DOS API            */
/*--------------------------------------------------------------------*/

static HFILE com_handle;
static struct _LINECONTROL com_attrib;
static struct _MODEMSTATUS com_signals;
static struct _DCBINFO com_dcbinfo;

static void ShowError( const USHORT status );

static void ShowModem( const BYTE status );

/*--------------------------------------------------------------------*/
/*    n o p e n l i n e                                               */
/*                                                                    */
/*    Open the serial port for I/O                                    */
/*--------------------------------------------------------------------*/

int nopenline(char *name, BPS baud, const boolean direct )
{

   APIRET rc;
   USHORT com_error;

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;

   ULONG action;

#else
   USHORT action;
#endif

   if (portActive)               /* Was the port already active?    */
      closeline();               /* Yes --> Shutdown it before open */

#ifdef UDEBUG
   printmsg(15, "nopenline: %s, %lu", name, baud);
#endif

/*--------------------------------------------------------------------*/
/*                      Validate the port format                      */
/*--------------------------------------------------------------------*/

   if (!equaln(name, "COM", 3 ))
   {
      printmsg(0,"nopenline: Communications port begin with COM, was %s",
         name);
      panic();
   }

/*--------------------------------------------------------------------*/
/*                          Perform the open                          */
/*--------------------------------------------------------------------*/

   rc = DosOpen( name,
                 &com_handle,
                 &action,
                 0L,
                 0 ,
                 FILE_OPEN ,
                 OPEN_FLAGS_FAIL_ON_ERROR |
                 OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE, 0L );

/*--------------------------------------------------------------------*/
/*    Check the open worked.  We translation the common obvious       */
/*    error of file in use to english, for all other errors are we    */
/*    report the raw error code.                                      */
/*--------------------------------------------------------------------*/

   if ( rc )
   {
      printOS2error( name, rc );
      return TRUE;
   }

/*--------------------------------------------------------------------*/
/*            Reset any errors on the communications port             */
/*--------------------------------------------------------------------*/

#ifdef __OS2__

   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_error);
   rc = DosDevIOCtl( com_handle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
      NULL,0L,&ParmLengthInOut,(PVOID) &com_error,sizeof(com_error),
      &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error, FAR_NULL, ASYNC_GETCOMMERROR ,
                     IOCTL_ASYNC, com_handle);

#endif

   if (rc)
   {
      printmsg(0,
            "nopenline: Unable to read errors for %s, error bits %x",
               name, (int) com_error );
      printOS2error( "DosDevIOCtl", rc );
   } /*if */
   else if ( com_error )
      ShowError( com_error );

/*--------------------------------------------------------------------*/
/*                           Set baud rate                            */
/*--------------------------------------------------------------------*/

   SIOSpeed(baud);

/*--------------------------------------------------------------------*/
/*                        Set line attributes                         */
/*--------------------------------------------------------------------*/

#ifdef UDEBUG
   printmsg(15,"nopenline: Getting attributes");
#endif

#ifdef __OS2__

   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_attrib);
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_GETLINECTRL,
                     NULL,
                     0L,
                     &ParmLengthInOut,
                     (PVOID) &com_attrib,
                     sizeof(com_attrib),
                     &DataLengthInOut); // Get old attributes from device

#else

   rc = DosDevIOCtl( &com_attrib,
                     FAR_NULL,
                     ASYNC_GETLINECTRL,
                     IOCTL_ASYNC,
                     com_handle);   // Get old attributes from device
#endif

   if (rc)
   {
      printmsg(0,"nopenline: Unable to get line attributes for %s",name);
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

   com_attrib.bDataBits = 0x08; /* Use eight bit path for data      */
   com_attrib.bParity   = 0x00; /* No parity                        */
   com_attrib.bStopBits = 0x00; /* 1 Stop Bit                       */

#ifdef UDEBUG
   printmsg(15,"nopenline: Setting attributes");
#endif

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_attrib);
   DataLengthInOut = 0;
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETLINECTRL,
                     (PVOID) &com_attrib,
                     sizeof(com_attrib),
                     &ParmLengthInOut,
                     NULL,
                     0L,
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( FAR_NULL,
                     &com_attrib,
                     ASYNC_SETLINECTRL,
                     IOCTL_ASYNC,
                     com_handle);
#endif

   if (rc)
   {
      printmsg(0,"nopenline: Unable to set line attributes for %s",name);
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

/*--------------------------------------------------------------------*/
/*       Disable software (XON/XOFF) flow control and enable          */
/*       hardware (CTS) for flow control                              */
/*--------------------------------------------------------------------*/

#ifdef UDEBUG
   printmsg(15,"nopenline: Getting flow control information");
#endif

#ifdef __OS2__

   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_dcbinfo);
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_GETDCBINFO,
                     NULL,
                     0L,
                     &ParmLengthInOut,
                     (PVOID) &com_dcbinfo,
                     sizeof(com_dcbinfo),
                     &DataLengthInOut);   // Get old attributes from device

#else

   rc = DosDevIOCtl( &com_dcbinfo,
                     FAR_NULL,
                     ASYNC_GETDCBINFO,
                     IOCTL_ASYNC,
                     com_handle);    // Get old attributes from device

#endif

   if (rc)
   {
      printmsg(0,"nopenline: Unable to get line attributes for %s",name);
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

   com_dcbinfo.usWriteTimeout = 2999;  /* Write timeout 30 seconds   */
   com_dcbinfo.usReadTimeout = 24;     /* Read timeout .25 seconds   */
   com_dcbinfo.fbCtlHndShake = (BYTE)
                               (direct ? 0 : MODE_CTS_HANDSHAKE);
   com_dcbinfo.fbFlowReplace = 0;
                                       /* Unless rquested            */
   com_dcbinfo.fbTimeout = MODE_READ_TIMEOUT | MODE_NO_WRITE_TIMEOUT;

#ifdef UDEBUG
   printmsg(15,"nopenline: Setting dcb information");
#endif

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_dcbinfo);
   DataLengthInOut = 0;
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETDCBINFO,
                     (PVOID) &com_dcbinfo,
                     sizeof(com_dcbinfo),
                     &ParmLengthInOut,
                     NULL,
                     0L,
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( FAR_NULL,
                     &com_dcbinfo,
                     ASYNC_SETDCBINFO,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if ( rc )
   {
      printmsg(0,"nopenline: Unable to set flow control for %s",name);
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

/*--------------------------------------------------------------------*/
/*                     Raise Data Terminal Ready                      */
/*--------------------------------------------------------------------*/

   com_signals.fbModemOn = DTR_ON | RTS_ON ;
   com_signals.fbModemOff = 0xff;

#ifdef UDEBUG
   printmsg(15,"nopenline: Raising RTS/DTR");
#endif

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_signals);
   DataLengthInOut = sizeof(com_error);

   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETMODEMCTRL,
                     (PVOID)&com_signals,
                     sizeof(com_signals),
                     &ParmLengthInOut,
                     (PVOID) &com_error,
                     sizeof(com_error),
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error,
                     &com_signals,
                     ASYNC_SETMODEMCTRL,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if (rc)
   {
      printmsg(0,
            "nopenline: Unable to raise DTR/RTS for %s, error bits %#x",
                  name, (int) com_error );
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

   traceStart( name );     // Enable logging

   portActive = TRUE;     /* record status for error handler        */
   carrierDetect = FALSE;  /* Modem is not connected                 */

/*--------------------------------------------------------------------*/
/*                     Wait for port to stablize                      */
/*--------------------------------------------------------------------*/

   ddelay(500);            /* Allow port to stablize          */
   return 0;

} /*nopenline*/

/*--------------------------------------------------------------------*/
/*    n s r e a d                                                     */
/*                                                                    */
/*    Read from the serial port                                       */
/*                                                                    */
/*   Non-blocking read essential to "g" protocol.  See "dcpgpkt.c"    */
/*   for description.                                                 */
/*                                                                    */
/*   This all changes in a multi-tasking system.  Requests for I/O    */
/*   should get queued and an event flag given.  Then the             */
/*   requesting process (e.g. gmachine()) waits for the event flag    */
/*   to fire processing either a read or a write.  Could be           */
/*   implemented on VAX/VMS or DG but not MS-DOS.                     */
/*                                                                    */
/*    OS/2 we could multitask, but we just let the system provide     */
/*    a timeout for us with very little CPU usage.                    */
/*--------------------------------------------------------------------*/

unsigned int nsread(char *output, unsigned int wanted, unsigned int timeout)
{
   APIRET rc;
   static char save[MAXPACK];
   static USHORT bufsize = 0;
   time_t stop_time ;
   time_t now ;
   USHORT com_error;

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif

/*--------------------------------------------------------------------*/
/*           Determine if our internal buffer has the data            */
/*--------------------------------------------------------------------*/

   if (bufsize >= wanted)
   {
      memmove( output, save, wanted );
      bufsize -= wanted;
      if ( bufsize )          /* Any data left over?                 */
         memmove( save, &save[wanted], bufsize );  /* Yes --> Save it*/
      return wanted + bufsize;
   } /* if */

/*--------------------------------------------------------------------*/
/*            Reset any errors on the communications port             */
/*--------------------------------------------------------------------*/

#ifdef __OS2__

   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_error);
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_GETCOMMERROR,
                     NULL,
                     0L,
                     &ParmLengthInOut,
                     (PVOID) &com_error,
                     sizeof(com_error),
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error,
                     FAR_NULL,
                     ASYNC_GETCOMMERROR ,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if (rc )
   {
      printmsg(0,"nsread: Unable to read port errors");
      printOS2error( "DosDevIOCtl", rc );
   } /*if */
   else if ( com_error )
      ShowError( com_error );

/*--------------------------------------------------------------------*/
/*                 Determine when to stop processing                  */
/*--------------------------------------------------------------------*/

   if ( timeout == 0 )
   {
      stop_time = 0;
      now = 1;                /* Any number greater than stop time   */
   }
   else {
      time( & now );
      stop_time = now + timeout;
   }

/*--------------------------------------------------------------------*/
/*            Try to read any needed data into the buffer             */
/*--------------------------------------------------------------------*/

   do {
      USHORT needed =  (USHORT) wanted - bufsize;

      USHORT port_timeout;

#ifdef __OS2__
      ULONG received = 0;
#else
      USHORT received = 0;
#endif

/*--------------------------------------------------------------------*/
/*                     Handle an aborted program                      */
/*--------------------------------------------------------------------*/

      if ( raised )
         return 0;

      if ( terminate_processing )
      {
         static boolean recurse = FALSE;
         if ( ! recurse )
         {
            printmsg(2,"nsread: User aborted processing");
            recurse = TRUE;
         }
         return 0;
      }

/*--------------------------------------------------------------------*/
/*           Compute a new timeout for the read, if needed            */
/*--------------------------------------------------------------------*/

      if (stop_time > now )
      {
         port_timeout = (USHORT) (stop_time - now) / needed * 100;
         if (port_timeout < 100)
            port_timeout = 100;
      }
      else
         port_timeout = 0;

      if ( port_timeout != com_dcbinfo.usReadTimeout )
      {
         com_dcbinfo.usReadTimeout = port_timeout;

#ifdef __OS2__

         ParmLengthInOut = sizeof(com_dcbinfo);
         DataLengthInOut = 0;
         rc = DosDevIOCtl( com_handle,
                           IOCTL_ASYNC,
                           ASYNC_SETDCBINFO,
                           (PVOID) &com_dcbinfo,
                           sizeof(com_dcbinfo),
                           &ParmLengthInOut,
                           NULL,
                           0L,
                           &DataLengthInOut);

#else

         rc = DosDevIOCtl(FAR_NULL,
                          &com_dcbinfo,
                          ASYNC_SETDCBINFO,
                          IOCTL_ASYNC,
                          com_handle);

#endif
         if ( rc )
         {
            printmsg(0,"nsread: Unable to set timeout for comm port");
            printOS2error( "DosDevIOCtl", rc );
            panic();
         } /* if */
      } /* if */

#ifdef UDEBUG
      printmsg(15,"nsread: Port time out is %ud seconds/100",
               port_timeout);
#endif

/*--------------------------------------------------------------------*/
/*                 Read the data from the serial port                 */
/*--------------------------------------------------------------------*/

      rc = DosRead( com_handle, &save[bufsize], needed, &received );

      if ( rc == ERROR_INTERRUPT)
      {
         printmsg(2,"Read Interrupted");
         return 0;
      }
      else if ( rc != 0 )
      {
         printmsg(0,"nsread: Read from comm port for %d bytes failed.",
                  needed);
         printOS2error( "DosRead", rc );
         bufsize = 0;
         return 0;
      }

#ifdef UDEBUG
      printmsg(15,"nsread: Want %d characters, received %d, total %d in buffer",
            (int) wanted, (int) received, (int) bufsize + received);
#endif

/*--------------------------------------------------------------------*/
/*                    Log the newly received data                     */
/*--------------------------------------------------------------------*/

      traceData( &save[bufsize], received, FALSE );

/*--------------------------------------------------------------------*/
/*            If we got the data, return it to the caller             */
/*--------------------------------------------------------------------*/

      bufsize += received;
      if ( bufsize == wanted )
      {
         memmove( output, save, bufsize);
         bufsize = 0;

         if (debuglevel > 14)
            fwrite(output,1,bufsize,stdout);

         return wanted;
      } /* if */

/*--------------------------------------------------------------------*/
/*                 Update the clock for the next pass                 */
/*--------------------------------------------------------------------*/

      if (stop_time > 0)
         time( &now );

   } while (stop_time > now);

/*--------------------------------------------------------------------*/
/*         We don't have enough data; report what we do have          */
/*--------------------------------------------------------------------*/

   return bufsize;

} /*nsread*/

/*--------------------------------------------------------------------*/
/*    n s w r i t e                                                   */
/*                                                                    */
/*    Write to the serial port                                        */
/*--------------------------------------------------------------------*/

int nswrite(const char *input, unsigned int len)
{

   char *data = (char *) input;

#ifdef __OS2__
    ULONG bytes;
#else
   size_t bytes;
#endif

   APIRET rc;

   hangupNeeded = TRUE;      /* Flag that the port is now dirty  */

/*--------------------------------------------------------------------*/
/*         Write the data out as the queue becomes available          */
/*--------------------------------------------------------------------*/

   rc = DosWrite( com_handle, data , len, &bytes);
   if (rc)
   {
      printOS2error( "DosWrite", rc );
      return bytes;
   } /*if */

/*--------------------------------------------------------------------*/
/*                        Log the data written                        */
/*--------------------------------------------------------------------*/

   traceData( data, len, TRUE);

/*--------------------------------------------------------------------*/
/*            Return bytes written to the port to the caller          */
/*--------------------------------------------------------------------*/

   return len;

} /* nswrite */

/*--------------------------------------------------------------------*/
/*    n s s e n d b r k                                               */
/*                                                                    */
/*    send a break signal out the serial port                         */
/*--------------------------------------------------------------------*/

void nssendbrk(unsigned int duration)
{

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif

   USHORT com_error;

#ifdef UDEBUG
   printmsg(12, "ssendbrk: %d", duration);
#endif

#ifdef __OS2__

   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_error);
   DosDevIOCtl( com_handle,
                IOCTL_ASYNC,
                ASYNC_SETBREAKON,
                NULL,
                0L,
                &ParmLengthInOut,
                (PVOID) &com_error,
                sizeof(com_error),
                &DataLengthInOut);

#else

   DosDevIOCtl( &com_error,
                FAR_NULL,
                ASYNC_SETBREAKON,
                IOCTL_ASYNC,
                com_handle);

#endif

   if ( com_error )
      ShowError( com_error );

   ddelay( duration == 0 ? 200 : duration);

#ifdef __OS2__
   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(com_error);
   DosDevIOCtl( com_handle,
                IOCTL_ASYNC,
                ASYNC_SETBREAKOFF,
                NULL,
                0L,
                &ParmLengthInOut,
                (PVOID) &com_error,
                sizeof(com_error),
                &DataLengthInOut);

#else
   DosDevIOCtl( &com_error,
                FAR_NULL,
                ASYNC_SETBREAKOFF,
                IOCTL_ASYNC,
                com_handle);
#endif

   if ( com_error )
      ShowError( com_error );

} /*nssendbrk*/

/*--------------------------------------------------------------------*/
/*    n c l o s e l i n e                                             */
/*                                                                    */
/*    Close the serial port down                                      */
/*--------------------------------------------------------------------*/

void ncloseline(void)
{
   APIRET rc;
   USHORT com_error;

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif


   if ( ! portActive )
   {
      printmsg(0,"ncloseline: Internal error, port already closed");
      return;
   }

   portActive = FALSE; /* flag port closed for error handler  */
   hangupNeeded = FALSE;  /* Don't fiddle with port any more  */

/*--------------------------------------------------------------------*/
/*                             Lower DTR                              */
/*--------------------------------------------------------------------*/

   com_signals.fbModemOn  = 0x00;
   com_signals.fbModemOff = DTR_OFF | RTS_OFF;

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_signals);
   DataLengthInOut = sizeof(com_error);

   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETMODEMCTRL,
                     (PVOID)&com_signals,
                     sizeof(com_signals),
                     &ParmLengthInOut,
                     (PVOID) &com_error,
                     sizeof(com_error),
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error,
                     &com_signals,
                     ASYNC_SETMODEMCTRL,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if ( rc )
   {
      printmsg(0,"ncloseline: Unable to lower DTR/RTS for port");
      printOS2error( "DosDevIOCtl", rc );
   }
   else if ( com_error )
         ShowError( com_error );

/*--------------------------------------------------------------------*/
/*                      Actually close the port                       */
/*--------------------------------------------------------------------*/

   rc = DosClose( com_handle );

   if ( rc != 0 )
      printOS2error( "DosClose", rc );

/*--------------------------------------------------------------------*/
/*                   Stop logging the data to disk                    */
/*--------------------------------------------------------------------*/

   traceStop();

} /* ncloseline */

/*--------------------------------------------------------------------*/
/*    n h a n g u p                                                   */
/*                                                                    */
/*    Hangup the telephone by dropping DTR.  Works with HAYES and     */
/*    many compatibles.                                               */
/*    14 May 89 Drew Derbyshire                                       */
/*--------------------------------------------------------------------*/

void nhangup( void )
{

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif

   USHORT com_error;
   APIRET rc;

   if (!hangupNeeded)
      return;

   hangupNeeded = FALSE;

/*--------------------------------------------------------------------*/
/*                              Drop DTR                              */
/*--------------------------------------------------------------------*/

   com_signals.fbModemOn  = 0x00;
   com_signals.fbModemOff = DTR_OFF;

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_signals);
   DataLengthInOut = sizeof(com_error);

   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETMODEMCTRL,
                     (PVOID)&com_signals,
                     sizeof(com_signals),
                     &ParmLengthInOut,
                     (PVOID) &com_error,
                     sizeof(com_error),
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error,
                     &com_signals,
                     ASYNC_SETMODEMCTRL,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if ( rc )
   {
      printmsg(0,"hangup: Unable to lower DTR for comm port");
      printOS2error( "DosDevIOCtl", rc );
   } /*if */
   else if ( com_error )
         ShowError( com_error );

/*--------------------------------------------------------------------*/
/*                  Wait for the telephone to hangup                  */
/*--------------------------------------------------------------------*/

   printmsg(3,"hangup: Dropped DTR");
   carrierDetect = FALSE;  /* Modem is not connected                 */
   ddelay(500);            /* Really only need 250 milliseconds         */

/*--------------------------------------------------------------------*/
/*                          Bring DTR back up                         */
/*--------------------------------------------------------------------*/

   com_signals.fbModemOn = DTR_ON;
   com_signals.fbModemOff = 0xff;

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_signals);
   DataLengthInOut = sizeof(com_error);

   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
                     (PVOID)&com_signals,
                     sizeof(com_signals),
                     &ParmLengthInOut,
                     (PVOID) &com_error,
                     sizeof(com_error),
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( &com_error,
                     &com_signals,
                     ASYNC_SETMODEMCTRL,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if ( rc )
   {
      printmsg(0,"hangup: Unable to raise DTR for comm port");
      printOS2error( "DosDevIOCtl", rc );
   } /*if */
   else if ( com_error )
         ShowError( com_error );

   ddelay(2000);           /* Now wait for the poor thing to recover    */

} /* nhangup */

/*--------------------------------------------------------------------*/
/*    n S I O S p e e d                                               */
/*                                                                    */
/*    Re-specify the speed of an opened serial port                   */
/*                                                                    */
/*    Dropped the DTR off/on calls because this makes a Hayes drop    */
/*    the line if configured properly, and we don't want the modem    */
/*    to drop the phone on the floor if we are performing             */
/*    autobaud.                                                       */
/*                                                                    */
/*    (Configured properly = standard method of making a Hayes        */
/*    hang up the telephone, especially when you can't get it into    */
/*    command state because it is at the wrong speed or whatever.)    */
/*--------------------------------------------------------------------*/

void nSIOSpeed(BPS baud)
{
   APIRET rc;

#ifdef __OS2__

   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;

   struct
   {
      ULONG baud;       // this structure is needed to set the extended
      BYTE fraction;    // baud rate using function 41h DosDevIOCtl
   } com_baud;

#else

   USHORT speed = (USHORT) baud;

#endif

#ifdef UDEBUG
   printmsg(15,"SIOSpeed: Setting baud rate to %lu",
               (unsigned long) baud);
#endif

#ifdef __OS2__

/*--------------------------------------------------------------------*/
/*       OS/2 2.x Format of call for DosDevIOCtl accepts baud         */
/*       rates greater than 19200.                                    */
/*--------------------------------------------------------------------*/

   com_baud.baud = baud;
   com_baud.fraction = 0;
   ParmLengthInOut = sizeof(com_baud);
   DataLengthInOut = 0;

   rc = DosDevIOCtl( com_handle,
                    IOCTL_ASYNC,
                    ASYNC_SETBAUDRATE,
                    (PVOID) &com_baud,
                    sizeof(com_baud),
                    &ParmLengthInOut,
                    NULL,
                    0L,
                    &DataLengthInOut);

#else
   rc = DosDevIOCtl( FAR_NULL,
                     &speed,
                     ASYNC_SETBAUDRATE,
                     IOCTL_ASYNC,
                     com_handle);
#endif

   if (rc)
   {
      printmsg(0,"SIOSPeed: Unable to set baud rate for port to %lu",
               baud);
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

   currentSpeed = (unsigned short) baud;

} /* nSIOSpeed */

/*--------------------------------------------------------------------*/
/*    n f l o w c o n t r o l                                         */
/*                                                                    */
/*    Enable/Disable in band (XON/XOFF) flow control                  */
/*--------------------------------------------------------------------*/

void nflowcontrol( boolean flow )
{
   APIRET rc;

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif

   if ( flow )
       com_dcbinfo.fbFlowReplace = (char)
            (com_dcbinfo.fbFlowReplace |
            (MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE));
   else
      com_dcbinfo.fbFlowReplace = (char)
            (com_dcbinfo.fbFlowReplace &
            (0xff - MODE_AUTO_TRANSMIT - MODE_AUTO_RECEIVE));

#ifdef __OS2__

   ParmLengthInOut = sizeof(com_dcbinfo);
   DataLengthInOut = 0;
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_SETDCBINFO,
                     (PVOID) &com_dcbinfo,
                     sizeof(com_dcbinfo),
                     &ParmLengthInOut,
                     NULL,
                     0L,
                     &DataLengthInOut);

#else

   rc = DosDevIOCtl( FAR_NULL,
                     &com_dcbinfo,
                     ASYNC_SETDCBINFO,
                     IOCTL_ASYNC,
                     com_handle);

#endif

   if ( rc )
   {
      printmsg(0,"flowcontrol: Unable to set flow control");
      printOS2error( "DosDevIOCtl", rc );
      panic();
   } /*if */

} /* nflowcontrol */

/*--------------------------------------------------------------------*/
/*    n G e t S p e e d                                               */
/*                                                                    */
/*    Report current speed of communications connection               */
/*--------------------------------------------------------------------*/

BPS nGetSpeed( void )
{
   return currentSpeed;
} /* nGetSpeed */

/*--------------------------------------------------------------------*/
/*   n C D                                                            */
/*                                                                    */
/*   Return status of carrier detect                                  */
/*--------------------------------------------------------------------*/

boolean nCD( void )
{
   boolean previousCarrierDetect = carrierDetect;
   APIRET rc;

#ifdef __OS2__
   ULONG ParmLengthInOut;
   ULONG DataLengthInOut;
#endif

   BYTE status;
   static BYTE oldstatus = (BYTE) 0xDEAD;

#ifdef __OS2__
   ParmLengthInOut = 0;
   DataLengthInOut = sizeof(status);
   rc = DosDevIOCtl( com_handle,
                     IOCTL_ASYNC,
                     ASYNC_GETMODEMINPUT,
                     NULL,
                     0L,
                     &ParmLengthInOut,
                     (PVOID) &status,
                     sizeof(status),
                     &DataLengthInOut);
#else

   rc = DosDevIOCtl( &status,
                     0L,
                     ASYNC_GETMODEMINPUT,
                     IOCTL_ASYNC,
                     com_handle );

#endif

   if ( rc )
   {
      printmsg(0,"CD: Unable to get modem status");
      printOS2error( "DosDevIOCtl", rc );
   } /*if */

   if ( status != oldstatus )
   {
      ShowModem( status );
      oldstatus = status;
   }

/*--------------------------------------------------------------------*/
/*    If we previously had carrier detect but have lost it, we        */
/*    report it was lost.  If we do not yet have carrier detect,      */
/*    we return success because we may not have connected yet.        */
/*--------------------------------------------------------------------*/

   carrierDetect = status && DCD_ON;

   if (previousCarrierDetect)
      return carrierDetect;
   else
      return (status && DSR_ON);

} /* nCD */

/*--------------------------------------------------------------------*/
/*    S h o w M o d e m                                               */
/*                                                                    */
/*    Report current modem status                                     */
/*--------------------------------------------------------------------*/

#define mannounce(flag, bits, text ) ((flag & bits) ? text : "" )

static void ShowModem( const BYTE status )
{
   if ( debuglevel < 4 )
      return;

   printmsg(0, "ShowModem: %#02x%s%s%s%s",
      (int) status,
      mannounce(DCD_ON,   status, "  Carrier Detect"),
      mannounce(RI_ON,    status, "  Ring Indicator"),
      mannounce(DSR_ON,   status, "  Data Set Ready"),
      mannounce(CTS_ON,   status, "  Clear to Send"));

} /* ShowModem */

/*--------------------------------------------------------------------*/
/*    S h o w E r r o r                                               */
/*                                                                    */
/*    Report modem error bits in English (more or less)               */
/*--------------------------------------------------------------------*/

static void ShowError( const USHORT status )
{
   printmsg(2, "Port Error: %#04x%s%s%s%s",
      (int) status,
      mannounce(RX_QUE_OVERRUN,      status, "  Queue Overrrun"),
      mannounce(RX_HARDWARE_OVERRUN, status, "  Hardware Overrun"),
      mannounce(PARITY_ERROR,        status, "  Parity Error"),
      mannounce(FRAMING_ERROR,       status, "  Framing Error"));

} /* ShowError */
