📄 pc8250.cpp
字号:
//
// PC8250.CPP
//
// Source code from:
//
// Serial Communications: A C++ Developer's Guide, 2nd Edition
// by Mark Nelson, IDG Books, 1999
//
// Please see the book for information on usage.
//
// This file contains most of the code used in the PC8250
// class. The remainder of the code can be found in
// ISR_8250.CPP, which has the ISR and its support code.
//
#include <stdio.h>
#include <dos.h>
#include <ctype.h>
#include <conio.h>
#include "rs232.h"
#include "pc8250.h"
#include "_pc8250.h"
#include "ascii.h"
// Data used to initialize UART addresses and IRQ lines.
static int Uarts[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
static enum irq_name IRQs[] = { IRQ4, IRQ3, IRQ10, IRQ11 };//IRQ4, IRQ3
// This is the one and only constructor for an object of class
// PC8250. A quick look at PC8250.H will show you that all of
// the parameters in the list except the port have default
// values, so the list isn't as overwhelming as it might look.
PC8250::PC8250( RS232PortName port,
long baud_rate,
char parity,
int word_length,
int stop_bits,
int dtr,
int rts,
int xon_xoff,
int rts_cts,
int dtr_dsr,
Handler *handler,
int uart_address,
irq_name irq_line )
{
int mcr;
interrupt_handler = handler;
port_name = port;
error_status = RS232_SUCCESS;
// This section of code initializes most of the items in the
// isr_data structure, which contains all of the items used in
// the ISR.
isr_data = new isr_data_block;
if ( isr_data == 0 ) {
error_status = RS232_MEMORY_ALLOCATION_ERROR;
return;
}
set_uart_address_and_irq( handler, uart_address, irq_line );
if ( error_status < RS232_SUCCESS )
return;
isr_data->overflow = 0;
isr_data->tx_running = 0;
isr_data->tx_int_count = 0;
isr_data->rx_int_count = 0;
isr_data->ls_int_count = 0;
isr_data->ms_int_count = 0;
isr_data->line_status = 0;
isr_data->handshaking = 0;
isr_data->blocking = 0;
isr_data->blocked = 0;
isr_data->send_handshake_char = -1;
// PC8250 has to share the debug output with the parent
// class. To determine where our first line starts, we call the
// FormatDebugOutput() function from our parent class.
first_debug_output_line = RS232::FormatDebugOutput();
debug_line_count = FormatDebugOutput();
// Determine whether the UART is there and what type it is.
check_uart();
if ( error_status < RS232_SUCCESS )
return;
// Save all of the old UART settings, and then set it to the
// new ones passed to the constructor.
read_settings();
saved_settings = settings;
settings.Adjust( baud_rate,
parity,
word_length,
stop_bits,
dtr,
rts,
xon_xoff,
rts_cts,
dtr_dsr );
write_settings();
// Here we set up the interrupt handler, then turn on
// interrupts. After this code is done the UART will be
// running.
outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
mcr |= MCR_OUT2;
mcr &= ~MCR_LOOPBACK;
outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
if ( interrupt_handler == 0 ) {
error_status = ConnectToIrq( irq,
isr_data, (void (*)(void *))
isr_8250 );
if ( error_status < RS232_SUCCESS ) {
outp( isr_data->uart + MODEM_CONTROL_REGISTER, 0 );
outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
return;
}
} else {
error_status = interrupt_handler->AddPort( port_name,
isr_data );
if ( error_status < RS232_SUCCESS )
return;
}
inp( isr_data->uart ); // Clear any pending interrupts
inp( isr_data->uart + INTERRUPT_ID_REGISTER );
_disable();
isr_data->modem_status =
(unsigned int)
inp( isr_data->uart + MODEM_STATUS_REGISTER );
outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER,
IER_RX_DATA_READY + IER_TX_HOLDING_REGISTER_EMPTY +
IER_MODEM_STATUS + IER_LINE_STATUS );
outp( 0x20, 0xc0 + IRQ3 - 1 );
_enable();
// Finally, set up the last few parameters and exit.
Dtr( settings.Dtr );
Rts( settings.Rts );
XonXoffHandshaking( settings.XonXoff );
RtsCtsHandshaking( settings.RtsCts );
DtrDsrHandshaking( settings.DtrDsr );
}
void PC8250::set_uart_address_and_irq( Handler *handler,
int uart_address,
irq_name irq_line )
{
// If I have a handler or have a defined irq_line, I won't
// use the default IRQs. If I have a uart_address, I won't
// use the default UART address.
if ( handler == 0 && irq_line == ILLEGAL_IRQ ) {
if ( port_name > COM4 )
error_status = RS232_PORT_NOT_FOUND;
else
irq = IRQs[ port_name ];
} else
irq = irq_line;
if ( uart_address == 0 ) {
if ( port_name > COM4 )
error_status = RS232_PORT_NOT_FOUND;
else
isr_data->uart = Uarts[ port_name ];
} else
isr_data->uart = uart_address;
}
// The destructor has a much easier time of it than the
// constructor. It disables interrupts, then restores the line
// settings of the UART.
PC8250::~PC8250( void )
{
if ( error_status == RS232_SUCCESS ) {
outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
outp( isr_data->uart + MODEM_CONTROL_REGISTER, 0 );
if ( interrupt_handler == 0 )
DisconnectFromIRQ( irq );
else
interrupt_handler->DeletePort( port_name );
settings = saved_settings;
write_settings();
Dtr( settings.Dtr );
Rts( settings.Rts );
}
if ( isr_data != 0 )
delete isr_data;
}
// This routine determines if a UART is present, and if so,
// whether or not it is a 16550. If it is a 16550, the FIFO is
// enabled with a trigger at 14 bytes.
void PC8250::check_uart( void )
{
int temp;
outp( isr_data->uart + FIFO_CONTROL_REGISTER, 0 );
temp = inp( isr_data->uart + INTERRUPT_ID_REGISTER );
if ( ( temp & 0xf8 ) != 0 ) {
isr_data->uart_type = UART_UNKNOWN;
error_status = RS232_PORT_NOT_FOUND;
return;
}
outp( isr_data->uart + FIFO_CONTROL_REGISTER,
FCR_FIFO_ENABLE + FCR_TRIGGER_14 );
temp = inp( isr_data->uart + INTERRUPT_ID_REGISTER );
if ( ( temp & 0xf8 ) == 0xc0 ) {
isr_data->uart_type = UART_16550;
fifo_setting = 14;
} else {
isr_data->uart_type = UART_8250;
fifo_setting = 0;
outp( isr_data->uart + FIFO_CONTROL_REGISTER, 0 );
}
}
// After any function that reads data from the ISR buffers,
// this routine is called. If the read operation dropped us
// below a handshaking trigger point, this routine will figure
// out what action to take.
void PC8250::check_rx_handshaking()
{
int mcr;
// Take a quick exit if we aren't handshaking, blocking, or if
// the RX Queue is not below the low-water mark.
if ( !isr_data->handshaking || !isr_data->blocking )
return;
if ( isr_data->RXQueue.InUseCount() > LowWaterMark )
return;
// If RTS/CTS handshaking is in effect, I raise RTS.
if ( ( isr_data->handshaking & rts_cts ) &&
( isr_data->blocking & rts_cts ) ) {
_disable();
mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
mcr |= MCR_RTS;
outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
isr_data->blocking &= ~rts_cts;
_enable();
}
// If DTR/DSR handshaking is in effect, I raise DTR.
if ( ( isr_data->handshaking & dtr_dsr ) &&
( isr_data->blocking & dtr_dsr ) ) {
_disable();
mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
mcr |= MCR_DTR;
outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
isr_data->blocking &= ~dtr_dsr;
_enable();
}
// If XON/XOFF is in effect, I send an XON. Note that if
// there is a pending XOFF that never made it out, I cancel it
// and don't send anything else.
if ( ( isr_data->handshaking & xon_xoff ) &&
( isr_data->blocking & xon_xoff ) ) {
_disable();
isr_data->blocking &= ~xon_xoff;
if ( isr_data->send_handshake_char == XOFF )
isr_data->send_handshake_char = -1;
else {
isr_data->send_handshake_char = XON;
jump_start( isr_data );
}
_enable();
}
}
// This routine just pulls out a byte and checks for
// handshaking activity.
int PC8250::read_byte( void )
{
int c;
if ( error_status < 0 )
return error_status;
c = isr_data->RXQueue.Remove();
if ( c < 0 )
return RS232_TIMEOUT;
check_rx_handshaking();
return c;
}
// When sending a byte to the output buffer, I have to check
// to see if the TX interrupt system needs to be restarted.
int PC8250::write_byte( int c )
{
if ( error_status < 0 )
return error_status;
if ( !isr_data->TXQueue.Insert( (unsigned char) c ) )
return RS232_TIMEOUT;
if ( !isr_data->tx_running && !isr_data->blocked )
jump_start( isr_data );
return RS232_SUCCESS;
}
// read_buffer() pulls in only as many bytes as are
// immediately available. Any high-level functions such as
// timing out or looking for a terminator are handled by one of
// the higher level Read() routines from class RS232.
int PC8250::read_buffer( char *buffer, unsigned int count )
{
ByteCount = 0;
if ( error_status < 0 )
return error_status;
while ( isr_data->RXQueue.InUseCount() ) {
if ( count <= 0 )
break;
*buffer++ = (char) isr_data->RXQueue.Remove();
count--;
ByteCount++;
}
*buffer = '\0';
if ( ByteCount > 0 )
check_rx_handshaking();
if ( count > 0 )
return RS232_TIMEOUT;
else
return RS232_SUCCESS;
}
// write_buffer() sends as many characters as the buffer can
// immediately manage. Like read_buffer(), it relies on higher
// level routines from class RS232 to perform the nicer functions
// such as adding termination, timing, etc.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -