home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
ddjmag
/
ddj8910.arc
/
SMITH2.ARC
/
COMMINT.C
next >
Wrap
Text File
|
1989-04-20
|
13KB
|
330 lines
/* COMMINT.C: Interrupt driven comm port handler, written 100% in C.
* Contains: All PC Hardware specific routines, all routines that
* access the infifo data structure directly.
* Goal: Provide high speed, publishable comm routines for state
* machine driven Comm protocol series of articles. Should be usable
* on either com1 or com2, but not both simultaneously.
* Warning: This code deals with PC hardware directly, and should
* only be used on 100% hardware compatibles. Also, if your program
* aborts abnormally without removing the IH, I'm not responsible.
* Externally callable routines include:
* int Inst_IH( void interrupt (*faddr)(), int comnum) - Installs IH.
* int Remove_IH() - Removes previously installed Int. handler
* void Config_Comm( int port, S_INIT conf ) - Configure comm port
* int incomm() - tests if a char. is available in the comm buffer
* unsigned int getcomch() - returns oldest char. from comm buffer
* int writecomm(unsigned char *buf, int len) - send chars via UART.
*/
#include <dos.h>
#include "commint.h" /* Hardware specific (low level) defines */
#include "commn.h" /* common to commint and cterm files */
/* ------- Storage area to be able to put things back when we leave */
static void interrupt (*oldvect)();
static unsigned char oldimr; /* 8259 interrupt mask reg. */
static unsigned char old_Int_Enable; /* 8250 interrupt enable reg */
static S_INIT old_bps_lctrl; /* Saved speed, par, etc. */
/* ------- Globally visible variables and tables ----------- */
int CurPortAddr; /* Save the currently active comm port here */
unsigned char RxErr = 0; /* Note flags of comm read error */
unsigned int RxErrLoc; /* Save location of the first error */
/* ------- Preloaded arrays for Comm port configuration speed variables */
unsigned divisors[] = { 0x900, 0x600, 0x417, 0x359, 0x300, 0x180, 0x0C0, 0x060,
0x040, 0x03A, 0x030, 0x020, 0x018, 0x010, 0x00C, 0x006,
0x004, 0x003, 0x002, 0 };
unsigned speedi[] = { 50 , 75 , 110 , 135, 150 , 300 , 600 , 1200,
1800 , 2000 , 2400 , 3600 , 4800 , 7200 , 9600 ,19200,
28800, 38400, 57600, 0 };
/* ------- The input fifo (initialized) -------- */
static FIFO infifo = { 0, /* empty */
&infifo.data[0], /* init to start */
&infifo.data[0] };
static int inum = 0; /* used to tell if IH already installed */
/* External variables affected by this module */
extern S_INIT cur_config; /* from module CTERMx.C */
/* ---------- RXRDY interrupt handler -----------------------
* Does: Inputs serial data into receive FIFO. Overwrites if need be.
* Pass: Nothing.
* Sets: Global flag word as follows: Bits 0 :3 Line Status bits.
* Bit 0: FIFO oflow, 1: UART Orun, 2: Parity Err, 3: Framing Err
* Bits 4 :15 buf loc. of error.
* NOTE: Only sets location area upon first error. User should clear.
*/
void interrupt rxrdy_IH()
{
unsigned char lstat; /* Used to save Line status locally */
lstat = inportb( CurPortAddr + LineStatus );
/* Insure there is data to receive.. */
if ( (lstat & DataRdyBit) != 0 ) { /* Bit zero on = data. */
*(infifo.hdr.inptr++) = inportb(CurPortAddr); /* read it and bump ptr. */
if (infifo.hdr.inptr == &infifo.data[FIFOSIZE])
infifo.hdr.inptr = &infifo.data[0]; /* wrap the circular buffer */
if ( ++infifo.hdr.count > FIFOSIZE ) { /* If data overwritten. */
/* Note that LSB is already set (DataRdyBit) and means buf overwrite */
if (RxErr == 0) { /* No Previous error? */
RxErr = lstat;
RxErrLoc = infifo.hdr.inptr - &infifo.data[0];
}
else
RxErr |= lstat; /* OR Rcv errors as we go */
}
else
if ( (lstat &= 0x0E) != 0 ) /* Other Rcv errors? */
RxErr |= lstat; /* OR errors as we go */
}
outportb(Int_Cmd_Reg, End_Int_Cmd); /* Tell 8259 we're done */
}
/* ************** High level interface to hardware ********** */
/* ---------- Get_Conf ----------------------------------
* Does: Reads the speed, parity, stops of async UART.
* Our interrupt handler need not be installed to do so.
* Pass: The comm port # (1 or 2) of interest.
* Returns: A struct containing S_INIT config info.
*/
S_INIT Get_Conf( int port )
{
S_INIT conf; /* to be returned after filling in */
unsigned int divword; /* two halves of baud rate divisor */
int portaddr = (port == 1) ? COM1 : COM2;
unsigned char line_ctrl, temp;
int i;
line_ctrl = inportb(portaddr + LineControl); /* Read current value */
/* Enable access to baud rate divisor regs */
outportb( (portaddr + LineControl), (line_ctrl | DivBit) );
divword = inportb(portaddr + DLL); /* Try the LSByte first */
temp = inportb(portaddr + 1);
divword += (unsigned int) ( temp << 8 );
for ( i = 0; divisors[i] != 0; i++) /* look up divisor inlist */
if (divword == divisors[i])
break;
conf.speed = speedi[i];
outportb( (portaddr + LineControl), line_ctrl ); /* See Data regs again */
conf.ubits.lctrl = line_ctrl;
return(conf);
}
/* ---------- Config_Comm ----------------------------------
* Does: Configures speed, parity, stops of async port.
* Our interrupt handler need not be installed to do so.
* Pass: The comm port # (1 or 2) of interest.
* A struct containing the desired config info.
* Returns: NOTHING.
* Use: Should be used once during init and every time config changes.
* NOTE: Sets global cur_config to new value.
*/
void Config_Comm( int port, S_INIT conf )
{
unsigned char divhi, divlo; /* two halves of baud rate divisor */
int portaddr = (port == 1) ? COM1 : COM2;
int i;
for ( i = 0; speedi[i] != 0; i++) /* look up the chosen speed */
if (conf.speed == speedi[i])
break;
divhi = (unsigned char) (divisors[i] >> 8);
divlo = (unsigned char) (divisors[i] & 0x00FF);
outportb( (portaddr + LineControl), 0x80); /* To config baud divisors */
outportb( (portaddr + DLM), divhi);
outportb( (portaddr + DLL), divlo);
outportb( (portaddr + LineControl), conf.ubits.lctrl );
cur_config = conf;
delay(100);
}
/* ---------- xmit_break ----------------------------------
* Does: Sends a 12 char time break signal to current comm port via UART.
* Our interrupt handler need not be installed to do so.
* Returns: nothing of value.
*/
int xmit_break( )
{
unsigned char line_ctrl;
long break_bits = ( 12 * 10 * 1000L); /* Bits per 12 char * 1000 */
int msecs; /* Number of msecs to send break */
msecs = (int)( break_bits / (long)cur_config.speed );
line_ctrl = inportb(CurPortAddr + LineControl); /* Read current value */
outportb( (CurPortAddr + LineControl), (line_ctrl | BrkBit) );
delay( msecs + 10 ); /* Add some for the timer interrupt */
outportb( (CurPortAddr + LineControl), line_ctrl );
return(0);
}
/* ---------- Install a comm interrupt handler ---------
* Does: Installs the rxrdy comm interrupt handler requested
* Pass: The address of the interrupt function.
* Which comm port (1 or 2) you would like installed.
* Uses: Global variable oldvect to save previous owner
* Global CurPortAddr so that interrupt handler can use it.
* Returns: 0 if alls well, or interrupt # already installed.
*/
int Inst_IH( void interrupt (*faddr)(), int comnum)
{
unsigned char temp;
int i;
unsigned long myvect;
if (inum != 0) /* Do NOT install over ourselves */
return(inum);
/* Both the below set globals for later use to disable */
inum = (comnum == 1) ? 0x0C : 0x0B;
CurPortAddr = (comnum == 1) ? COM1 : COM2;
disable(); /* Disable interrupts to change vectors */
oldvect = getvect(inum); /* Save old vector to clean up later */
old_Int_Enable = inportb(CurPortAddr + Int_Enable_Reg); /* Save this too */
/* Clear the 8250 UART of any garbage by reading all registers */
for (i = 0; i < 6; i++)
temp = inportb(CurPortAddr + i);
old_bps_lctrl = Get_Conf(comnum); /* Save UART speed, etc. */
/* Read the 8259 int mask reg and set IRQ3 or 4 low to enable interrupts.
* Write result back to 8259 when finished */
oldimr = temp = inportb(Int_Mask_Reg);
temp &= ( (CurPortAddr == COM1) ? 0xEF : 0xF7);
outportb(Int_Mask_Reg, temp);
/* Set Out2, DTR and RTS high for the 8250 */
temp = (Out2Bit + DTRBit + RTSBit);
outportb( (CurPortAddr + ModemControl), temp);
/* Set only rxrdy interrupt bit on (bit 0) */
outportb( (CurPortAddr + Int_Enable_Reg), 0x01 );
/* Finally set up the vector and wait and enable interupts */
setvect(inum, faddr);
enable();
return(0);
}
/* ---------- Remove Interrupt Handler. ------------------
* Does: Removes an interrupt handler installed by Inst_IH.
* Re-installs the saved 8259 and 8250 interrupt control regs.
* Uses: Global variables oldvect, CurPortAddr, oldimr and old_Int_Enable.
* Sets: Global inum to 0 if successfully removed.
* Returns: 0 if alls well, or interrupt # already installed.
*/
int Remove_IH()
{
if (inum == 0)
return(inum);
disable(); /* Turn interrupts off to change vectors */
Config_Comm( (inum == 0x0C ? 1 : 2), old_bps_lctrl ); /* restore speed.. */
/* Restore 8259 and 8250 interrupt registers as they were */
outportb(Int_Mask_Reg, oldimr);
outportb( (CurPortAddr + Int_Enable_Reg), old_Int_Enable);
setvect(inum, oldvect); /* Put the other guys interrupt vector back */
inum = 0; /* reset to enable re-install */
enable();
return(0);
}
/* ---------- writecomm -------------------------
* Does: Writes a list of characters to the UART directly.
* Tests UART xmit status. If full, waits until empty.
* If full for too long, returns error value.
* Pass: unsigned char pointer to start of buffer to send.
* length of buffer to send.
* Returns: 1 if write timeout or error, else returns 0.
* NOTE: Not interrupt driven at this time.
*/
writecomm(unsigned char *buf, int len)
{
int i = len;
int ctimes = 0;
unsigned char *cptr = buf;
while (i > 0) {
if ( inportb(CurPortAddr + LineStatus) & TxRdyBit ) {
outportb( CurPortAddr, *(cptr++) );
i--;
ctimes = 0;
}
else {
if (++ctimes > 1000) /* give it a good second to clear */
return(1);
delay(1); /* should take 83 ms per ch at 1200, 333 at 300 */
}
}
return(0);
}
/* ********* Routines that access the infifo data structure ********** */
/* ---------- incomm ----------------------------------
* Does: Tests if a character is ready in the comm buffer.
* Returns: The number of characters stored in the input fifo.
* Use: Similar to keyboard inkey() call.
*/
int incomm()
{
return(infifo.hdr.count);
}
/* ---------- getcomch ----------------------------------
* Does: Returns the oldest character from the comm buffer.
* Similar to keyboard getch() call.
* WARNING: Don't call unless incomm() says it's O.K.
* New 12/23: Now returns unsigned int. If RxErr is set, encodes
* error information in the high byte from RxErr and zeroes RxErr.
*/
unsigned char getcomch()
{
unsigned char outch;
outch = *(infifo.hdr.outptr++);
/* if (RxErr != 0)
* Pass up error and reset RxErr
*/
if (infifo.hdr.outptr == &infifo.data[FIFOSIZE]) /* wrap time? */
infifo.hdr.outptr = &infifo.data[0];
infifo.hdr.count--;
return(outch);
}
/* ---------- eat_noise ------------------------
* Does: Digests any junk that has arrived in the buffer before it
* is allowed to be interpreted by zeroing fifo counters and ptrs.
* Use: Before initial NAK and just after send completes.
* Returns: nothing
* NOTE: Belongs in this module since this is the only place the infifo
* structure is visible.
*/
void eat_noise()
{
infifo.hdr.count = 0;
infifo.hdr.inptr = &infifo.data[0];
infifo.hdr.outptr = infifo.hdr.inptr;
}