Serial Com

Reino de Boer reino at cs.eur.nl
Thu Nov 23 20:20:18 AEST 1989


mike at relgyro.stanford.edu (Mike Macgirvin) writes:

>In article <89111504064675 at masnet.uucp> mark.longo at canremote.uucp (MARK LONGO) writes:
>>
>>  My `SEND' routine works fine , but I am having trouble with the
>>receive part.  Basically what happens is that characters are not `all'
>>being read from the com port.
>>
>[other text deleted]
>	This is a major headache, I agree. It boils down to the fact that
>the BIOS serial port functions do NOT work in interrupt mode. That is,
> ......[text deleted]
>	The painful solution is to write (or purchase) an interrupt handler
>for the serial port. There is a wealth of literature available on the
> ......[text deleted]
>handler support. (Can someone supply a vendor?)

The following is some code in Turbo C, which works at our site, 
hope it helps -- Reino

/*  globdefs.h */

/*  portability questions */
#define DOS_ERRNO _doserrno
#define CREATE_TEXT "wt"

/*  documentation defines */
#define PRIVATE static
#define PUBLIC
#define IMPORT  extern
#define FALSE 0
#define TRUE  1
#define ENDSTR '\0'

/*  generic NULL value */
#define NIL( type ) (( type * ) NULL)

/*  IGNORE( f ) ignores the result of calling f in a graceful way */
#define IGNORE( value ) ( void ) (value)

/*  convert any value to TRUE or FALSE */
#define BOOLEAN( b ) ( (b) ? TRUE : FALSE )

#ifndef GLOBDEFS
#define GLOBDEFS

/*  useful type definitions */

typedef unsigned char byte;
typedef int bool;
typedef char *string;
typedef unsigned int word;
struct file_id_s {
	string filename;
	string version;
};
typedef struct file_id_s file_id_t;
#endif

/*****************************************************************************/
/*  timer.h */

#define ONE_SECOND 19	/*  actually 18.2 */

/*  interface functions */
#define CLOCKTICKS( ) clockticks
#define TIMED_OUT( start, period ) ( NoTimer ? delay( 500 ), TRUE :\
	BOOLEAN( CLOCKTICKS( ) - (start) > (period) ) )
#define WAIT( msecs ) delay( msecs )

/*  should be called before the others */
IMPORT void init_timer( void );

/*  the following should never be used directly */
IMPORT long clockticks;
IMPORT bool NoTimer;

/*****************************************************************************/
/*  timer.c */

#include "globdefs.h"
#include <dos.h>
#include <stdlib.h>

/*  source file identification */
PRIVATE file_id_t FileId = { __FILE__, "%I%" };

/*  exported variables: */
/*  clockticks contains the system dependent clock count. It is increased
    approximately 18.2 times a second. */
/*  NoTimer controls whether clockticks can be used */
PUBLIC long clockticks = 0L;
PUBLIC bool NoTimer = TRUE;

/*  private variables: */
/*  old_timer contains the old vector for the timer interrupt */
PRIVATE void ( interrupt *old_timer )( void );

/*  interrupt number of software timer interrupt */
#define TIMER_INT 0x1c

/*  isr for timer interrupt. Updates clockticks and then chains to the old
	isr which may be in use for other purposes. */
PRIVATE void interrupt timer( void )
{
	clockticks++;
	( *old_timer )();
}

/*  graceful exit */
PRIVATE void exit_timer( void )
{
	NoTimer = TRUE;
	setvect( TIMER_INT, old_timer );
}

/*  initialization routine for this module */
PUBLIC void init_timer( void )
{
	old_timer = getvect( TIMER_INT ); /*  get old vector */
	setvect( TIMER_INT, timer ); /*  install our isr */
	NoTimer = FALSE; /*  we can use clockticks now */
	IGNORE( atexit( exit_timer ) ); /*  prepare graceful exit */
}

/*****************************************************************************/
/*  bits.h */

/*  return value for only bit n set */
#define BIT( n ) ( 1 << (n) )

