Category : C Source Code
Archive   : COMLIB11.ZIP
Filename : COMLIB.C

 
Output of file : COMLIB.C contained in archive : COMLIB11.ZIP
/******************************************************************************
* COMLIB *
* Writen by: Lynn R. Lively *
* Date writen: 7/5/87 (first version) 6/1/88 (largely rewriten) *
* Last change: 8/09/89 *
* *
* This is a toolbox of routines to support interrupt driven serial *
* communications on the IBMPC (and compatables). The routines contained *
* here should allow the programmer to concentrate on the application program *
* rather than the complexities of serial communications. Four ports are *
* supported (COM1, COM2, COM3, & COM4) in such a way that they may be active *
* at the same time. Also included is a simple terminal program which may be *
* activated by defining a compile time constant DEBUG to 1 and compiling the *
* whole library into COMLIB.EXE (this makes for easy test driving of *
* modifications to COMLIB). I am officially placing this library in *
* THE PUBLIC DOMAIN. It was writen using Turbo C (by Borland). Compiler *
* specific is located in COMLIBMD.C. If you have questions/suggestions *
* concerning these routines, I can be reached at 713-947-7876 (Home) or *
* 713-944-1622 x 251 (Work). *
* *
******************************************************************************/

#include
#include
#include
#include
#include
#include
#include
#include "portlib.h"
#include "comlib.h"
#include "comlibmd.h"

/*
* These are the ASCII codes that are used for flow control.
*/

#define XOFF 0x13 /* Ctrl-S char code. Means "Stop sending." */
#define XON 0x11 /* Ctrl-Q char code. Means "Start again." */

/*
* This is the structure where all the communications port information is
* kept for each COM port.
*/

