ASYNC.C

20.1 KB bf95b6078ee81bc7…
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*  (C) Copyright 1987-91, Bit Bucket Software Co., a Delaware Corporation. */
/*                                                                          */
/*                                                                          */
/*               This module was written by Peter Fitzsimmons               */
/*                                                                          */
/*                                                                          */
/*                 BinkleyTerm OS/2 Async Comm I/O Routines                 */
/*                                                                          */
/*                                                                          */
/*    For complete  details  of the licensing restrictions, please refer    */
/*    to the License  agreement,  which  is published in its entirety in    */
/*    the MAKEFILE and BT.C, and also contained in the file LICENSE.250.    */
/*                                                                          */
/*    USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE    */
/*    BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF    */
/*    THIS  AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,  OR IF YOU DO    */
/*    NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET    */
/*    SOFTWARE CO.  AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT    */
/*    SHOULD YOU  PROCEED TO USE THIS FILE  WITHOUT HAVING  ACCEPTED THE    */
/*    TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER    */
/*    AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.      */
/*                                                                          */
/*                                                                          */
/* You can contact Bit Bucket Software Co. at any one of the following      */
/* addresses:                                                               */
/*                                                                          */
/* Bit Bucket Software Co.        FidoNet  1:104/501, 1:343/491             */
/* P.O. Box 460398                AlterNet 7:491/0                          */
/* Aurora, CO 80046               BBS-Net  86:2030/1                        */
gg/*                                Internet f491.n343.z1.fidonet.org         */
/*                                                                          */
/* Please feel free to contact us at any time to share your comments about  */
/* our software and/or licensing policies.                                  */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#ifndef OS_2
#pragma message("This Module For OS/2")
#else
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#define INCL_DOS
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSSEMAPHORES
#define INCL_NOPM
#include <os2.h>
#include "bink.h"
#include "com.h"
#include "async.h"

/*--------------------------------------------------------*
 * This file contains the operating system specific       *
 * serial communications routines for OS/2.               *
 *                                                        *
 * (C) Copyright Peter Fitzsimmons Sun  04-16-1989        *
 *                                                        *
 * Add-ins for BinkleyTerm on Sat  05-06-1989             *
 *                                                        *
 * Worked over once again on 06/06-91, trying to get the  *
 * same effect as the Rev 6 FOSSIL in it                  *
 *--------------------------------------------------------*/

/* This one has excellent send or receive, even with 8088 */
/* but the xmit rate is a bit low in Janus w/o SetPrty.   */


/* #define DEBUG */           /* Exposes a bit more about ASYNC comm */
/* #define MultiWrt */        /* Not always reliable                 */


extern void status_line (char *,...);
extern void com_kick (void);

/* Private data */
/* static HFILE hfComHandle = -1; */     /* handle from DosOpen() */
HFILE hfComHandle = -1;      /* handle from DosOpen() */

/* transmitter stuff */
#define TSIZE 8192
static unsigned char tBuf[TSIZE];
static unsigned char zTxBuf[TSIZE];
static int zpos = 0;
static int tBufsize = 0;
static unsigned int TQSize = 0;
/* static HSEM WriteSem = 0; */
ULONG WriteSem = 0;

/* receiver stuff */
#define RSIZE 8192
static unsigned char rbuf[RSIZE];
static USHORT rpos = 0;
static int rbufsize = 0;
static USHORT Rbytes = 0;
static word RQBbytes = 0;
static word RQBbyte2 = 0;

#ifdef DEBUG

#define debug_msg(m,c)  status_line("!" m, c)
#define IOCtl(func, data, parm)  _ioctl(#func, func, (void far *) data, (void far *) parm)

static int _ioctl(char *funcname, int func, void far * data, void far * parm)
{
    int i;

    if (i = DosDevIOCtl((PVOID) data, (PVOID) parm, func, IOCTL_ASYNC, (HFILE) hfComHandle)) {
        printf("ioctl(%s) err(0x%04x)\n", funcname, i);
        status_line("!ioctl(%s) err(0x%04x)", funcname, i);
    }
    return (i);
}

#else
#define debug_msg(m,c)
#define IOCtl(func, data, parm) DosDevIOCtl((PVOID) data, (PVOID) parm, func, IOCTL_ASYNC, (HFILE) hfComHandle)
#endif

static int com_getDCB(struct _DCBINFO far * dcb);
static int com_setDCB(struct _DCBINFO far * dcb);


/* com_init() : Intialize communications port. Baud rate is preserved.
 *            int port  : Hardware port (0 based) to init
 *    -OR-  char *unc   : Full UNC \\networkId\modemId to init.
 *
 * if unc==NULL, port is used. if unc != NULL, unc is used
 */
int com_init(int port, char far *unc)
{
    char str[30];
    char *s;
    USHORT ActionTaken;         /* action: returned by OS/2 */
    USHORT stat;
    DCBINFO sDCB;
    RXQUEUE q;

    cputs("\033[1;33mOS/2 FOSSIL emulator for BinkleyTerm, Peter Fitzsimmons (1:250/628)\033[0m\r\n");
    sprintf(str, "COM%d", port + 1);
    if (!unc)
       unc = str;
#ifdef DEBUG
    stat = DosOpen((PSZ) unc,(PHFILE) &hfComHandle,(PUSHORT) &ActionTaken, 0L, 0, 0x0001, 0x4012, 0L);
#else
    stat = DosOpen((PSZ) unc,(PHFILE) &hfComHandle,(PUSHORT) &ActionTaken, 0L, 0, 0x0001, 0x6012, 0L);
#endif
    if (stat) {
       hfComHandle = -1;
       printf("com_init() : DosOpen() error 0x%04x on '%s'\n", stat, unc);
       return (FALSE);
    }
    (void) DosSemClear((HSEM) &WriteSem);
    if (!IOCtl(ASYNC_GETINQUECOUNT, (PVOID) &q, (PVOID) 0L)) {
       s = getenv("RBUF");
       if (s)
           rbufsize = min( RSIZE, atoi(s));
       else
           rbufsize = RSIZE;
       RQBbytes = min( RSIZE, rbufsize);
       RQBbyte2 = RQBbytes/2;
#ifdef DEBUG
       printf("com_init() : ASYNC Input Buffer size is %d'\n", q.cb);
#endif
    }
    if (!IOCtl(ASYNC_GETOUTQUECOUNT, (PVOID) &q, (PVOID) 0L)) {
       s = getenv("TBUF");
       if (s)
           tBufsize = min( TSIZE, atoi(s));
       else
           tBufsize = TSIZE;
/*     TQSize = min(TSIZE, q.cb/2);    */    /* WRA */
       TQSize = min(TSIZE, q.cb-16);
#ifdef DEBUG
       printf("com_init() : ASYNC Output Buffer size is %d'\n", q.cb);
#endif
    }
    com_XON_disable();

    com_getDCB((struct _DCBINFO far *) &sDCB);

    /* turn off IDSR, ODSR */
/*  sDCB.fbCtlHndShake &= ~(MODE_DSR_HANDSHAKE | MODE_DSR_SENSITIVITY); */

    /* raise DTR, CTS output flow control */
/*  sDCB.fbCtlHndShake |= (MODE_DTR_CONTROL | MODE_CTS_HANDSHAKE); */

    /* turn off XON/XOFF flow control, error replacement off, null
     * stripping off, break replacement off */
    sDCB.fbFlowReplace &= ~(MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE |
                      MODE_ERROR_CHAR | MODE_NULL_STRIPPING |
                      MODE_BREAK_CHAR);

    /* RTS enable */
/*  sDCB.fbFlowReplace |= MODE_RTS_CONTROL; */

/*  sDCB.fbTimeout |= (MODE_NO_WRITE_TIMEOUT | MODE_NOWAIT_READ_TIMEOUT); */
    sDCB.fbTimeout |= (MODE_NOWAIT_READ_TIMEOUT);
    sDCB.fbTimeout &= ~(MODE_NO_WRITE_TIMEOUT);

    sDCB.usReadTimeout = 1000;
    sDCB.usWriteTimeout = 1000;
    com_setDCB((struct _DCBINFO far *) &sDCB);

    return (!stat);
}

void com_DTR_on(void)
{
    DCBINFO sDCB;

    com_getDCB((struct _DCBINFO far *) &sDCB);
    sDCB.fbCtlHndShake |= MODE_DTR_CONTROL;   /* raise DTR */
    com_setDCB((struct _DCBINFO far *) &sDCB);
}

void com_DTR_off(void)
{
    DCBINFO sDCB;

    com_getDCB((struct _DCBINFO far *) &sDCB);
    sDCB.fbCtlHndShake &= ~MODE_DTR_CONTROL; /* lower DTR */
    com_setDCB((struct _DCBINFO far *) &sDCB);
}