/*  return b with high bit on */
#define HIGH( b ) ( (b) | BIT( 7 ) )

/*****************************************************************************/
/*  serial.h */

/*  error bits for SerialError */
#define E_OVERRUN BIT( 1 )
#define E_PARITY  BIT( 2 )
#define E_FRAMING BIT( 3 )
#define BREAK_INT BIT( 4 ) /*  Break received */

#define USER_INT 0x0800
#define TIME_OUT 0x1000

/*  Error checking in LSR */
#define E_NONE  0x0000 /*  No error */
#define E_LOGIC 0x0100
#define ERROR_MASK ( E_OVERRUN | E_PARITY | E_FRAMING | BREAK_INT | E_LOGIC )

IMPORT volatile word SerialError;
IMPORT volatile bool Busy;

/*  Buffer handling */
#define BUFFER_SIZE 4096 /*  Must be a power of 2 */

/*  general buffer access routines */
IMPORT  int Inwaiting; /*  should never be used directly */
#define IN_FULL( )   ( Inwaiting  >= BUFFER_SIZE )
#define IN_EMPTY( )  ( Inwaiting  <= 0 )
#define IN_WAITING( )  ( Inwaiting > 0 )
#define IN_OVERFLOW  0x0200

IMPORT  int Outwaiting; /*  should never be used directly */
#define OUT_FULL( )  ( Outwaiting >= BUFFER_SIZE )
#define OUT_EMPTY( ) ( Outwaiting <= 0 )
#define OUT_WAITING( ) ( Outwaiting > 0 )
#define OUT_OVERFLOW 0x0400

IMPORT void dispose_garbage( void );
IMPORT void poll( bool sending );
IMPORT void send( byte b );
IMPORT bool receive( byte *b, bool sending );
IMPORT void do_break( void );
IMPORT void send_break( void );
IMPORT void reset_UART( void );
IMPORT void send_XOFF( void );
IMPORT void send_XON( void );

/*  should be called before any of the other routines */
IMPORT void init_com( void );

/*****************************************************************************/
/*  serial.c */

/* #define STAT /*  when in need of isr statistics */
/* #define M_DEBUG /*  when in need of i/o-debugging */

#include "globdefs.h"
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include "except.h"
#include "bits.h"
#include "ascii.h" /*  defines all ASCII codes as mnemonics */
#include "timer.h"
#include "uims.h" /*  defines message( ) */
#include "serial.h"

/*  source file identification */
PRIVATE file_id_t FileId = { __FILE__, "%I%" };

#ifdef M_DEBUG
#define DEBUGCH( c ) ( (c) == '\r' ? putch( '\n' ) : putch( (c) ) )
#endif

/*  chosen serial port */
#define COM1

/*  base addresses of serial ports */
#define COM1_BASE 0x3f8
#define COM2_BASE 0x2f8
#ifdef COM1
#define COM_BASE COM1_BASE
#else
#define COM_BASE COM2_BASE
#endif

/*  bit rate divisor table */
#define BPS_45_5  2532
#define BPS_50    2304
#define BPS_75    1536
#define BPS_110   1047
#define BPS_134_5 857
#define BPS_150   768
#define BPS_300   384
#define BPS_600   192
#define BPS_1200  96
#define BPS_1800  64
#define BPS_2000  58
#define BPS_2400  48
#define BPS_4800  24
#define BPS_9600  12
#define BPS_19200 6
#define BPS_38400 3

/*  chosen divisor */
#define BPS BPS_9600

/*  port offsets 0x00 and 0x01 from chosen base address */
/*  when DLAB is set in LCR */
#define BAUD0 ( COM_BASE + 0x00 )
#define BAUD1 ( COM_BASE + 0x01 )
/*  When DLAB isn't set in LCR */
#define RBR   ( COM_BASE + 0x00 ) /*  when read from  */
#define THR   ( COM_BASE + 0x00 ) /*  when written to */
#define IER   ( COM_BASE + 0x01 )