COM_INFO COMPORT[4] =
{
0, 0, 0, 0, 0x3fb, 0x3fd, 0x3fc, 0x3fe, 0x3f8, 0x3fa, 0x3f9, 0x0c, 0xef, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x2fb, 0x2fd, 0x2fc, 0x2fe, 0x2f8, 0x2fa, 0x2f9, 0x0b, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x3eb, 0x3ed, 0x3ec, 0x3ee, 0x3e8, 0x3ea, 0x3e9, 0x0c, 0xef, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x2eb, 0x2ed, 0x2ec, 0x2ee, 0x2e8, 0x2ea, 0x2e9, 0x0b, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/*
* This routine releases the communications port and disables the IRQ.
*/

void comclose (int port)
{
unsigned int temp;

COM_INFO * cport;

disable(); /* Critical code, so disable interrupts */

cport = &COMPORT[port];

/*
* Set vector back to the origional setting.
*/

setvect (cport->com_int_vec, cport->old_com_intr);
free (cport->combuf);
cport->combuf = NULL;
/*
* Clear the IRQ mask of this port.
*/

outportb((cport->mcr), 0x0b); /* Set MCR to allow interrupts. */
inportb((cport->msr)); /* Clear Modem Status Register. */
inportb((cport->lsr)); /* Clear Line Status Register. */
inportb((cport->ior)); /* Clear I/O Register. */
temp = inportb((cport->lcr)) & 0x7f; /* Get lcr masking off DLAB bit.*/
outportb((cport->lcr), temp); /* Set DLAB to 0. */
temp = inportb(IMR) |
(cport->com_irq_mask ^ 0xff); /* Disable IQR. */
outportb(IMR, temp); /* Allow com interrupts. */
outportb((cport->ier), 0x01); /* Enable receive interrupt. */

enable(); /* Re-enable interrupts. */
}

/*
* This routine puts a character onto a circular buffer. If XON/XOF flow
* control is on and the buffer gets dangerously full it sends and XOFF
* character to the other end. If XON/XOFF flow control is on and the
* other side sends and XON/XOFF toggle the xoff_out_stat accordingly.
*/

void far cominp (int port)
{
register unsigned int c;

register COM_INFO * cport;

cport = &COMPORT[port];

if (((inportb(cport->lsr)) & 0x01)) /* Check for char interrupt. */
{
c = inportb(cport->ior); /* Read I/O register. */

if (cport->flw_ctrl_flg == 1) /* Check for flow control on.*/
{
if (cport->xoff_out_stat == 1) /* If output XOFF is on */
{
if (c == XON) /* and char is an XON */
{
cport->xoff_out_stat = 0; /* turn XOFF flag off */
}
else /* else put char in buff.*/
{
cport->combuf[cport->tail++] = c;
}
}
else /* Else XOFF flag is off */
{
if (c == XOFF) /* is this an XOFF char? */
{
cport->xoff_out_stat = 1; /* Yes, turn XOFF flag on.*/
}
else /* No, put char in buff. */
{
cport->combuf[cport->tail++] = c;
}
}
}
else /* Flow control isn't on */
{ /* So put char in buff. */
cport->combuf[cport->tail++] = c;
}

if (cport->tail == cport->buf_siz)
{
cport->tail = 0; /* reset ring buffer pointer */
}
}

outportb(cport->mcr, 0x0b); /* Reset MCR. */
outportb(ICR, EOI); /* Send EOI to 8259 */

/*
* Now handle input flow control.
*/

if ((cport->flw_ctrl_flg == 1) &&
(cport->xoff_in_stat == 0))
{
if ((cport->head < cport->tail) &&
((cport->tail - cport->head) > cport->flw_hi_lim))
{
cport->xoff_in_stat = 1; /* Indicate XOFF status. */
combwrt (port, XOFF); /* Send XOFF char while we */
/* still have time. */
}
else
{
if ((cport->head >= cport->tail) &&
((cport->buf_siz - (cport->head - cport->tail)) >
cport->flw_hi_lim))
{
cport->xoff_in_stat = 1; /* Indicate XOFF status. */
combwrt (port, XOFF); /* Send XOFF char while we */
/* still have time. */
}
}
}
}

/*
* This routine gets a character from the circular buffer and clears an
* XOFF (if on) when the buffer reaches a manageable level. A -1 is
* returned if the buffer is empty;
*/

int combrd (int port)
{
register unsigned int c = 0;

register COM_INFO * cport;

cport = &COMPORT[port];

if (cport->tail == cport->head)
{
return (-1);
}

c = (cport->combuf[cport->head++] & 0xff);

if (cport->head == cport->buf_siz)
{
cport->head = 0;
}

if ((cport->flw_ctrl_flg == 1) && (cport->xoff_in_stat == 1))
{
if ((cport->head < cport->tail) &&
((cport->tail - cport->head) < cport->flw_lo_lim))
{
combwrt (port, XON); /* Send XON to start flow again. */
cport->xoff_in_stat = 0;
}
else
{
if ((cport->head >= cport->tail) &&
((cport->buf_siz -
(cport->head - cport->tail)) < cport->flw_lo_lim))
{
combwrt (port, XON); /* Send XON to start flow again. */
cport->xoff_in_stat = 0;
}
}
}

return (c);
}

/*
* This routine will return characters from the combuff until it hits
* one of the specified delimiters or the number characters = maxchrs or
* timeout (upto 32767) seconds has passed. The target string will be
* NUL terminated. The routine returns 0 = delimeter found, 1 = maxchrs
* reached, or 2 = timed out. You must allocate at least maxchrs + 1
* characters for your target string. The number of characters read is
* returned in the int pointed to by maxchrs.
*/

int comswrd (int port, char *s, char *delims, int * maxchrs, int timeout)
{
register int c;
register int i;

long cur_time,
end_time;

char *nul_ptr,
*delim_ptr;

time (&end_time);
end_time += (long) timeout;

nul_ptr = strchr (delims, 0); /* find NUL on the end of delims. */

/*
* Get characters until maxchrs, delimeter, or timeout condition is
* reached.
*/

i = 0;
while ((i < *maxchrs) &&
(time (&cur_time) < end_time))
{
c = combrd (port);
if (c != -1)
{
i++;
*s++ = c;
delim_ptr = strchr(delims, c);
if ((delim_ptr != NULL) &&
(delim_ptr != nul_ptr))
{
*s = '\0';
*maxchrs = i;
return (0); /* Found one of the delimiters. */
}
}
}

*s = '\0';

if (i < *maxchrs)
{
*maxchrs = i;
return (2); /* Timeout reached. */
}

*maxchrs = i;
return (1); /* Maxchrs reached. */
}

/*
* This routine will return characters from the combuff until it hits
* one of the specified delimiters up to maxchrs. The target string will
* be NUL terminated. Be aware that the NUL character is an implicit
* delimiter. If no delimiter is found (maybe because it hasn't been
* transmitted yet) you will get whatever is currently in the buffer
* (upto maxchars). If the combuff is empty you will get back an empty
* string. The routine returns 0 = delimiter found, 1 = maxchrs reached,
* 2 = read until buffer empty without finding a delimiter.
* Consider this a nowait string read (also called a greedy read).
* You must allocate at least maxchrs + 1 characters for your target string.
* The number of characters read are returned in the int pointed to by maxchrs
*/

int comsrd (int port,
char * s,
char * delims,
int * maxchrs)
{
register int c;
register int i;

char * delim_ptr;

/*
* Get characters until maxchrs, delimiter, or buffer empty whichever
* comes first.
*/

i = 0;
while ((i < *maxchrs) &&
((c = combrd (port)) != -1))
{
i++;
*s++ = c;
delim_ptr = strchr(delims, c);
if (delim_ptr != NULL)
{
*s = '\0';
*maxchrs = i;
return (0); /* Found one of the delimiters. */
}
}

*s = '\0';

if (i == *maxchrs)
{
*maxchrs = i;
return (1); /* Maxchrs reached. */
}

*maxchrs = i;
return (2); /* Read until buffer empty. */
}

/*
* This routine will return characters from the combuff until it hits
* one of the specified delimiters (Not NUL) or the number of
* characters = maxchrs or timeout (upto 32767) seconds has passed.
* The routine returns 0 = delimeter found, 1 = maxchrs reached,
* or 2 = timed out. The number of characters read is returned in
* the int pointed to by maxchrs.
*/

int commwrd (int port,
char * s,
char * delims,
int * maxchrs,
int timeout)
{
register int c;
register int i;

long cur_time,
end_time;

char *nul_ptr,
*delim_ptr;

time (&end_time);
end_time += (long) timeout;
nul_ptr = strchr (delims, 0); /* find NUL on the end of delims. */

/*
* Get characters until maxchrs, delimeter, or timeout condition is
* reached.
*/

i = 0;
while ((i < *maxchrs) &&
(time (&cur_time) < end_time))
{
c = combrd (port);
if (c != -1)
{
i++;
*s++ = c;
delim_ptr = strchr(delims, c);
if ((delim_ptr != NULL) &&
(delim_ptr != nul_ptr))
{
*maxchrs = i;
return (0); /* Found one of the delimiters. */
}
}
}

if (i < *maxchrs)
{
*maxchrs = i;
return (2); /* Timeout reached. */
}

*maxchrs = i;
return (1); /* Maxchrs reached. */
}

/*
* This routine will return characters from the combuff until it hits
* one of the specified delimiters up to maxchrs (Note: NUL is not a
* valid delimiter here).
* If no delimiter is found (maybe because it hasn't been
* transmitted yet) you will get whatever is currently in the buffer
* (upto maxchars). If the combuff is empty you will get back an empty
* string. The routine returns 0 = delimiter found, 1 = maxchrs reached,
* 2 = read until buffer empty without finding a delimiter.
* Consider this a nowait block read (also called a greedy read).
* The number of characters read are returned in the int pointed to
* by maxchrs.
*/

int commrd (int port, char *s, char *delims, int * maxchrs)
{
register int c;
register int i;

char * nul_ptr,
* delim_ptr;

nul_ptr = strchr (delims, 0); /* find NUL on the end of delims. */

/*
* Get characters until maxchrs, delimiter, or buffer empty whichever
* comes first.
*/

i = 0;
while ((i < *maxchrs) &&
((c = combrd (port)) != -1))
{
i++;
*s++ = c;
delim_ptr = strchr(delims, c);
if ((delim_ptr != NULL) &&
(delim_ptr != nul_ptr))
{
*maxchrs = i;
return (0); /* Found one of the delimiters. */
}
}

if (i == *maxchrs)
{
*maxchrs = i;
return (1); /* Maxchrs reached. */
}

*maxchrs = i;
return (2); /* Read until buffer empty. */
}

/*
* This routine puts a character out the COM port.
*/

void combwrt (int port, int c)
{
register int ch;

register COM_INFO * cport;

cport = &COMPORT[port];

while ((cport->flw_ctrl_flg == 1) && /* If flow control is on */
(cport->xoff_out_stat == 1)) /* and output XOFF is on */
{ /* wait for it to clear. */
disable ();
if (((inportb(cport->lsr)) & 0x01)) /* Check for char ready. */
{
ch = inportb(cport->ior); /* Read I/O register. */
if (ch == XON) /* If char is an XON */
{ /* clear the XOFF status.*/
cport->xoff_out_stat = 0;
}
else /* Otherwise put char into buffer */
{
cport->combuf[cport->tail++] = ch;
if (cport->tail == cport->buf_siz)
{
cport->tail = 0; /* reset ring buffer pointer */
}

/*
* Theoretically if I send an XOFF now it will get hung up in
* this loop (pushed a level in the stack, of course). So
* we shouldn't both be in XOFF state at the same time. If this
* happens (its not real likely) its called a Fatal Embrace.
* If your application has high speed traffic in both directions
* at the same time. You might want to consider this logic
* carefully. You may have to choose between the lesser of two
* evils (possible fatal embrace, or possible lost characters
* (In which case, you will need to delete the following logic))
* unless you can figure an elegant way around it. If so, I'd
* really like to know about it.
*/

if (cport->xoff_in_stat == 0)
{
if ((cport->head < cport->tail) &&
((cport->tail - cport->head) > cport->flw_hi_lim))
{
cport->xoff_in_stat = 1; /* Indicate XOFF status. */
combwrt (port, XOFF); /* Send XOFF char while we */
/* still have time. */
}
else
{
if ((cport->head >= cport->tail) &&
((cport->buf_siz - (cport->head - cport->tail)) > cport->flw_hi_lim))
{
cport->xoff_in_stat = 1; /* Indicate XOFF status. */
combwrt (port, XOFF); /* Send XOFF char while we */
/* still have time. */
}
}
}
}

}
enable ();
}

outportb((cport->mcr), 0x0b);
while ((((inportb(cport->lsr)) & 0x20) == 0)) /* Wait until I/O */
{ /* register is */
; /* clear. */
}

outportb(cport->ior, c); /* Send the character. */
}

/*
* This routine sends a NUL terminated string out the COM port.
*/

int comswrt (int port, char *s)
{
register int i;

i = 0;
while (*s != '\0')
{
combwrt (port, *s++);
i++;
}

return (i);
}

/*
* This routine writes the specified number of characters to the
* specified port.
*/

int commwrt (int port, char *s, int ch_cnt)
{
register int i = 0;

while (i < ch_cnt)
{
combwrt (port, *s++);
i++;
}

return (i);
}

#ifdef DEBUG

#define ALT_D 288
#define ALT_P 281
#define ALT_X 301

int c_break (void);
int inkeych (void);

void main ()
{
unsigned int port = COM1_PORT,
speed = 1200,
parity = NO_PARITY,
data_len = BITS_8,
stopbits = STOP_1,
skip_flg = 0,
brk_flg = 0,
c = 0,
i = 0,
str_len = 0,
dsp_ctrl = 0;

char dial_str[81],
s[81];

int ky;

#if TURBOC

ctrlbrk (c_break); /* Disable Ctrl-C/Break sensing */

#endif

comopen (port, (long) speed, parity, stopbits, data_len, 1);

while (1)
{
str_len = 80;
comsrd (port, s, "\r\n", &str_len); /* receive a string. */
if (str_len != 0)
{
if (dsp_ctrl == 1)
{
i = 0;
while (i < str_len)
{
c = s[i++];
if (c < 32)
{
if (c != 3)
{
printf ("^%c", (c + 64));
}
if (c == 10 || c == 13)
{
putch(c);
}
}
else
{
putch(c); /* write to screen */
}
}
}
else
{
printf ("%s", s);
}
}

if ((ky = inkeych ()) != 0); /* Strobe Kybd to see if any input */
{
switch (ky)
{
case ALT_D: /* Dial out */

printf ("\nEnter dial String: ");
strupr (gets(dial_str));
strcat (dial_str, "\r");
comswrt (port, dial_str);
skip_flg = 1;

break;

case ALT_X:

brk_flg = 1;

break;

case ALT_P:

comclose (port);
printf ("\nEnter Port (COM1, COM2, COM3, COM4): ");
strupr (gets (s));
port = (atoi (s) - 1);
if (port > 3 || port < 0)
{
port = 0;
}
printf ("\nEnter Baud Rate: ");
strupr (gets (s));
if (s[0] != '\0')
{
speed = atoi (s);
}
else
{
speed = 1200;
}
printf ("\nEnter Data Length: ");
strupr (gets (s));
data_len = atoi (s);
switch (data_len)
{
case 5:
data_len = BITS_5;
break;
case 6:
data_len = BITS_6;
break;
case 7:

data_len = BITS_7;
break;
case 8:
data_len = BITS_8;
break;
default:
data_len = BITS_8;
break;
}
printf ("\nEnter Parity (N,E,O,M,S): ");
strupr (gets (s));
switch (s[0])
{
case 'N':
parity = NO_PARITY;
break;
case 'E':
parity = EVEN_PARITY;
break;
case 'O':
parity = ODD_PARITY;
break;
case 'M':
parity = MARK_PARITY;
break;
case 'S':
parity = SPACE_PARITY;
break;
default:
parity = NO_PARITY;
break;
}
printf ("\nEnter Stop Bits 1, 2(use this for 1.5 also): ");
strupr (gets (s));
stopbits = (atoi (s) - 1);
if (stopbits > 1 || stopbits < 0)
{
stopbits = 0;
}
comopen (port, (long) speed, parity, stopbits, data_len, 1);

break;

default:

break;
}
if (brk_flg) /* If brk_flg == 1 quit */
{
break;
}
if (skip_flg == 0 && ky != 0)
{
combwrt (port, ky); /* otherwise send character */
}
skip_flg = 0;
}
}
comclose (port);
}

/*
This routine Disables the Ctrl-C/Ctrl-Break interrupt that aborts a program
when a Ctrl-C/Ctrl-Break is keyed. It also passes the Ctrl-C thru to the
comm port (In case the attached system needs Ctrl-C).
*/

#if TURBOC

int c_break (void)
{
register int i;

i = 0;
while (i < 4)
{
if (COMPORT[i].combuf != NULL)
{
combwrt (i, 3);
}
i++;
}

return (1);
}

#endif

/*
This routine strobes the std input to see if any key has been pressed
and passes back the ASCII value of the character. If the character was
an extended key (ALT+ky, etc) it adds 256 to the value and returns the
altered value. If no key was hit it returns 0.
*/

int inkeych (void)
{
register int ky = 0;

if (kbhit () && ((ky = getch()) == 0)) /* If a key was hit and the value */
{ /* was 0 its an extended key. */
ky = 256 + getch(); /* So add 256 to the next key value.*/
}

return (ky);
}

#endif


  3 Responses to “Category : C Source Code
Archive   : COMLIB11.ZIP
Filename : COMLIB.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/