/* close communications channel. Baud rate is preserved. */
int com_fini(void)
{
    int stat;

    if (!(hfComHandle == -1)) {
       /* com_wait(); : */
       while ((!com_out_empty()) && com_online())
          DosSleep (1L);
       while ((DosSemWait((HSEM) &WriteSem, 0L)) && (com_online()))
          DosSleep (1L);
       com_clear_in();
       com_clear_out();
       stat = DosClose(hfComHandle);
       if (stat) {
          hfComHandle = -1;
          debug_msg("DosClose() error 0x%04x", stat);
          return (FALSE);
       }
       hfComHandle = -1;
    }
    return(TRUE);
}

long com_cur_baud(void)
{
    USHORT rate = 0;

    IOCtl(ASYNC_GETBAUDRATE, (PVOID) &rate, (PVOID) 0L);
    return ((long) rate);
}

/* com_set_baud() :
 *
 *  rate = 110..19200
 *  parity = N, E, O, M, S (none,even, odd, mark, space)
 *  databits = 5..8
 *  stopbits = 1..2
 *
 */
int com_set_baud(unsigned rate, char parity, int databits, int stopbits)
{
    int stat;
    struct _LINECONTROL {
    BYTE bDataBits;
    BYTE bParity;
    BYTE bStopBits;
    BYTE fbTransBreak;
    } lc;

    stat = IOCtl(ASYNC_SETBAUDRATE, (PVOID) 0L, (PVOID) &rate);
    if (stat) {
       return (FALSE);
    }
    lc.bDataBits = (BYTE) databits;
    switch (stopbits) {
    case 1:
        lc.bStopBits = 0;
        break;
    case 2:
        lc.bStopBits = 2;
        break;
    default:
        if (databits == 5)
        lc.bStopBits = 1;
    }
    lc.fbTransBreak = 0;
    switch (toupper(parity)) {
    case 'N':
        lc.bParity = 0;
        break;
    case 'O':
        lc.bParity = 1;
        break;
    case 'E':
        lc.bParity = 2;
        break;
    case 'M':
        lc.bParity = 3;
        break;
    case 'S':
        lc.bParity = 4;
        break;
    default:
        debug_msg("Bad parity '%c'", parity);
        return (FALSE);
    }
    stat = IOCtl(ASYNC_SETLINECTRL, (PVOID) 0L, (PVOID) &lc);
    if (stat) {
       return (FALSE);
    }
    return (TRUE);
}

static int com_getDCB(struct _DCBINFO far * dcb)
{
    int stat;

    stat = IOCtl(ASYNC_GETDCBINFO, (PVOID) dcb, (PVOID) 0L);
    return (!stat);
}

static int com_setDCB(struct _DCBINFO far * dcb)
{
    int stat;

    stat = IOCtl(ASYNC_SETDCBINFO, (PVOID) 0L, (PVOID) dcb);
    return (!stat);
}

void com_XON_disable(void)
{
    DCBINFO sDCB;

    if (com_getDCB((struct _DCBINFO far *) &sDCB)) {
       /* disable auto Xmit and recv flow control */
       sDCB.fbFlowReplace &= ~(MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE);
       com_setDCB((struct _DCBINFO far *) &sDCB);
    }
    com_kick();

}

void com_XON_enable(void)
{
    DCBINFO sDCB;

    if (com_getDCB((struct _DCBINFO far *) &sDCB)) {
       /* enable auto Xmit and recv flow control */
       sDCB.fbFlowReplace |= (MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE);
       com_setDCB((struct _DCBINFO far *) &sDCB);
    }
}

/* nuke receive buffer */
void com_clear_in(void)
{
    int stat;
    char FlushParm = 0;         /* param to flush IOCTL function */

    Rbytes = rpos = 0;
    if (hfComHandle == -1)
       return;
    stat = DosDevIOCtl((PVOID) 0L, (PVOID) &FlushParm, DEV_FLUSHINPUT, IOCTL_GENERAL, hfComHandle);
    if (stat) {
       debug_msg("DEV_FLUSHINPUT err 0x%04x", stat);
    }
}