/*  other port offsets from chosen base address */
#define IIR   ( COM_BASE + 0x02 )
#define LCR   ( COM_BASE + 0x03 )
#define MCR   ( COM_BASE + 0x04 )
#define LSR   ( COM_BASE + 0x05 )
#define MSR   ( COM_BASE + 0x06 )

/*  port retrieval functions */
#define GET_RBR( ) inportb( RBR )
#define GET_IIR( ) inportb( IIR )
#define GET_LCR( ) inportb( LCR )
#define GET_MCR( ) inportb( MCR )
#define GET_LSR( ) inportb( LSR )
#define GET_MSR( ) inportb( MSR )

/*  port update procedures */
#define SET_THR( v ) outportb( THR, (v) )
#define SET_IER( v ) outportb( IER, (v) )
#define SET_LCR( v ) outportb( LCR, (v) )
#define SET_MCR( v ) outportb( MCR, (v) )

/*  bits for Interrupt Enable Register (IER) */
#define RBR_ENABLE BIT( 0 )
#define THR_ENABLE BIT( 1 )
#define LSR_ENABLE BIT( 2 )
#define MSR_ENABLE BIT( 3 )

/*  chosen enabled classes */
#define ENABLE_BITS ( RBR_ENABLE | THR_ENABLE | LSR_ENABLE | MSR_ENABLE )
#define ENABLE_ALL( )  SET_IER( ENABLE_BITS )
#define DISABLE_ALL( ) SET_IER( 0x00 )

/*  bits for Interrupt Identification Register (IIR) */
#define MSR_ID 0x00
#define NO_ID  0x01
#define THR_ID 0x02
#define RBR_ID 0x04
#define LSR_ID 0x06

/*  bits for Line Control Register (LCR) */
#define DATA_BITS_5 0x00
#define DATA_BITS_6 0x01
#define DATA_BITS_7 0x02
#define DATA_BITS_8 0x03
#define STOP_BITS_1 0x00
#define STOP_BITS_2 0x04 /*  1.5 stop bits when 5 data bits */
#define NO_PARITY   0x00
#define ODD_PARITY  0x08
#define EVEN_PARITY 0x18
#define PARITY_1    0x28
#define PARITY_2    0x38
#define BREAK_BIT BIT( 6 )
#define DLAB BIT( 7 ) /*  Divisor Latch Access Bit */

/*  chosen line control */
#define LINE_CONTROL ( DATA_BITS_8 | STOP_BITS_1 | NO_PARITY )

/*  Line Control Register access routines */
#define SET_BREAK( )   SET_LCR( LINE_CONTROL | BREAK_BIT )
#define CLEAR_BREAK( ) SET_LCR( LINE_CONTROL )
#define SET_DLAB( )    SET_LCR( LINE_CONTROL | DLAB )
#define CLEAR_DLAB( )  SET_LCR( LINE_CONTROL )

/*  bits for Modem Control Register (MCR) */
#define DTR       BIT( 0 )  /*  signal Data Terminal ready       */
#define RTS       BIT( 1 )  /*  request To Send                  */
#define OUT1      BIT( 2 )  /*  reset Hayes 1200b internal modem */
#define OUT2      BIT( 3 )  /*  controls 8250 interrupt signals  */
#define SELF_TEST BIT( 4 )  /*  turn on self-test configuration  */

/*  default settings for MCR */
#define MODEM_CONTROL ( DTR | RTS | OUT2 )

/*  bits for Line Status Register (LSR) */
#define RDR       BIT( 0 ) /*  Received Data Ready */

/*  The next bits are defined in serial.h
#define E_OVERRUN BIT( 1 )
#define E_PARITY  BIT( 2 )
#define E_FRAMING BIT( 3 )
#define BREAK_INT BIT( 4 ) Break received */
#define THR_EMPTY BIT( 5 )
#define TSR_EMPTY BIT( 6 )

