home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 2 BBS
/
02-BBS.zip
/
BTMTSRC3.ZIP
/
ASYNC.C
next >
Wrap
C/C++ Source or Header
|
1991-10-17
|
18KB
|
869 lines
/* async.c
**
** Copyright (c) 1991, Chris Laforet Software/Chris Laforet
** All Rights Reserved
**
** Started: 12 August 1991
** From s_fossil.c for Simplex/2 BBS: Started: 23 November 1989
**
** Revision Information: $Logfile: H:/binkley/vcs/async.c_v $
** $Date: 01 Sep 1991 14:32:44 $
** $Revision: 1.0 $
**
*/
#define INCL_KBD
#define INCL_DOSPROCESS
#define INCL_DOSFILEMGR
#define INCL_DOSDEVICES
#define INCL_DOSSEMAPHORES
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <os2.h>
#include "com.h"
#include "bink.h"
/* ----------------------- Structures for IOCTL ------------------------
** Taken from MS's OS/2 toolkit -- missing in IBM's!
*/
struct _dcbinfo
{
USHORT usWriteTimeout;
USHORT usReadTimeout;
BYTE fbCtlHndShake;
BYTE fbFlowReplace;
BYTE fbTimeout;
BYTE bErrorReplacementChar;
BYTE bBreakReplacementChar;
BYTE bXONChar;
BYTE bXOFFChar;
};
struct _linecontrol
{
BYTE bDataBits;
BYTE bParity;
BYTE bStopBits;
BYTE fTransBreak;
};
struct _rxqueue
{
USHORT cch;
USHORT cb;
};
struct _modemstatus
{
BYTE fbModemOn;
BYTE fbModemOff;
};
/* -------------------- End of Structures for IOCTL -------------------- */
#define COM_TIMEOUT 1
HFILE com_port = (HFILE)-1;
int leave_port = 0;
static struct _dcbinfo save_di;
/* -------------------- Start of Ring Buffer Handlers -------------------- */
#define RING_STACKSIZE 2048
#define RINGBUF_SIZE 1024
#define READ_AHEAD 32
int oring_stack[RING_STACKSIZE / sizeof(int)]; /* word-align the stack */
int ringthread = 1;
unsigned char inoringbuf[RINGBUF_SIZE];
unsigned char *inoringptr;
unsigned char *outoringptr;
long oring_sem = 0L; /* controls access to our oring buffer */
TID oring_tid;
int iring_stack[RING_STACKSIZE / sizeof(int)]; /* word-align the stack */
unsigned char iniringbuf[RINGBUF_SIZE];
unsigned char *iniringptr;
unsigned char *outiringptr;
long iring_sem = 0L; /* controls access to our oring buffer */
TID iring_tid;
int fossil_init = 0;
void pascal purge_oringbuf(void)
{
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
inoringptr = outoringptr = inoringbuf;
DosSemClear(&oring_sem);
}
int pascal write_oringbuf(unsigned char character,int checkcd)
{
int type;
int ok;
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (inoringptr != outoringptr)
{
type = 0;
if ((inoringptr < outoringptr) && ((inoringptr + 1) == outoringptr))
type = 1;
else if ((outoringptr == inoringbuf) && (inoringptr == (inoringbuf + (RINGBUF_SIZE - 1)))) /* no room in the buffer */
type = 2;
if (type)
{
ok = 0;
do
{
DosSemClear(&oring_sem);
if (checkcd && !com_online()) /* CD still there? */
return 0;
DosSleep(10L);
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (type == 1 && ((inoringptr + 1) != outoringptr))
ok = 1;
else if (type == 2 && outoringptr != inoringbuf)
ok = 1;
}
while (!ok);
}
}
*(unsigned char*)inoringptr = character;
++inoringptr;
if (inoringptr >= (inoringbuf + RINGBUF_SIZE))
inoringptr = inoringbuf;
DosSemClear(&oring_sem);
return 1;
}
void oring_handler(void far *dummy)
{
unsigned char buffer[READ_AHEAD];
int written;
int current;
int slice = 0;
int send;
int left;
DosSetPrty(PRTYS_THREAD,PRTYC_FOREGROUNDSERVER,PRTYD_MINIMUM,oring_tid);
while (ringthread)
{
if (com_port != (HFILE)-1)
{
if (inoringptr != outoringptr)
{
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
/* send a char */
send = 0;
do
{
buffer[send++] = (unsigned char)*outoringptr;
++outoringptr;
if (outoringptr >= (inoringbuf + RINGBUF_SIZE))
outoringptr = inoringbuf;
}
while (send < READ_AHEAD && inoringptr != outoringptr);
DosSemClear(&oring_sem);
if (send)
{
current = 0;
left = send - current;
do
{
DosWrite(com_port,buffer + current,left,&written);
if (written)
{
current += written;
left = send - current;
}
if (written != left)
{
if (!ringthread)
break;
if (slice >= 20)
{
DosSleep(1L);
slice = 0;
}
else
++slice;
}
}
while (current < send);
}
}
else
DosSleep(1L);
}
else
DosSleep(30L);
}
_endthread();
}
void pascal purge_iringbuf(void)
{
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
iniringptr = outiringptr = iniringbuf;
DosSemClear(&iring_sem);
}
int pascal peek_iringbuf(void)
{
int rtn;
if (iniringptr == outiringptr)
rtn = -1;
else
rtn = (int)*(unsigned char *)outiringptr;
return rtn;
}
int pascal read_iringbuf(void)
{
int rtn = -1;
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
if (iniringptr != outiringptr)
{
rtn = (int)*(unsigned char *)outiringptr;
++outiringptr;
if (outiringptr >= (iniringbuf + RINGBUF_SIZE))
outiringptr = iniringbuf;
}
DosSemClear(&iring_sem);
return rtn;
}
void iring_handler(void far *dummy)
{
unsigned char buffer[READ_AHEAD];
int current;
int bytesread;
int type;
int ok;
DosSetPrty(PRTYS_THREAD,PRTYC_FOREGROUNDSERVER,PRTYD_MINIMUM,iring_tid);
while (ringthread)
{
if (com_port != (HFILE)-1)
{
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
DosRead(com_port,buffer,READ_AHEAD,&bytesread);
if (bytesread)
{
current = 0;
do
{
if (iniringptr != outiringptr)
{
type = 0;
if ((iniringptr < outiringptr) && ((iniringptr + 1) == outiringptr))
type = 1;
else if ((outiringptr == iniringbuf) && (iniringptr == (iniringbuf + (RINGBUF_SIZE - 1)))) /* no room in the buffer */
type = 2;
if (type)
{
ok = 0;
do
{
DosSemClear(&iring_sem);
DosSleep(1L);
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
if (type == 1 && ((iniringptr + 1) != outiringptr))
ok = 1;
else if (type == 2 && outiringptr != iniringbuf)
ok = 1;
}
while (!ok);
}
}
*(unsigned char*)iniringptr = buffer[current];
++iniringptr;
if (iniringptr >= (iniringbuf + RINGBUF_SIZE))
iniringptr = iniringbuf;
++current;
}
while (current < bytesread);
DosSemClear(&iring_sem);
}
else
{
DosSemClear(&iring_sem);
DosSleep(1L);
}
}
else
DosSleep(30L);
}
_endthread();
}
int start_ringthread(void)
{
iniringptr = outiringptr = iniringbuf; /* start input ring thread */
DosSemClear(&iring_sem);
if ((iring_tid = _beginthread(iring_handler,iring_stack,RING_STACKSIZE,NULL)) == -1)
return 0;
inoringptr = outoringptr = inoringbuf; /* start output ring thread */
DosSemClear(&oring_sem);
if ((oring_tid = _beginthread(oring_handler,oring_stack,RING_STACKSIZE,NULL)) == -1)
return 0;
return 1;
}
void hold_comthread(void)
{
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
}
void restart_comthread(void)
{
DosSemClear(&iring_sem);
DosSemClear(&oring_sem);
}
/* -------------------- End of Ring Buffer Handlers -------------------- */
/* 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 *unc)
{
struct _dcbinfo di;
struct _modemstatus ms;
UCHAR szBuffer[5];
USHORT action;
USHORT error;
if (com_port == -1)
{
if (!fossil_init) /* print sign-on ONLY the first time we start up */
{
fprintf(stderr,"\n\nLow-level OS/2 communications interface routines for Binkleyterm-OS/2.\n");
fprintf(stderr,"Copyright (c) 1991, Chris Laforet and Chris Laforet Software (1:151/402).\n\n");
fossil_init = 1;
}
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (!unc)
{
sprintf(szBuffer,"com%u",port + 1);
unc = szBuffer;
}
if (DosOpen(unc,&com_port,&action,0L,FILE_NORMAL,FILE_OPEN,OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE,0L))
{
DosSemClear(&oring_sem);
DosSemClear(&iring_sem);
return 0; /* failed port open */
}
DosDevIOCtl(&di,0L,0x73,1,com_port);
if (!leave_port)
{
di.fbFlowReplace = (BYTE)0x40; /* flip RTS Control mode on */
di.fbCtlHndShake = (BYTE)0x8; /* enable CTS control */
}
else
{
memcpy(&di,&save_di,sizeof(struct _dcbinfo));
di.fbFlowReplace &= ~0x3; /* Turn off XON/XOFF automatic input and output flow control */
}
di.usWriteTimeout = COM_TIMEOUT;
di.usReadTimeout = COM_TIMEOUT;
di.fbTimeout &= 0xfa; /* kill no write timeout and wait read timeout -- LEAVE hardware buffering */
di.fbTimeout = (BYTE)2; /* read timeout */
di.fbCtlHndShake |= (BYTE)1; /* enable dtr control */
DosDevIOCtl(0L,&di,0x53,1,com_port);
ms.fbModemOn = 0x2; /* flip RTS on */
ms.fbModemOff = 0xff;
DosDevIOCtl(&error,&ms,0x46,1,com_port); /* set modem control */
iniringptr = outiringptr = iniringbuf;
inoringptr = outoringptr = inoringbuf;
DosSemClear(&oring_sem);
DosSemClear(&iring_sem);
return 1;
}
else
return 0;
}
int com_fini(void) /* close port */
{
struct _dcbinfo di;
struct _modemstatus ms;
USHORT error;
if (com_port != (HFILE)-1)
{
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (com_port != -1)
{
if (!leave_port)
{
DosDevIOCtl(&di,0L,0x73,1,com_port);
di.usWriteTimeout = COM_TIMEOUT;
di.usReadTimeout = COM_TIMEOUT;
di.fbCtlHndShake = (BYTE)8; /* enable dtr control and CTS control mode */
di.fbFlowReplace = (BYTE)0x40; /* flip RTS Control mode on */
di.fbTimeout &= 0xfa; /* kill no write timeout and wait read timeout -- LEAVE hardware buffering */
di.fbTimeout = (BYTE)2; /* read timeout */
DosDevIOCtl(0L,&di,0x53,1,com_port);
}
else
DosDevIOCtl(0L,&save_di,0x53,1,com_port);
}
raise_dtr();
ms.fbModemOn = 0x2; /* flip RTS on */
ms.fbModemOff = 0xff;
DosDevIOCtl(&error,&ms,0x46,1,com_port); /* set modem control */
DosClose(com_port);
com_port = (HFILE)-1;
iniringptr = outiringptr = iniringbuf;
inoringptr = outoringptr = inoringbuf;
DosSemClear(&oring_sem);
DosSemClear(&iring_sem);
return 1;
}
return 0;
}
int Cominit(int port) /* Fitzsimmons' or Andrus' code */
{
if (com_port != (HFILE)-1)
com_fini();
if (port == 0xff || com_init(port,NULL))
return 0x1954;
return 0;
}
void raise_dtr(void)
{
struct _modemstatus ms;
USHORT error;
ms.fbModemOn = 0x1; /* DTR on */
ms.fbModemOff = 0xff;
DosDevIOCtl(&error,&ms,0x46,1,com_port);
}
void lower_dtr(void)
{
struct _modemstatus ms;
USHORT error;
ms.fbModemOn = 0x0;
ms.fbModemOff = 0xfe; /* DTR off */
DosDevIOCtl(&error,&ms,0x46,1,com_port);
}
int com_set_baud(unsigned int baud,char parity,int databits,int stopbits)
{
struct _linecontrol lc;
int rtn = 1;
DosSemRequest(&iring_sem,SEM_INDEFINITE_WAIT);
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (DosDevIOCtl(0L,&baud,0x41,1,com_port))
rtn = 0;
else
{
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;
else
lc.bStopBits = 0;
}
lc.fTransBreak = 0;
switch (parity)
{
case 'O':
case 'o':
lc.bParity = 1;
break;
case 'E':
case 'e':
lc.bParity = 2;
break;
case 'M':
case 'm':
lc.bParity = 3;
break;
case 'S':
case 's':
lc.bParity = 4;
break;
default: /* default to N parity */
lc.bParity = 0;
break;
}
if (DosDevIOCtl(0L,&lc,0x42,1,com_port))
rtn = 0;
}
DosSemClear(&oring_sem);
DosSemClear(&iring_sem);
return rtn;
}
void com_XON_disable(void)
{
struct _dcbinfo di;
DosDevIOCtl(&di,0L,0x73,1,com_port);
di.fbFlowReplace &= ~0x3; /* Turn off XON/XOFF automatic input and output flow control */
DosDevIOCtl(0L,&di,0x53,1,com_port);
}
void com_XON_enable(void)
{
struct _dcbinfo di;
DosDevIOCtl(&di,0L,0x73,1,com_port);
di.fbFlowReplace |= 0x3; /* Turn on XON/XOFF automatic input and output flow control */
di.bXOFFChar = (char)0x13;
di.bXONChar = (char)0x11;
DosDevIOCtl(0L,&di,0x53,1,com_port);
}
/* com_break() : start break if on==TRUE, stop break if on==FALSE */
void com_break(int cmd)
{
USHORT err;
if (cmd == 1)
DosDevIOCtl(&err,0L,0x4b,1,com_port); /* setbreakon */
else
DosDevIOCtl(&err,0L,0x45,1,com_port); /* setbreakoff */
}
/* return non-0 value if carrier detected */
int pascal com_online(void)
{
USHORT flags;
DosDevIOCtl(&flags,0L,0x67,1,com_port); /* get modem input */
if (flags & 0x80) /* DCD on? */
return RLSD;
return 0;
}
void com_clear_in(void)
{
purge_iringbuf();
DosDevIOCtl(0L,0L,0x1,0xb,com_port); /* dev flush input */
}
void com_clear_out(void)
{
purge_oringbuf();
DosDevIOCtl(0L,0L,0x2,0xb,com_port); /* dev flush output */
}
/* 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 int pascal com_putc_now(unsigned char c)
{
if (DosDevIOCtl(0L,&c,0x44,0x1,com_port)) /* dev transmit immediately */
return 0;
return 1;
}
/* com_putc() : output to com port
** This function is very slow..where possible, write to com port in blocks
** using com_write() instead...especially above 2400 bps -- Peter Fitzsimmons'
** comment.
**
** CML - this function should not be that much slower.
*/
void pascal com_putc(unsigned char c)
{
write_oringbuf(c,0); /* stick it into out buffer */
}
/* CML: Only for SPECIAL occasions. */
void pascal com_direct(unsigned char c)
{
USHORT written;
do
{
DosWrite(com_port,&c,1,&written);
if (!written)
DosSleep(0L);
}
while (!written);
}
/* com_write() : buffered block write */
void pascal com_write(char *buf,int num,int checkcd)
{
int count;
for (count = 0; count < num; count++)
{
if (!write_oringbuf((unsigned char)*buf++,checkcd))
break;
}
}
/* com_out_empty() : return TRUE if output buffer is empty */
int pascal com_out_empty(void)
{
struct _rxqueue rq;
int empty = 0;
DosDevIOCtl(&rq,0L,0x69,1,com_port);
if (rq.cch <= 1)
{
if (inoringptr == outoringptr) /* check our ring buffer */
++empty;
}
return empty;
}
/* com_out_full() : return TRUE if output buffer is full */
int pascal com_out_full(void)
{
int full = 0;
DosSemRequest(&oring_sem,SEM_INDEFINITE_WAIT);
if (inoringptr != outoringptr)
{
if ((inoringptr < outoringptr) && ((inoringptr + 1) == outoringptr))
full = 1;
else if ((outoringptr == inoringbuf) && (inoringptr == (inoringbuf + (RINGBUF_SIZE - 1)))) /* no room in the buffer */
full = 1;
}
DosSemClear(&oring_sem);
return full;
}
/* wait for output buffer to empty */
void com_wait(void)
{
while (com_online && !com_out_empty())
DosSleep (1L);
}
/* com_getchar() : return negative value if error */
int pascal com_getchar(void)
{
return read_iringbuf();
}
/* non destructive read ahead; no wait */
int pascal com_peek(void)
{
return peek_iringbuf();
}
/* if RXQueue over half full, return TRUE */
int pascal com_in_check(void)
{
struct _rxqueue rq;
DosDevIOCtl(&rq,0L,0x68,1,com_port);
if (rq.cch >= (rq.cb >> 1))
return 1;
return 0;
}
/* return number of chars in input buffer */
int pascal com_char_avail(void)
{
if (iniringptr != outiringptr)
return 1; /* this might work */
return 0;
}
extern int port_num;
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 int rate) /* this is Fitzsimmons' or Andrus' code */
{
char _parity;
int databits;
int stopbits;
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;
}
com_set_baud(rate,_parity,databits,stopbits);
}
void MDM_DISABLE(void) /* this is Fitzsimmons' or Andrus' code */
{
if (com_port == -1)
return;
com_clear_out();
com_clear_in();
com_fini();
}
void pascal BUFFER_BYTE(unsigned char ch)
{
write_oringbuf(ch,0);
}
void UNBUFFER_BYTES(void)
{
/* since stuff is in another thread, the writing will occur anyway! */
}