/* com_getbuf() : return negative value if error */
int com_getbuf(void)
{
   int stat = 0;
   RXQUEUE q;
   USHORT Rbytet;

   if (rpos == Rbytes)                    /* If buffer empty, */
      rpos = Rbytes = 0;                  /* reset pointers   */

   Rbytet = Rbytes;                       /* Save old count   */
   Rbytes = 0;                            /* Clear new count  */

   if (!(IOCtl(ASYNC_GETINQUECOUNT, (PVOID) &q, (PVOID) 0L))) {
      if (q.cch > 0)
         stat = DosRead(hfComHandle, rbuf+Rbytet, (USHORT) min(RQBbytes-Rbytet,q.cch), &Rbytes);
      else
         return (-1);
   }  else
         return (-1);

   if (stat && !Rbytes && (!(stat==ERROR_MORE_DATA))) {
      debug_msg("DosRead() error 0x%04x", stat);
      return (-1);
   }

   Rbytes += Rbytet;
   return (TRUE);
}

/* com_getchar() : return negative value if error */
int com_getchar(void)
{
   if (rpos != Rbytes)
      return ((int) rbuf[rpos++]);

   if (com_getbuf() == TRUE)
      return ((int) rbuf[rpos++]);
   return (-1);
}

/*non destructive read ahead; no wait */
int com_peek(void)
{
   int c;

   if (!com_char_avail())
      c = -1;
   else {
      c = com_getchar();
      if (c != -1)
         rpos--;
   }
   return (c);
}

/* if RXQueue over half full, return TRUE */
bool com_in_check(void)
{
   RXQUEUE q;

   if (IOCtl(ASYNC_GETINQUECOUNT, (PVOID) &q, (PVOID) 0L))
      return (FALSE);
   return ((bool)((q.cch > RQBbyte2) ? TRUE : FALSE));
}

/* return number of chars in input buffer */
int com_char_avail(void)
{
   RXQUEUE q;

   if (rpos < Rbytes)
      return (Rbytes-rpos);

   return ((!(IOCtl(ASYNC_GETINQUECOUNT, (PVOID) &q, (PVOID) 0L))) ? q.cch : 0 );
}

/* return non 0 value if carrier detected */
bool com_online(void)
{
   BYTE msr;

   if (hfComHandle == -1)
      return(FALSE);

   if (IOCtl(ASYNC_GETMODEMINPUT, (PVOID) &msr, (PVOID) 0L))
      return(FALSE);
   return((bool) (msr & DCD_ON));
}

/* com_break() : start break if on==TRUE, stop break if on==FALSE */
void com_break(int on)
{
   int cmd;
   USHORT comerr;

   cmd = (on) ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF;
   IOCtl(cmd, (PVOID) &comerr, (PVOID) 0L);
}

/* com_out_empty() : return TRUE if output buffer is empty */
bool com_out_empty(void)
{
   RXQUEUE q;

   /* Service Inbound side if necessary */
   if (com_in_check())
      (void) com_getbuf();

   if (DosSemWait((HSEM) &WriteSem, 1L) && !com_online ()) /* WRA 6/14/91 */
      return (TRUE);

   if (IOCtl(ASYNC_GETOUTQUECOUNT, (PVOID) &q, (PVOID) 0L))
      return (TRUE);

   return ((bool) (q.cch == 0));
}

/* com_out_full() : return TRUE if output buffer is full */
bool com_out_full(void)
{
   RXQUEUE q;

   if (com_in_check())
      (void) com_getbuf();

   if (IOCtl(ASYNC_GETOUTQUECOUNT, (PVOID) &q, (PVOID) 0L))
      return (FALSE);

   return ((bool) (q.cch == q.cb));
}

/* nuke transmit buffer */
void com_clear_out(void)
{
   char FlushParm = 0;         /* param to flush IOCTL function */
   int stat;

   zpos = 0;

   DosSemClear((HSEM) &WriteSem);
   if (hfComHandle == -1)
       return;
   stat = DosDevIOCtl((PVOID) 0L, (PVOID) &FlushParm, DEV_FLUSHOUTPUT, IOCTL_GENERAL, hfComHandle);
   if (stat) {
      debug_msg("DEV_FLUSHOUTPUT err 0x%04x", stat);
   }
   com_kick ();
}

/* com_putc_now() : disregard any buffering and send a byte now damnit! */
/* this function should be called only during emergencies...like when
 * trying to cancel a file transfer
 */
/* Since the equivalent is a Com_ call, which in DOS is unsigned... */
unsigned com_putc_now(byte c)
{
   return (!IOCtl(ASYNC_TRANSMITIMM, (PVOID) 0L, (PVOID) &c));
}

/* com_putc() : output to com port */
/* This function is very slow..where possile, write to com port in blocks
 * using com_write() instead...especially above 2400 bps
 */