/*  error checking in LSR is defined in serial.h
#define E_NONE  0x0000 No error
#define E_LOGIC 0x0100
#define ERROR_MASK ( E_OVERRUN | E_PARITY | E_FRAMING | BREAK_INT | E_LOGIC ) */

/*  bits for Modem Status Register (MSR) */
#define CTS_CHANGED BIT( 0 )
#define DSR_CHANGED BIT( 1 )
#define RI_CHANGED  BIT( 2 )
#define DCD_CHANGED BIT( 3 )
#define CTS_LEVEL   BIT( 4 )
#define DSR_LEVEL   BIT( 5 )
#define RI_LEVEL    BIT( 6 )
#define DCD_LEVEL   BIT( 7 )

/*  Modem Status Register access functions */
#define CTS_VALUE( ) ( ( ModemStatus & CTS_LEVEL ) ? 0x01 : 0x00 )
#define DSR_VALUE( ) ( ( ModemStatus & DSR_LEVEL ) ? 0x01 : 0x00 )
#define RI_VALUE( )  ( ( ModemStatus & RI_LEVEL  ) ? 0x01 : 0x00 )
#define DCD_VALUE( ) ( ( ModemStatus & DCD_LEVEL ) ? 0x01 : 0x00 )

/* Interrupt ReQuest lines */
#define COM1_IRQ 4
#define COM2_IRQ 3
#ifdef COM1
#define COM_IRQ COM1_IRQ
#else
#define COM_IRQ COM2_IRQ
#endif

/*  corresponding Hardware Interrupt Vectors */
#define COM1_INTR 0x0c
#define COM2_INTR 0x0b
#ifdef COM1
#define COM_INTR COM1_INTR
#else
#define COM_INTR COM2_INTR
#endif

/*  registers for Programmable Interrupt Controller (PIC) */
#define ISR 0x20
#define IMR 0x21

/*  send end-of-interrupt */
#define EOI( ) outportb( ISR, 0x20 )

/*  enable, disable interrupts from chosen port */
#define ENABLE( )  outportb( IMR, inportb( IMR ) & ~( 1 << COM_IRQ ) )
#define DISABLE( ) outportb( IMR, inportb( IMR ) |  ( 1 << COM_IRQ ) )

#ifdef STAT
PRIVATE long isrCalled  = 0L;
PRIVATE long isrData    = 0L;
PRIVATE long isrTwice   = 0L;
#endif
PRIVATE long UARTresets = 0L;

/*  defined in serial.h
#define USER_INT 0x0800
#define TIME_OUT 0x1000 */

/*  periods to wait for outgoing or incoming data */
#define OUT_PERIOD ( 2L * ONE_SECOND )
#define IN_PERIOD 6

/*  some handy delay definitions */
#define WAIT( msecs ) delay( msecs )
#define WASTE_CYCLES( ) WAIT( 100 )

/*  length of a break signal in milliseconds */
#define BREAK_LEN 300

/*  exported variables: */
/*  SerialError contains accumulated errors from the serial module */
PUBLIC	volatile word SerialError = E_NONE;

/*  private variables: */
/*  LineStatus contains last value read from LSR */
/*  THR_free is TRUE if THR is empty, otherwise FALSE */
/*  ModemStatus contains last value read from MSR */
/*  Cts contains value of CTS in ModemStatus */
/*  Dsr contains value of DSR in ModemStatus */
/*  Ri contains value of RI in ModemStatus */
/*  Dcd contains value of DCD in ModemStatus */
/*  old_isr contains old interrupt vector for the chosen serial port */
/*  VectorSet is TRUE if old_isr contains valid data, otherwise FALSE */
/*  Installed is TRUE if installation was successful, otherwise FALSE */
/*  disposing is TRUE when disposing garbage */
PRIVATE volatile byte LineStatus = 0;
PRIVATE volatile bool THR_free = FALSE;
PRIVATE volatile byte ModemStatus = 0;
PRIVATE volatile byte Cts = 0;
PRIVATE volatile byte Dsr = 0;
PRIVATE volatile byte Ri  = 0;
PRIVATE volatile byte Dcd = 0;
PRIVATE void ( interrupt *old_isr )( void );
PRIVATE bool VectorSet = FALSE;
PRIVATE bool Installed = FALSE;
PRIVATE bool disposing = FALSE;

