home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_09_12
/
9n12064a
< prev
next >
Wrap
Text File
|
1991-07-27
|
19KB
|
615 lines
/*** LISTING 1 ** UARTAPI.C **************************
Application Programming Interface for
UART Asynch Driver.
*****************************************************/
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <dos.h>
#include "uart.h"
#include "uartmacs.c"
/* unused interrupt vector to redirect Ctrl-Break to */
#define UNUSEDVECT 0x48
/* Ctrl-Break vector */
#define CTRL_BRKVECT 0x23
/* Table for converting baud rate to divisor
settings */
static struct
{
unsigned long baud_rate;
unsigned char divisor_high_byte;
unsigned char divisor_low_byte;
} gas_baud_to_divisor[] =
{
{ 50L, 0x09, 0x00 },
{ 75L, 0x06, 0x00 },
{ 110L, 0x04, 0x17 },
{ 134L, 0x03, 0x59 },
{ 150L, 0x03, 0x00 },
{ 300L, 0x01, 0x80 },
{ 600L, 0x00, 0xC0 },
{ 1200L, 0x00, 0x60 },
{ 1800L, 0x00, 0x40 },
{ 2000L, 0x00, 0x3A },
{ 2400L, 0x00, 0x30 },
{ 3600L, 0x00, 0x20 },
{ 4800L, 0x00, 0x18 },
{ 7200L, 0x00, 0x10 },
{ 9600L, 0x00, 0x0C },
{ 19200L, 0x00, 0x06 },
{ 38400L, 0x00, 0x03 },
{ 57600L, 0x00, 0x02 },
{ 115200L, 0x00, 0x01 },
{ 0L, 0x00, 0x00 },
} ;
/* Space to save original asynch interrupt vector */
static void (interrupt far * gp_orig_asynch_vector)();
/* to save original Ctrl-Break vector */
static void (interrupt far *gp_ctrlbrk_vector)();
/*****************************************************
rc = Init_UART()
rc = 1 : UART(s) intialized OK
rc = -1: Failure in initialization.
Before calling this function, you must allocate and
initialize the gp_port_info array to describe
the UART(s) you want to drive, and you must set
g_uart_irq to the IRQ level used (all ports must
use the same IRQ level).
This function latches the UART's interrupt driver
into the system interrupt table and initializes the
UART(s). If successful, it registers the
Exit_UART function to be called during system
shutdown.
*****************************************************/
int Init_UART(void)
{
static char a_funcname[] = "Init_UART:";
register int value_from_port;
unsigned int i;
int retcode;
unsigned char cur_port;
struct t_port_info *p_cur_port_info;
void (interrupt far * p_unusedvect)();
/* Verify that calling program has initialized
info structures and variables */
if (g_num_ports < 1)
{
fprintf(stderr,"%s g_num_ports (%u) < 1\n",
a_funcname,g_num_ports);
return(-1);
}
if (gp_port_info == NULL)
{
fprintf(stderr,"%s gp_port_info is NULL\n",
a_funcname);
return(-1);
}
if ( (g_uart_irq < 3) || (g_uart_irq > 7) )
{
fprintf(stderr,"%s g_uart_irq (%u) not 3-7\n",
a_funcname,g_uart_irq);
return(-1);
}
/* Point at entry for first port */
p_cur_port_info = gp_port_info;
/* Loop through array of port info entries and
initialize each port */
for (cur_port=1;cur_port<=g_num_ports;cur_port++)
{
/* Initialize internal variables in port
info entry */
p_cur_port_info->tx_head = 0;
p_cur_port_info->tx_tail = 0;
p_cur_port_info->rx_head = 0;
p_cur_port_info->rx_tail = 0;
p_cur_port_info->non_int_chars = 0;
/* Check for valid port on this IRQ and at
this base address */
retcode = Test_for_asynch_port(
p_cur_port_info->base_address,g_uart_irq);
if (retcode == -1)
{
fprintf(stderr,
"%s port %u-no UART at %.4Xh on IRQ %u\n",
a_funcname,cur_port,
p_cur_port_info->base_address,
g_uart_irq);
return(-1);
}
/* Validate modem parameters */
if ( (p_cur_port_info->stop_bits < 1) ||
(p_cur_port_info->stop_bits > 2) )
{
fprintf(stderr,
"%s port %u-stop bits (%u) not 1 or 2\n",
a_funcname,cur_port,
p_cur_port_info->stop_bits);
return(-1);
}
switch (p_cur_port_info->parity)
{
case 0 :
case 1 :
case 3 :
break;
default :
fprintf(stderr,
"%s port %u-parity (%u) not 0,1,or 3\n",
a_funcname,cur_port,
p_cur_port_info->parity);
return(-1);
}
if ( (p_cur_port_info->data_bits < 5) ||
(p_cur_port_info->data_bits > 8) )
{
fprintf(stderr,
"%s port %u-data bits (%u) not 5-8\n",
a_funcname,cur_port,
p_cur_port_info->data_bits);
return(-1);
}
/* Translate specified baud rate into
divisor settings */
for (i=0;
gas_baud_to_divisor[i].baud_rate != 0L;
i++)
{
if (p_cur_port_info->baud_rate ==
gas_baud_to_divisor[i].baud_rate)
break;
}
if (gas_baud_to_divisor[i].baud_rate == 0L)
{
fprintf(stderr,
"%s port %u-invalid baud rate %lu\n",
a_funcname,cur_port,
p_cur_port_info->baud_rate);
return(-1);
}
/* Make sure interrupt generation is disabled */
outp(p_cur_port_info->base_address+
INTERRUPT_ENABLE_REGISTER,0x00);
/* Delay for port to catch up */
IO_DELAY(3);
/* Enable writing to divisor latch */
outp(p_cur_port_info->base_address+
LINE_CONTROL_REGISTER,0x80);
/* Delay for port to catch up */
IO_DELAY(3);
/* Set the low byte of the divisor latch */
outp(p_cur_port_info->base_address+
DIV_LATCH_LOW_BYTE,
gas_baud_to_divisor[i].divisor_low_byte);
/* Delay for port to catch up */
IO_DELAY(3);
/* Set the low byte of the divisor latch */
outp(p_cur_port_info->base_address+
DIV_LATCH_HIGH_BYTE,
gas_baud_to_divisor[i].divisor_high_byte);
/* Delay for port to catch up */
IO_DELAY(3);
/* Set parity, data bits, stop bits, and
disable writing to divisor latch */
value_from_port = (int)
( ((p_cur_port_info->stop_bits - 1) << 2 ) |
( p_cur_port_info->parity << 3 ) |
( p_cur_port_info->data_bits - 5 ) ) ;
outp(p_cur_port_info->base_address +
LINE_CONTROL_REGISTER, value_from_port);
/* Delay for port to catch up */
IO_DELAY(3);
/* Initialize Modem Control Register - raise the
OUT2 signal */
outp(p_cur_port_info->base_address +
MODEM_CONTROL_REGISTER,0x08);
/* Delay for port to catch up */
IO_DELAY(3);
/* Enable the FIFO and set it to generate rcv
interrupts only when fifo has more than 8
characters (if UART doesn't have a fifo -
it is an older UART - this instruction will
do nothing). */
outp(p_cur_port_info->base_address +
FIFO_CONTROL_REGISTER,FIFO_ENABLE);
/* Delay for port to catch up */
IO_DELAY(3);
/* Read the interrupt identification register
to determine if the FIFO is enabled. If the
fifo is enabled, use it to transmit multiple
characters. */
value_from_port =
inp(p_cur_port_info->base_address +
INTERRUPT_IDENT_REGISTER);
if ( (value_from_port & IIR_FIFO_ENABLED) ==
IIR_FIFO_ENABLED)
/* Port has a fifo so it can transmit 16
characters at once. */
p_cur_port_info->max_tx_chars = 16;
else
p_cur_port_info->max_tx_chars = 1;
/* If port is not going to use hardware flow
control, raise the Request To Send signal
and keep it on since some modems will not
transmit unless this signal is raised. */
if ( !p_cur_port_info->obey_rts_cts)
{
RAISE_RTS;
}
/* Point at next port info struct in array */
p_cur_port_info++;
} /* End Initialize UART loop */
/* Save current interrupt vector */
gp_orig_asynch_vector =
_dos_getvect( (0x08 + g_uart_irq) );
/* get original Ctrl-Break vector */
gp_ctrlbrk_vector = _dos_getvect(CTRL_BRKVECT);
/* get an unused vector to redirect Ctrl-Break to */
p_unusedvect = _dos_getvect(UNUSEDVECT);
/* Register UART Shutdown for execution on
program exit */
atexit(Exit_UART);
/* disable interrupts */
_disable();
/* set Ctrl_brk to unused vector */
_dos_setvect(CTRL_BRKVECT, p_unusedvect);
/* Latch new UART interrupt routine into vector
table */
_dos_setvect( (0x08 + g_uart_irq), Asynch_driver );
/* Get current 8259 interrupt controller mask */
value_from_port = inp(R8259A_MASK);
/* Delay for port to catch up */
IO_DELAY(3);
/* Turn off bit for asynch interrupt so that
it will be enabled. Note: 0x01 << g_uart_irq
is the equivalent of 2 raised to the
g_uart_irq-th power */
value_from_port &= (0xff ^ ( 0x01 << g_uart_irq) );
/* Output new mask to 8259 interrupt controller */
outp(R8259A_MASK,value_from_port);
/* Point at entry for first port */
p_cur_port_info = gp_port_info;
/* Loop through array of port info entries and
enable interrupts for each port */
for (cur_port=1;cur_port<=g_num_ports;cur_port++)
{
/* set interrupt enable registers to generate
interrupts for everything (i.e., Transmit
Holding Register Empty, Character Received,
Receive Error, and Modem Status Change). */
outp(p_cur_port_info->base_address +
INTERRUPT_ENABLE_REGISTER,0x0F);
/* Delay for port to catch up */
IO_DELAY(3);
/* Clear interrupt identification register */
value_from_port =
inp(p_cur_port_info->base_address +
INTERRUPT_IDENT_REGISTER);
/* Delay for port to catch up */
IO_DELAY(3);
/* Clear receive register. */
value_from_port =
inp(p_cur_port_info->base_address +
RECEIVE_REGISTER);
/* Delay for port to catch up */
IO_DELAY(3);
/* Clear line status interrupt */
value_from_port =
inp(p_cur_port_info->base_address +
LINE_STATUS_REGISTER);
/* Delay for port to catch up */
IO_DELAY(3);
/* Clear modem status interrupt and initialize
modem status signal flags */
value_from_port =
inp(p_cur_port_info->base_address +
MODEM_STATUS_REGISTER);
if (IS_CTS(value_from_port))
p_cur_port_info->cts_state = 1;
else
p_cur_port_info->cts_state = 0;
if (IS_DSR(value_from_port))
p_cur_port_info->dsr_state = 1;
else
p_cur_port_info->dsr_state = 0;
if (IS_DCD(value_from_port))
p_cur_port_info->dcd_state = 1;
else
p_cur_port_info->dcd_state = 0;
/* Initialize modem status signal states */
/* Delay for port to catch up */
IO_DELAY(3);
/* Flush transmit and receive fifos */
outp(p_cur_port_info->base_address +
FIFO_CONTROL_REGISTER,
(FIFO_ENABLE | FCR_FLUSH_XMIT_FIFO |
FCR_FLUSH_RCV_FIFO) );
/* Delay for port to catch up */
IO_DELAY(3);
/* Turn on the Data Terminal Ready signal */
RAISE_DTR;
/* Point at next port info struct in array */
p_cur_port_info++;
} /* End interrupt enable loop */
/* Re-enable interrupts */
_enable();
return(1);
}
/*****************************************************
Exit_UART()
Resets UARTs, unlatches interrupt driver, and re-
enables control-break.
*****************************************************/
void Exit_UART(void)
{
register int value_from_port;
unsigned char cur_port;
struct t_port_info *p_cur_port_info;
/* If asynch not initialized, just return. */
if (gp_ctrlbrk_vector == NULL)
return;
/* disable interrupts */
_disable();
/* Get current 8259 interrupt controller mask */
value_from_port = inp(R8259A_MASK);
/* Delay for port to catch up */
IO_DELAY(3);
/* Turn on bit for asynch interrupt so that
it will be disabled. */
value_from_port |= ( 0x01 << g_uart_irq);
/* Output new mask to 8259 interrupt controller */
outp(R8259A_MASK,value_from_port);
/* Latch original UART interrupt routine into vector
table */
_dos_setvect( (0x08 + g_uart_irq),
gp_orig_asynch_vector);
/* Point at entry for first port */
p_cur_port_info = gp_port_info;
/* Loop through array of port info entries and
reset interrupt generation and modem signals
for each port */
for (cur_port=1;cur_port<=g_num_ports;cur_port++)
{
/* disable interrupt generation on UART */
outp(p_cur_port_info->base_address +
INTERRUPT_ENABLE_REGISTER,0x00);
/* Delay for port to catch up */
IO_DELAY(3);
/* Reset Line Control Register */
outp(p_cur_port_info->base_address +
LINE_CONTROL_REGISTER,0x00);
/* Delay for port to catch up */
IO_DELAY(3);
/* Reset Modem Control Register */
outp(p_cur_port_info->base_address +
MODEM_CONTROL_REGISTER,0x00);
/* Delay for port to catch up */
IO_DELAY(3);
/* Reset the FIFO */
outp(p_cur_port_info->base_address +
FIFO_CONTROL_REGISTER,FCR_RESET_FIFO);
/* Delay for port to catch up */
IO_DELAY(3);
/* Point at next port info struct in array */
p_cur_port_info++;
} /* End reset port loop */
/* Re-enable control-break by setting its interrupt
vector back to the original value */
_dos_setvect(CTRL_BRKVECT, gp_ctrlbrk_vector);
/* Re-enable interrupts */
_enable();
return;
}
/*****************************************************
rc = Send_char(unsigned int port_number,
unsigned char tx_char)
rc = 1 : Character accepted by circular buffer
rc = -1: Character not accepted for transmission
Attempts to queue tx_char for transmission on
port_number (1 to g_num_ports) by adding it to the
circular transmit buffer. If port is not currently
transmitting, interrupt driver will be 'woken up' by
bouncing the Transmit Holding Register Empty (THRE)
interrup.
*****************************************************/
int Send_char(unsigned int port_number,
unsigned char tx_char)
{
static char a_funcname[] = "Send_char:";
struct t_port_info *p_cur_port_info;
unsigned int next_tail,old_tail;
/* Validate port number */
if ( (port_number < 1) ||
(port_number > g_num_ports) )
{
fprintf(stderr,"%s bad port_number (%u)\n",
a_funcname,port_number);
return(-1);
}
/* Set pointer to port's info entry */
p_cur_port_info = &(gp_port_info[port_number -1]);
/* Calculate what the new transmit tail will be, and
see if loading this character would overflow the
circular transmit buffer */
old_tail = p_cur_port_info->tx_tail;
next_tail = old_tail + 1;
if (next_tail >= BUFFER_SIZE)
next_tail = 0;
if (next_tail == p_cur_port_info->tx_head)
{
fprintf(stderr,"%s transmit buffer overflow!\n",
a_funcname);
return(-1);
}
/* Load the character into the circular transmit
buffer */
p_cur_port_info->a_transmit_buffer[old_tail] =
tx_char;
/* Update tail pointer */
p_cur_port_info->tx_tail = next_tail;
/* If this is now the only character in the transmit
buffer, wake up the interrupt driver. This is
held off until this point since the interrupt
driver may have emptied the list even while the
earlier parts of this function were executing! */
if (old_tail == p_cur_port_info->tx_head)
BOUNCE_THRE;
return(1);
}
/*****************************************************
rc = Read_char(unsigned int port_number)
rc = -1: Error reading
rc = -2: No data available
otherwise, low byte of rc is data character,
high byte is bitmapped flag (see defines in
UART.H).
Attempts to read the next character from port_number
(1 to g_num_ports)'s circular receive buffer.
*****************************************************/
int Read_char(unsigned int port_number)
{
static char a_funcname[] = "Read_char:";
struct t_port_info *p_cur_port_info;
int new_head,return_char;
char *p_temp;
/* Validate port number */
if ( (port_number < 1) ||
(port_number > g_num_ports) )
{
fprintf(stderr,"%s bad port_number (%u)\n",
a_funcname,port_number);
return(-1);
}
/* Set pointer to port's info entry */
p_cur_port_info = &(gp_port_info[port_number -1]);
/* Check to see if there is any data */
if (p_cur_port_info->rx_tail ==
p_cur_port_info->rx_head)
return(-2);
/* Retrieve the next character from circular
recieve buffer, along with its status flags */
p_temp = p_cur_port_info->a_receive_buffer +
p_cur_port_info->rx_head;
return_char = *( (int *) p_temp);
/* Calculate next head position in receive buffer.
This is done in a temporary variable since
interrupt driver could interrupt in the middle
of the sequence. */
new_head = p_cur_port_info->rx_head += 2;
if (new_head >= (BUFFER_SIZE * 2) )
new_head = 0;
p_cur_port_info->rx_head = new_head;
return(return_char);
}