void com_putc(byte c)
{
   static USHORT bytes;
   int stat;
   RXQUEUE q;

   do {
      if (com_in_check())
         (void) com_getbuf();
    } while(com_online () && DosSemWait((HSEM) &WriteSem, 0L));

   do {
      if (com_in_check())
          (void) com_getbuf();
      if (IOCtl(ASYNC_GETOUTQUECOUNT, (PVOID) &q, (PVOID) 0L))
         return;
      if (q.cch >= q.cb)
      {
         com_kick ();
         DosSleep (1L);
      }
   } while ((q.cch >= q.cb) && com_online ());

   /*
    * DosWriteAsync() didn't work here.
    */
   stat = DosWrite(hfComHandle, &c, 1, &bytes);
   if (stat)
      debug_msg("DosWrite() err 0x%04x", stat);
}

/* com_write() : buffered block write */
void com_write(char *buf, unsigned int num, int carcheck)
{
   static USHORT err, bytes;
#ifdef MultiWrt
   unsigned int tnum = 0;
#endif

   do {
      if ((carcheck) && !com_online())
         return;
      if (com_in_check())
         (void) com_getbuf();
   } while(DosSemWait((HSEM) &WriteSem, 1L));

   (void) DosSemRequest((HSEM) &WriteSem, 0L);

#ifdef MultiWrt
   while (tnum+TQSize > num+TQSize) {
      memcpy (tBuf, &buf[tnum], TQSize);
      DosWriteAsync(hfComHandle, (PULONG) &WriteSem, &err, tBuf, TQSize, &bytes);
      tnum += TQSize;
      do {
         if ((carcheck) && !com_online())
            return;
         if (com_in_check())
            (void) com_getbuf();
      } while(DosSemWait((HSEM) &WriteSem, 1L));

      (void) DosSemRequest((HSEM) &WriteSem, 0L);
   }

   memcpy (tBuf, &buf[tnum], num-tnum);
   DosWriteAsync(hfComHandle, (PULONG) &WriteSem, &err, tBuf, num-tnum, &bytes);
#else

   memcpy (tBuf, buf, num);
   DosWriteAsync(hfComHandle, (PULONG) &WriteSem, &err, tBuf, num, &bytes);
#endif
}

/* wait for output buffer to empty */
void com_wait(void)
{
    while ((!com_out_empty()) && com_online())
       DosSleep (1L);
}

/* force transmitter to go */
void com_kick(void)
{
   int stat;

   if (hfComHandle == -1)
       return;
   stat = IOCtl(ASYNC_STARTTRANSMIT, (PVOID) 0L, (PVOID) 0L);
   if (stat) {
      debug_msg("ASYNC_STARTTRANSMIT err 0x%04x", stat);
   }
}

extern unsigned int baud;
extern unsigned int comm_bits;
extern unsigned int parity;
extern unsigned int stop_bits;
extern struct baud_str btypes[];

void MDM_ENABLE(unsigned rate)
{
    char _parity;
    int databits;
    int stopbits;

    if (hfComHandle == -1)
       return;
    com_clear_out();
    com_clear_in();
    databits = 7 + (comm_bits == BITS_8);
    stopbits = 1 + (stop_bits == STOP_2);
    switch (parity) {
    case NO_PARITY:
        _parity = 'N';
        break;
    case ODD_PARITY:
        _parity = 'O';
        break;
    case EVEN_PARITY:
        _parity = 'E';
        break;
    default:
        _parity = 'N';
    }
    com_set_baud(rate, _parity, databits, stopbits);
}

void MDM_DISABLE(void)
{
    if (hfComHandle == -1)
       return;
    com_clear_out();
    com_clear_in();
    com_fini();
}

/* zsend.c uses BUFFER_BYTE and UNBUFFER_BYTES...good idea. */
void BUFFER_BYTE(unsigned char ch)
{
      if (zpos == tBufsize)
         UNBUFFER_BYTES();
      zTxBuf[zpos++]  = ch;
}

void UNBUFFER_BYTES(void)
{
   if (com_online() && zpos)
      (zpos == 1) ? (com_putc(zTxBuf[0])) : (com_write(zTxBuf,zpos,1)) ;
   zpos = 0;
}

unsigned Cominit(int port, int failsafe)
{
    failsafe = failsafe;

    if (hfComHandle != -1)
       com_fini();

    if (port == 0xff || com_init(port, NULL))
       return (0x1954);
    else
       return (0);
}
#endif /* OS_2 */