/*  exported variables: */
/*  Busy is TRUE if channel is busy, otherwise FALSE */
PUBLIC  volatile bool Busy;

/*  buffer handling */
/*  Defined in serial.h
#define BUFFER_SIZE 4096	    Must be a power of 2 */
/*  INDEX_MASK speeds up buffer operations. If ever BUFFER_SIZE is not a
    power of 2, INDEX_MASK becomes useless:
    x &= INDEX_MASK then becomes x %= BUFFER_SIZE */
#define INDEX_MASK  ( BUFFER_SIZE - 1 )
/*  buffers are arrays of byte */
typedef byte buffer_t[BUFFER_SIZE];

/*  The input buffer contains incoming data stored by the isr( ), in a
	sequential circular queue. The input buffer is emptied by receive( ) */
PRIVATE buffer_t Indata;
PUBLIC  int Inwaiting  = 0;
PRIVATE int Infront    = 0;
PRIVATE int Inrear     = 0;
/*  defined in serial.h
#define IN_FULL( )   ( Inwaiting  >= BUFFER_SIZE )
#define IN_EMPTY( )  ( Inwaiting  <= 0 )
#define IN_WAITING( )  ( Inwaiting > 0 )
#define IN_OVERFLOW  0x0200 */

/*  The output buffer is filled by send( ), and emptied by poll( TRUE ).
    It is a sequential circular queue, as the input buffer */
PRIVATE buffer_t Outdata;
PUBLIC  int Outwaiting = 0;
PRIVATE int Outfront   = 0;
PRIVATE int Outrear    = 0;
/*  defined in serial.h
#define OUT_FULL( )  ( Outwaiting >= BUFFER_SIZE )
#define OUT_EMPTY( ) ( Outwaiting <= 0 )
#define OUT_WAITING( ) ( Outwaiting > 0 )
#define OUT_OVERFLOW 0x0400 */

/*  store b in the input buffer.
    If the buffer is full, the output overflow bit is set in SerialError;
    this should be considered a serious error.
    This routine also does some essential Multilink handling, although it
    never eats any data. */
PRIVATE void put_in( byte b )
{
	if( IN_FULL( ) )
		SerialError |= IN_OVERFLOW;
	else {
		if( b == XOFF )
			Busy = TRUE;
		else if( b == XON )
			Busy = FALSE;
		else {
			Indata[Inrear++] = b; /*  normal buffering */
			Inrear &= INDEX_MASK;
			Inwaiting++;
		}
	}
}

/*  get next byte from input buffer. Used only by receive( ). Returns
    TRUE if there was data, FALSE if queue was empty */
PRIVATE bool get_in( byte *b )
{
	if( IN_EMPTY( ) )
		return( FALSE );
	else {
		disable( ); /*  temporarily disable interrupts */
		*b = Indata[Infront++];
		Infront &= INDEX_MASK;
		Inwaiting--;
		enable( ); /*  free to be interrupted again */
		return( TRUE );
	}
}

/*  put b into the output buffer for later processing by poll( ).
    If output buffer is full, the output overflow bit in SerialError is set */
PRIVATE void put_out( byte b )
{
	if( OUT_FULL( ) )
		SerialError |= OUT_OVERFLOW;
	else {
		Outdata[Outrear++] = b;
		Outrear &= INDEX_MASK;
		Outwaiting++;
	}
}

/*  get next byte from output buffer to be sent to the port. Returns
    FALSE if buffer was empty, otherwise TRUE */
PRIVATE bool get_out( byte *b )
{
	if( OUT_EMPTY( ) )
		return( FALSE );
	else {
		*b = Outdata[Infront++];
		Outfront &= INDEX_MASK;
		Outwaiting--;
		return( TRUE );
	}
}

