rhys@batserver.cs.uq.oz.au (Rhys Weatherley) (11/01/90)
Due to a few requests, here are my "bullet-proof" routines for serial comms under Turbo C. "Bullet-proof" that is, except 'comsave' and 'comrestore', which were recently added, but still need some work, as they cause funny things to happen to other comms programs after they have been used :-). The rest is OK as far as I'm able to test on my XT machine. They have support for up to 115.2 Kbaud, but I don't know if they can handle interrupts that fast! They are released to the public domain, and you can do anything you like with them, but it would be nice if you mentioned who you got them from :-). I invite criticisms, and down-right flames (if warranted) on this code, to help improve its suitability for any comms serial application in Turbo C. Suggestions for making this work under Microsoft C as well, are welcome, but I can't test MSC versions myself. Rhys. ------------------------cut-here--comms.h-------------------------------- /* COMMS.H - Communications Routines for Turbo C/C++ */ /* Version 1.0 by Rhys Weatherley, July 1990. */ /* Internet: rhys@batserver.cs.uq.oz.au */ #ifndef __COMMS_H__ #define __COMMS_H__ /* Defines for various port parameters */ #define BAUD_110 0 /* Baud rates */ #define BAUD_150 1 #define BAUD_300 2 #define BAUD_600 3 #define BAUD_1200 4 #define BAUD_2400 5 #define BAUD_4800 6 #define BAUD_9600 7 #define BAUD_19200 8 #define BAUD_38400 9 #define BAUD_115200 10 #define BITS_7 0x00 /* Data bits */ #define BITS_8 0x10 #define STOP_1 0x00 /* Stop bits */ #define STOP_2 0x20 #define PARITY_NONE 0x00 /* Parity modes */ #define PARITY_ODD 0x40 #define PARITY_EVEN 0x80 /* Declare the functions as C for Turbo C++ */ #ifdef __cplusplus extern "C" { #endif /* Enable a COM port for Interrupt Driven I/O by this module */ void comenable (int port); /* Save the current setting of a COM port to be restored later */ /* Call this function before calling 'comenable' on the port. */ void comsave (int port); /* Set the communications parameters for a COM port */ /* e.g. params = BAUD_2400 | BITS_8 */ void comparams (int port,int params); /* Disable the interrupt I/O for a COM port */ /* If 'leavedtr' != 0, then leave DTR up */ void comdisable (int port,int leavedtr); /* Restore the setting of a COM port - after 'comdisable' */ void comrestore (int port); /* Turn a COM port's test loop-back mode on or off */ /* NOTE: comenable always turns the loop-mode off */ void comtest (int port,int on); /* Return the number of available input chars on a COM port */ int comavail (int port); /* Flush all input from the COM port */ void comflush (int port); /* Receive a character from the COM port: -1 if not ready */ int comreceive (int port); /* Test to see if the COM port is ready for a new */ /* character to transmit. */ int comready (int port); /* Output a character to a COM port */ void comsend (int port,int ch); /* Test to see if a carrier is present */ int comcarrier (int port); /* Test to see if the DSR (Data Set Ready) signal is present */ int comdsr (int port); /* Drop the DTR signal on a COM port */ void comdropdtr (int port); /* Raise the DTR signal on a COM port */ void comraisedtr (int port); /* Set the BREAK pulse on a COM port to 0 or 1 */ void combreak (int port,int value); #ifdef __cplusplus } #endif #endif __COMMS_H__ ----------------------cut-here--comms.c------------------------------------ /* COMMS.C - Communications Routines for Turbo C/C++ */ /* Version 1.0 by Rhys Weatherley, July 1990. */ /* Internet: rhys@batserver.cs.uq.oz.au */ #include "comms.h" /* Declarations for this module */ #include <dos.h> /* Various PIC/COM masks and values */ #define PIC_MASK 0x21 #define PIC_EOI 0x20 #define ERR_MSK 0x9E /* Definitions for interrupt handling */ /* COM port offsets */ #define COM_DATA 0 /* Data received on this I/O address */ #define COM_IER 1 /* This register enables interrupts */ #define COM_LCR 3 /* Line control register */ #define COM_MCR 4 /* Control Register (signals) */ #define COM_STAT 5 /* Status Register */ #define COM_MSR 6 /* Modem Status Register */ /* Data for installing COM port interrupts */ #define COM_INT_1 0xC /* 0xC handles IRQ4 or COM1 by standard */ #define INT_MASK_1 0x10 /* Mask for PIC (programmable interrupt controller) 8259A */ #define COM_INT_2 0xB /* 0xB handles IRQ3 or COM2 by standard */ #define INT_MASK_2 0x8 /* Mask for PIC (programmable interrupt controller) 8259A */ /* Define the interrupt buffer structure */ #define MAX_INT_BUFSIZ 4096 struct IntBuf { char buffer[MAX_INT_BUFSIZ]; int input,output,size; int statport,dataport; int testbit; int oldier,oldmcr,oldlcr,oldbaud; }; static struct IntBuf Com1Buf,Com2Buf; /* Define the data for the interrupt handlers */ static void interrupt int_com1(); static void interrupt int_com2(); static void interrupt (*old_c1)(); static void interrupt (*old_c2)(); /* Change the test loop-back properties of a COM port (given MCR reg) */ static int changetest (mcrport,on) int mcrport,on; { if (on) { outportb (mcrport, inportb (mcrport) | 0x10); return (0x10); } else { outportb (mcrport, inportb (mcrport) & 0x0F); return (0); } } /* Turn a COM port's test loop-back mode on or off */ /* NOTE: comenable always turns the loop-mode off */ void comtest (port,on) int port,on; { switch (port) { case 1: Com1Buf.testbit = changetest (Com1Buf.dataport + COM_MCR,on); break; case 2: Com2Buf.testbit = changetest (Com2Buf.dataport + COM_MCR,on); break; default: break; } } /* Return the number of available input chars on a COM port */ int comavail (port) int port; { switch(port) { case 1: return (Com1Buf.size); case 2: return (Com2Buf.size); default:return 0; } } /* Flush all input from the COM port */ void comflush (port) int port; { switch (port) { case 1: disable (); /* Disallow ints while flushing */ Com1Buf.size = 0; Com1Buf.output = Com1Buf.input; enable (); break; case 2: disable (); /* Disallow ints while flushing */ Com2Buf.size = 0; Com2Buf.output = Com2Buf.input; enable (); break; default:break; } } /* comflush */ /* Receive a character from the COM port: -1 if not ready */ int comreceive(port) int port; { int ch; switch(port) { case 1: if (Com1Buf.size == 0) return -1; else { ch = Com1Buf.buffer[Com1Buf.output]; Com1Buf.output = (Com1Buf.output + 1) % MAX_INT_BUFSIZ; --Com1Buf.size; return ch; } case 2: if (Com2Buf.size == 0) return -1; else { ch = Com2Buf.buffer[Com2Buf.output]; Com2Buf.output = (Com2Buf.output + 1) % MAX_INT_BUFSIZ; --Com2Buf.size; return ch; } default:return (-1); } } /* COM1 Interrupt handler. HARDWARE DEPENDENT */ static void interrupt int_com1() { char ch; disable(); /* Make sure ints are disabled */ /* If no error occured, then store the character */ if ((ch = (inportb(Com1Buf.statport)&ERR_MSK)) == 0) { ch = inportb(Com1Buf.dataport);/* Get the character */ if (Com1Buf.size < MAX_INT_BUFSIZ) /* Store the char */ { Com1Buf.buffer[Com1Buf.input] = ch; Com1Buf.input = (Com1Buf.input + 1) % MAX_INT_BUFSIZ; ++Com1Buf.size; } } else ch = inportb(Com1Buf.dataport);/* Remove the erroneous character */ outportb(PIC_EOI,0x20); /* Tell PIC we have handled the int */ } /* COM2 Interrupt handler. HARDWARE DEPENDENT */ static void interrupt int_com2() { char ch; disable(); /* Make sure ints are disabled */ /* If no error occured, then store the character */ if ((ch = (inportb(Com2Buf.statport)&ERR_MSK)) == 0) { ch = inportb(Com2Buf.dataport);/* Get the character */ if (Com2Buf.size < MAX_INT_BUFSIZ) /* Store the char */ { Com2Buf.buffer[Com2Buf.input] = ch; Com2Buf.input = (Com2Buf.input + 1) % MAX_INT_BUFSIZ; ++Com2Buf.size; } } else ch = inportb(Com2Buf.dataport);/* Remove the erroneous character */ outportb(PIC_EOI,0x20); /* Tell PIC we have handled the int */ } /* Enable a COM port given its I/O port address and other data */ static void doenable (port) int port; { /* Set DLAB bit to zero to ensure that we */ /* access the correct COM port registers */ outportb(port + COM_LCR,inportb(port + COM_LCR) & 0x7F); /* Turn off the chip's interrupts to start with */ outportb(port + COM_IER,0); outportb(port + COM_MCR,8); /* Just DTR up for now */ /* Read status and data ports to clear any outstanding errors */ inportb(port + COM_STAT); inportb(port); /* Set ints for data available */ outportb(port + COM_IER,1); } /* Enable a COM port for Interrupt Driven I/O by this module */ void comenable(port) int port; { char ch; int dataport; switch(port) { case 1: /* Initialise buffers for COM1 */ Com1Buf.input = 0; Com1Buf.output = 0; Com1Buf.size = 0; dataport = *((int far *)0x400L); Com1Buf.dataport = dataport; Com1Buf.statport = Com1Buf.dataport + COM_STAT; Com1Buf.testbit = 0; doenable (dataport); /* Setup the regs */ /* Setup the ISR details */ old_c1 = getvect(COM_INT_1); /* Save old COM1 int */ setvect(COM_INT_1,int_com1); /* Install new int */ /* Now turn on DTR, RTS and OUT2: all ready */ outportb(dataport + COM_MCR,0x0B); /* Program the PIC to handle COM1 interrupts */ ch = inportb(PIC_MASK); /* Read current mask */ ch &= (0xFF^INT_MASK_1);/* Reset mask for COM1 */ outportb(PIC_MASK,ch); /* Send it to the 8259A */ break; case 2: /* Initialise buffers for COM2 */ Com2Buf.input = 0; Com2Buf.output = 0; Com2Buf.size = 0; dataport = *((int far *)0x402L); Com2Buf.dataport = dataport; Com2Buf.statport = Com2Buf.dataport + COM_STAT; Com2Buf.testbit = 0; doenable (dataport); /* Setup the regs */ /* Setup the ISR details */ old_c2 = getvect(COM_INT_2); /* Save old COM2 int */ setvect(COM_INT_2,int_com2); /* Install new int */ /* Now turn on DTR, RTS and OUT2: all ready */ outportb(dataport + COM_MCR,0x0B); /* Program the PIC to handle COM2 interrupts */ ch = inportb(PIC_MASK); /* Read current mask */ ch &= (0xFF^INT_MASK_2);/* Reset mask for COM2 */ outportb(PIC_MASK,ch); /* Send it to the 8259A */ break; default:break; } } /* Save the current setting of a COM port to be restored later */ /* Call this function before calling 'comenable' on the port. */ /* *** NOTE: haven't got this working properly yet! *** */ void comsave (port) int port; { int dataport; switch (port) { case 1: dataport = *((int far *)0x400L); Com1Buf.oldlcr = inportb (dataport + COM_LCR); Com1Buf.oldmcr = inportb (dataport + COM_MCR); outportb (dataport + COM_LCR,Com1Buf.oldlcr & 0x7F); Com1Buf.oldier = inportb (dataport + COM_IER); outportb (dataport + COM_LCR,Com1Buf.oldlcr | 0x80); Com1Buf.oldbaud = inport (dataport); outportb (dataport + COM_LCR,Com1Buf.oldlcr); break; case 2: dataport = *((int far *)0x402L); Com2Buf.oldlcr = inportb (dataport + COM_LCR); Com2Buf.oldmcr = inportb (dataport + COM_MCR); outportb (dataport + COM_LCR,Com2Buf.oldlcr & 0x7F); Com2Buf.oldier = inportb (dataport + COM_IER); outportb (dataport + COM_LCR,Com2Buf.oldlcr | 0x80); Com2Buf.oldbaud = inport (dataport); outportb (dataport + COM_LCR,Com2Buf.oldlcr); break; default: break; } } /* Set the communications parameters for a COM port */ void comparams (port,params) int port,params; { static int divisors[11] = /* COM port baud rate divisors */ {0x0417,0x0300,0x0180,0x00E0,0x0060, 0x0030,0x0018,0x000C,0x0006,0x0003, 0x0001}; int value,dataport; switch (port) { case 1: dataport = Com1Buf.dataport; break; case 2: dataport = Com2Buf.dataport; break; default: return; } if (params & BITS_8) value = 0x03; else value = 0x02; if (params & STOP_2) value |= 0x04; if (params & PARITY_ODD) value |= 0x08; else if (params & PARITY_EVEN) value |= 0x18; outportb (dataport + COM_LCR,value | 0x80); /* Set params and DLAB */ outport (dataport,divisors[params & 0x0F]); /* Set the baud rate */ outportb (dataport + COM_LCR,value); /* Clear DLAB bit */ } /* Disable the interrupt I/O for a COM port */ /* If 'leavedtr' != 0, then leave DTR up */ void comdisable(port,leavedtr) int port,leavedtr; { char ch; switch(port){ case 1: ch = inportb(PIC_MASK); /* Get 8259A (PIC) Mask */ ch |= INT_MASK_1; /* Set Interrupt Mask COM1 */ outportb(PIC_MASK,ch); /* Write int. mask to 8259A*/ /* Clear the interrupt enable register */ outportb(Com1Buf.dataport + COM_IER,0); /* Clear OUT2, and set DTR as required */ if (leavedtr) outportb(Com1Buf.dataport + COM_MCR,1); else outportb(Com1Buf.dataport + COM_MCR,0); setvect(COM_INT_1,old_c1);/* Restore COM1 int */ break; case 2: ch = inportb(PIC_MASK); /* Get 8259A (PIC) Mask */ ch |= INT_MASK_2; /* Set Interrupt Mask COM1 */ outportb(PIC_MASK,ch); /* Write int. mask to 8259A*/ /* Clear the interrupt enable register */ outportb(Com2Buf.dataport + COM_IER,0); /* Clear OUT2, and set DTR as required */ if (leavedtr) outportb(Com2Buf.dataport + COM_MCR,1); else outportb(Com2Buf.dataport + COM_MCR,0); setvect(COM_INT_2,old_c2);/* Restore COM2 int */ break; default:break; } } /* Restore the setting of a COM port - after 'comdisable' */ /* *** NOTE: haven't got this working properly yet! *** */ void comrestore (port) int port; { int dataport; switch (port) { case 1: dataport = *((int far *)0x400L); outportb (dataport + COM_LCR,Com1Buf.oldlcr & 0x7F); outportb (dataport + COM_IER,Com1Buf.oldier); outportb (dataport + COM_LCR,Com1Buf.oldlcr | 0x80); outport (dataport,Com1Buf.oldbaud); outportb (dataport + COM_LCR,Com1Buf.oldlcr); outportb (dataport + COM_MCR,Com1Buf.oldmcr); break; case 2: dataport = *((int far *)0x402L); outportb (dataport + COM_LCR,Com1Buf.oldlcr & 0x7F); outportb (dataport + COM_IER,Com1Buf.oldier); outportb (dataport + COM_LCR,Com1Buf.oldlcr | 0x80); outport (dataport,Com1Buf.oldbaud); outportb (dataport + COM_LCR,Com1Buf.oldlcr); outportb (dataport + COM_MCR,Com1Buf.oldmcr); break; default: break; } } /* Output a character to an I/O port */ static void dosend(dataport,statport,ch) int dataport,statport,ch; { while (!(inportb(statport) & 0x20)) ; /* wait till port is available */ outportb(dataport,ch); } /* Output a character to a COM port */ void comsend(port,ch) int port; unsigned char ch; { switch(port) { case 1: dosend (Com1Buf.dataport,Com1Buf.statport,ch); break; case 2: dosend (Com2Buf.dataport,Com2Buf.statport,ch); break; default:break; } } /* Test to see if a carrier is present */ int comcarrier (port) int port; { switch (port) { case 1: return ((inportb (Com1Buf.dataport + COM_MSR) & 0x80) != 0); case 2: return ((inportb (Com2Buf.dataport + COM_MSR) & 0x80) != 0); default: return 0; } } /* Test to see if the DSR (Data Set Ready) signal is present */ int comdsr (port) int port; { switch (port) { case 1: return ((inportb (Com1Buf.dataport + COM_MSR) & 0x20) != 0); case 2: return ((inportb (Com2Buf.dataport + COM_MSR) & 0x20) != 0); default: return 0; } } /* Test to see if the COM port is ready for a new */ /* character to transmit. */ int comready (port) int port; { switch (port) { case 1: return ((inportb (Com1Buf.statport) & 0x20) != 0); case 2: return ((inportb (Com2Buf.statport) & 0x20) != 0); default: return 0; } } /* Drop the DTR signal on a COM port */ void comdropdtr (port) int port; { switch (port) { case 1: outportb (Com1Buf.dataport + COM_MCR,0x0A | Com1Buf.testbit); break; case 2: outportb (Com2Buf.dataport + COM_MCR,0x0A | Com2Buf.testbit); break; } } /* Raise the DTR signal on a COM port */ void comraisedtr (port) int port; { switch (port) { case 1: outportb (Com1Buf.dataport + COM_MCR,0x0B | Com1Buf.testbit); break; case 2: outportb (Com2Buf.dataport + COM_MCR,0x0B | Com2Buf.testbit); break; default: break; } } /* Set the BREAK pulse on a COM port to 0 or 1 */ void combreak (port,value) int port,value; { switch (port) { case 1: outportb (Com1Buf.dataport + COM_LCR, (value ? inportb (Com1Buf.dataport + COM_LCR) | 0x40 : inportb (Com1Buf.dataport + COM_LCR) & 0xBF)); case 2: outportb (Com2Buf.dataport + COM_LCR, (value ? inportb (Com2Buf.dataport + COM_LCR) | 0x40 : inportb (Com2Buf.dataport + COM_LCR) & 0xBF)); default: break; } } ------------------------------cut-here--THE-END-------------------------------- +===============================+==============================+ || Rhys Weatherley | University of Queensland, || || rhys@batserver.cs.uq.oz.au | Australia. G'day!! || +===============================+==============================+