/*  interrupt service routine for chosen serial port. It is advisable
    to keep all local data static (PRIVATE) */
PRIVATE void interrupt isr( void )
{
	PRIVATE byte id;
	PRIVATE byte data;

#ifdef STAT
	isrCalled++;
#endif
	id = GET_IIR( ); /*  get cause of interrupt */
	enable( ); /*  enable other interrupts */
reswitch:
	if( ( id & MSR_ID ) == MSR_ID )
	{ /*  modem status change */
		ModemStatus = GET_MSR( );
		if( ModemStatus & CTS_CHANGED )
			Cts = CTS_VALUE( );
		if( ModemStatus & DSR_CHANGED )
			Dsr = DSR_VALUE( );
		if( ModemStatus & RI_CHANGED )
			Ri = RI_VALUE( );
		if( ModemStatus & DCD_CHANGED )
			Dcd = DCD_VALUE( );
	}
	if( ( id & LSR_ID ) == LSR_ID )
	{ /*  line status change. Error ? */
		LineStatus = GET_LSR( );
		SerialError |= ( LineStatus & ERROR_MASK );
		if( LineStatus & RDR )
		{ /*  incoming data in RBR */
			data = GET_RBR( ); /*  get data a.s.a.p */
			put_in( data ); /*  store for later processing */
#ifdef STAT
			isrData++;
#endif
		}
		if( LineStatus & THR_EMPTY )
		{ /*  THR empty. Free to send again */
			THR_free = TRUE;
		}
	}
	else
	{
		if( ( id & RBR_ID ) == RBR_ID )
		{ /*  incoming data in RBR */
			data = GET_RBR( ); /*  get data a.s.a.p */
			put_in( data ); /*  store for later processing */
#ifdef STAT
			isrData++;
#endif
		}
		else if( ( id & THR_ID ) == THR_ID )
		{ /*  THR empty. Free to send again */
			THR_free = TRUE;
		}
	}
	id = GET_IIR( ); /*  see if there's another interrupt pending */
	if( id != NO_ID )
	{
#ifdef STAT
		isrTwice++;
#endif
		goto reswitch; /*  handle it immediately */
	}
	disable( ); /*  disable interrupts */
	EOI( ); /*  send end-of-interrupt */
}

/*  try to send one byte to the serial port. Return TRUE on success, FALSE
    on failure */
PRIVATE bool out_byte( byte b )
{
	PRIVATE long start; /*  used in timing loops */

	/*  RS-232C protocol:
	    DTR high (static condition)
		DSR high (should be static condition)

	    set RTS high (always set in Multilink protocol)
	    wait for CTS to become high

	    wait for THR to become empty (UART signal)

	    The waiting is done in loops that time out when nothing changes
	    during the OUT_PERIOD. */

	while( ! ( Dsr && Cts && THR_free ) ) {
		if( ! Dsr ) /*  should never happen, check anyway */ {
			start = CLOCKTICKS( );
			while( ! Dsr && ! TIMED_OUT( start, OUT_PERIOD ) ) {
				/*  Do nothing */
			}
			if( ! Dsr ) {
				SerialError |= TIME_OUT;
				ERROR_MSG( "wachten op DSR duurt te lang" );
				return( FALSE );
			}
		}
		if( ! Cts ) {
			start = CLOCKTICKS( );
			while( ! Cts && ! TIMED_OUT( start, OUT_PERIOD ) ) {
				/*  Do nothing */
			}
			if( ! Cts ) {
				SerialError |= TIME_OUT;
				ERROR_MSG( "wachten op CTS duurt te lang" );
				return( FALSE );
			}
		}
		if( ! THR_free ) {
			start = CLOCKTICKS( );
			while( ! THR_free && ! TIMED_OUT( start, OUT_PERIOD ) ) {
				/*  Do nothing */
			}
			if( ! THR_free ) {
				SerialError |= TIME_OUT;
				ERROR_MSG( "wachten op THR duurt te lang" );
				return( FALSE );
			}
		}
	}
	SET_THR( b ); /*  actually send byte */
	THR_free = FALSE; /*  update global THR status */
#ifdef M_DEBUG
	DEBUGCH( b );
#endif
	return( TRUE );
}

/*  try to send as many bytes as possible */
PRIVATE void out_bytes( void )
{
	PRIVATE byte b;

	if( Busy ) {
		int x, y;

		x = wherex( );
		y = wherey( );
		putch( '*' );
		while( Busy ) {
			poll( FALSE );
			SET_MCR( MODEM_CONTROL ); /*  establish modem control */
		}
		gotoxy( x, y );
		putch( ' ' );
		gotoxy( x, y );
	}
	while( OUT_WAITING( ) ) {
		if( INTERRUPTED( ) )
		{ /*  user interrupted */
			SerialError |= USER_INT;
			return;
		}
		b = Outdata[Outfront]; /*  peek ahead to lose nothing */
		if( out_byte( b ) ) /*  update output buffer */
			IGNORE( get_out( &b ) );
		else
			return;
	}
}

/*  interface routine to poll the port. Use only when sending is
	necessary.
    If sending = TRUE and no errors occurred try to send as many bytes
    as possible. Earlier versions also aborted on serial errors */
PUBLIC void poll( bool sending )
{
	if( sending ) /*  send as many bytes as possible */
		out_bytes( );
	else if( INTERRUPTED( ) )
		SerialError |= USER_INT;
	if( SerialError )
	{
		SerialError &= ~TIME_OUT;
		if( INTERRUPTED( ) )
			FATAL_MSG( "onderbroken" );
		return;
	}
}

/*  interface routine to send a byte to the serial port.
    If it is the only byte to be sent, try to send it immediately, otherwise
    store for later processing by poll( ) */
PUBLIC void send( byte b )
{
	if( Busy ) {
		int x, y;

		x = wherex( );
		y = wherey( );
		putch( '*' );
		while( Busy ) {
			poll( FALSE );
			SET_MCR( MODEM_CONTROL ); /*  establish modem control */
		}
		gotoxy( x, y );
		putch( ' ' );
		gotoxy( x, y );
	}
	if( OUT_EMPTY( ) ) {
		if( ! out_byte( b ) )
		{ /*  sending failed, store for processing by poll */
			put_out( b );
			poll( TRUE );
		}
	}
	else {
		put_out( b );
		poll( TRUE );
	}
}

/*  interface routine to retrieve data from the serial port. Returns TRUE
    if data was present, otherwise returns FALSE. receive( ) waits for
	IN_PERIOD to see if data is coming in. If no data was received,
	poll is called with parameter sending. */
PUBLIC bool receive( byte *b, bool sending )
{
	PRIVATE long start;

	start = CLOCKTICKS( );
	do {
		if( get_in( b ) ) {
#ifdef M_DEBUG
			DEBUGCH( *b );
#endif
			return( TRUE );
		}
		else if( INTERRUPTED( ) ) {
			SerialError |= USER_INT;
			return( FALSE );
		}
	}
	while( ! TIMED_OUT( start, IN_PERIOD ) );
	SerialError |= TIME_OUT;
	poll( sending );
	return( FALSE );
}

/*  dispose of all incoming data. Resets TIME_OUT bit in SerialError */
PUBLIC void dispose_garbage( void )
{
	byte junk;

	disposing = TRUE;
	while( receive( &junk, TRUE ) && ! INTERRUPTED( ) ) {
		/*  Do nothing */
	}
	disposing = FALSE;
	if( INTERRUPTED( ) )
		SerialError |= USER_INT;
	SerialError &= ~TIME_OUT;
}

/*  send a break signal and dispose of all incoming data */
PUBLIC void do_break( void )
{
	SET_BREAK( );
	WAIT( BREAK_LEN );
	CLEAR_BREAK( );
	WASTE_CYCLES( );
}

PUBLIC void send_break( void )
{
	do_break( );
	dispose_garbage( );
}

/*  init variables for this module */
PRIVATE void init_com_data( void )
{
	Busy = FALSE;
	do /*   initialize data per existing signals, and handle all
		possible interrupts pending */ {
		LineStatus = GET_LSR( );
		THR_free = BOOLEAN( LineStatus & THR_EMPTY );
		( void ) GET_RBR( );
		ModemStatus = GET_MSR( );
		Cts = CTS_VALUE( );
		Dsr = DSR_VALUE( );
		Ri = RI_VALUE( );
		Dcd = DCD_VALUE( );
	}
	while( GET_IIR( ) != NO_ID );
}

/*  reset UART to a predefined state */
PRIVATE void reset_chip( void )
{
	disable( ); /*  disable all interrupts */
	DISABLE_ALL( ); /*  disable all interrupt types for the serial port */
	SET_MCR( GET_MCR( ) & ~MODEM_CONTROL ); /*  disable modem control */
	DISABLE( ); /*  disable interrupts for the serial port */
	enable( ); /*  enable all other interrupts */
}

/*  as graceful an exit as possible */
PRIVATE void exit_com( void )
{
	if( VectorSet ) /*  restore old interrupt vector */ {
		VectorSet = FALSE;
		setvect( COM_INTR, old_isr );
	}
	if( Installed ) /*  reset UART */ {
		Installed = FALSE;
		reset_chip( );
	}
#ifdef STAT
	if( Verbose ) {
		IGNORE( fprintf( stderr, "\nisr called %ld\n", isrCalled ) );
		IGNORE( fprintf( stderr, "isr data received %ld\n", isrData ) );
		IGNORE( fprintf( stderr, "isr reswitches %ld\n", isrTwice ) );
		IGNORE( fprintf( stderr, "UART resets %ld\n", UARTresets ) );
	}
#endif
}

PUBLIC void reset_UART( void )
{
	send_XOFF( );
#ifdef STAT
	UARTresets++;
#endif
	reset_chip( );
	disable( ); /*  temporarily disable all interrupts */
	SET_MCR( MODEM_CONTROL ); /*  establish modem control */
	ENABLE( ); /*  enable interrupts for the serial port */
	ENABLE_ALL( ); /*  enable all interrupt types for the serial port */
	EOI( ); /*  send an end-of-interrupt, just in case */
	enable( ); /*  free to be interrupted again */
	if( UARTresets == 1L ) {
		SET_DLAB( ); /*  program the baud rate generator */
		outport( BAUD0, BPS ); /*  set chosen bit-rate */
		CLEAR_DLAB( ); /*  return to normal operation */
	}
	WASTE_CYCLES( ); /*  give PBRG and UART some time to settle */
	send_XON( );
}

PUBLIC void send_XOFF( void )
{
	out_byte( HIGH( DLE ) );
	out_byte( HIGH( SO ) );
}

PUBLIC void send_XON( void )
{
	out_byte( HIGH( DLE ) );
	out_byte( HIGH( SI ) );
}

/*  initialize serial module */
PUBLIC void init_com( void )
{
	long start;

	( void ) atexit( exit_com ); /*  chain exit-proc */
	init_com_data( );
	if( ! VectorSet ) /*  retrieve old interrupt vector */ {
		old_isr = getvect( COM_INTR );
		VectorSet = TRUE;
	}
	setvect( COM_INTR, isr ); /*  install isr( ) */
	reset_UART( );
	Installed = TRUE;
	start = CLOCKTICKS( );
	while( ! Cts ) /*  wait for Cts to come up */ {
		if( TIMED_OUT( start, ONE_SECOND ) ) {
			FATAL_MSG(
				"geen node aangesloten ?..(wachten op CTS duurt te lang)" );
		}
	}
}

The code we use id in fact better documented, but the documentation
would take up much more bandwidth.

-- 
Reino R. A. de Boer
Erasmus University Rotterdam ( Informatica )
e-mail: reino at cs.eur.nl



More information about the Comp.lang.c mailing list