📄 pc8250.cpp
字号:
int PC8250::write_buffer( char *buffer, unsigned int count )
{
ByteCount = 0;
if ( error_status < 0 )
return error_status;
for ( ; ; ) {
if ( count == 0 )
break;
if ( !isr_data->TXQueue.Insert( *buffer ) )
break;
buffer++;
count--;
ByteCount++;
}
if ( !isr_data->tx_running && !isr_data->blocked )
jump_start( isr_data );
if ( count > 0 )
return RS232_TIMEOUT;
else
return RS232_SUCCESS;
}
// The Queue functions make it easy to flush the RX queue.
// After emptying it all, we need to be sure that handshaking
// gets managed.
int PC8250::FlushRXBuffer( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
_disable();
isr_data->RXQueue.Clear();
_enable();
check_rx_handshaking();
return RS232_SUCCESS;
}
// write_settings() is a protected routine called by the
// constructor and the public Set() function. It is long and
// stringy, mostly because setting up the UART is just a long
// case of setting or clearing bits in control registers. It
// might be possible to modularize this code, but it wouldn't be
// particularly useful.
RS232Error PC8250::write_settings( void )
{
int lcr;
int divisor_high;
int divisor_low;
RS232Error status = RS232_SUCCESS;
long result_baud;
if ( settings.BaudRate <= 0 || settings.BaudRate > 115200L ) {
settings.BaudRate = 9600;
status = RS232_ILLEGAL_BAUD_RATE;
}
divisor_low = (int) ( ( 115200L / settings.BaudRate ) & 0xff );
divisor_high = (int) ( ( 115200L / settings.BaudRate ) >> 8 );
result_baud = 115200L / ( 115200L / settings.BaudRate );
if ( result_baud != settings.BaudRate ) {
settings.BaudRate = result_baud;
status = RS232_ILLEGAL_BAUD_RATE;
}
lcr = inp( isr_data->uart + LINE_CONTROL_REGISTER );
lcr |= LCR_DLAB;
_disable();
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
outp( isr_data->uart + DIVISOR_LATCH_LOW, divisor_low );
outp( isr_data->uart + DIVISOR_LATCH_HIGH, divisor_high );
lcr &= ~LCR_DLAB;
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
_enable();
lcr &= ~LCR_PARITY_MASK;
switch ( toupper( settings.Parity ) ) {
case 'O' :
lcr |= LCR_PARITY_ENABLE;
break;
case 'E' :
lcr |= LCR_PARITY_ENABLE + LCR_EVEN_PARITY_SELECT;
break;
case 'M' :
lcr |= LCR_PARITY_ENABLE + LCR_STICK_PARITY;
break;
case 'S' :
lcr |= LCR_PARITY_ENABLE +
LCR_EVEN_PARITY_SELECT +
LCR_STICK_PARITY;
break;
default :
settings.Parity = 'N';
status = RS232_ILLEGAL_PARITY_SETTING;
case 'N' :
break;
}
lcr &= ~LCR_WORD_LENGTH_MASK;
switch ( settings.WordLength ) {
case 5 :
break;
case 6 :
lcr |= LCR_WORD_LENGTH_SELECT_0;
break;
case 7 :
lcr |= LCR_WORD_LENGTH_SELECT_1;
break;
default :
settings.WordLength = 8;
status = RS232_ILLEGAL_WORD_LENGTH;
case 8 :
lcr |= LCR_WORD_LENGTH_SELECT_0 +
LCR_WORD_LENGTH_SELECT_1;
break;
}
lcr &= ~LCR_STOP_BITS;
switch ( settings.StopBits ) {
default :
settings.StopBits = 1;
status = RS232_ILLEGAL_STOP_BITS;
case 1 :
break;
case 2 :
lcr |= LCR_STOP_BITS;
break;
}
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
return status;
}
// read_settings() is the protected inverse of
// write_settings(). This routine just reads in the state of the
// UART into a settings object. This is done when the routine
// starts up, so that the RS232 class will always have the saved
// settings available for restoration when the RS232 port is
// closed.
void PC8250::read_settings( void )
{
int lcr;
int mcr;
int divisor_low;
int divisor_high;
lcr = inp( isr_data->uart + LINE_CONTROL_REGISTER );
lcr |= LCR_DLAB;
_disable();
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
divisor_low = inp( isr_data->uart + DIVISOR_LATCH_LOW );
divisor_high = inp( isr_data->uart + DIVISOR_LATCH_HIGH );
lcr &= ~LCR_DLAB;
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
_enable();
if ( divisor_high | divisor_low )
settings.BaudRate =
115200L / ( ( divisor_high << 8 ) + divisor_low );
else
settings.BaudRate = -1;
switch ( lcr & LCR_PARITY_MASK ) {
case LCR_PARITY_ENABLE :
settings.Parity = 'O';
break;
case LCR_PARITY_ENABLE + LCR_EVEN_PARITY_SELECT :
settings.Parity = 'E';
break;
case LCR_PARITY_ENABLE + LCR_STICK_PARITY :
settings.Parity = 'M';
break;
case LCR_PARITY_ENABLE +
LCR_EVEN_PARITY_SELECT +
LCR_STICK_PARITY :
settings.Parity = 'S';
break;
default :
settings.Parity = 'N';
break;
}
switch ( lcr & LCR_WORD_LENGTH_MASK ) {
case 0 :
settings.WordLength = 5;
break;
case LCR_WORD_LENGTH_SELECT_0 :
settings.WordLength = 6;
break;
case LCR_WORD_LENGTH_SELECT_1 :
settings.WordLength = 7;
break;
case LCR_WORD_LENGTH_SELECT_0 + LCR_WORD_LENGTH_SELECT_1 :
settings.WordLength = 8;
break;
}
switch ( lcr & LCR_STOP_BITS ) {
case 0 :
settings.StopBits = 1;
break;
default :
settings.StopBits = 2;
break;
}
mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
settings.Dtr = ( mcr & MCR_DTR ) != 0;
settings.Rts = ( mcr & MCR_RTS ) != 0;
settings.XonXoff = -1;
settings.RtsCts = -1;
settings.DtrDsr = -1;
}
// Set() takes advantage of code used by the constructor to
// set up some of the UART parameters.
RS232Error PC8250::Set( long baud_rate,
int parity,
int word_length,
int stop_bits )
{
settings.Adjust( baud_rate,
parity,
word_length,
stop_bits,
UNCHANGED,
UNCHANGED,
UNCHANGED,
UNCHANGED,
UNCHANGED );
return write_settings();
}
// This virtual routine is easily handled by a Queue member
// function.
int PC8250::TXSpaceFree( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return isr_data->TXQueue.FreeCount();
}
// The same thing is true here.
int PC8250::RXSpaceUsed( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return isr_data->RXQueue.InUseCount();
}
// The 8250 UART doesn't have an intelligent BREAK function,
// so we have to just sit on the line while the BREAK goes out.
// Hopefully the IdleFunction() can do something useful while
// this takes place.
int PC8250::Break( long milliseconds )
{
int lcr;
long timer;
if ( error_status < RS232_SUCCESS )
return error_status;
timer = ReadTime() + milliseconds;
lcr = inp( isr_data->uart + LINE_CONTROL_REGISTER);
lcr |= LCR_SET_BREAK;
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
while ( ReadTime() < timer )
IdleFunction();
lcr &= ~LCR_SET_BREAK;
outp( isr_data->uart + LINE_CONTROL_REGISTER, lcr );
return RS232_SUCCESS;
}
// The four modem status functions just check the bits that
// were read in the last time a modem status interrupt took
// place, and return them to the calling routine.
int PC8250::Cd( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return ( isr_data->modem_status & MSR_CD ) ? 1 : 0;
}
int PC8250::Ri( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return ( isr_data->modem_status & MSR_RI ) ? 1 : 0;
}
int PC8250::Cts( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return ( isr_data->modem_status & MSR_CTS ) ? 1 : 0;
}
int PC8250::Dsr( void )
{
if ( error_status < RS232_SUCCESS )
return error_status;
return ( isr_data->modem_status & MSR_DSR ) ? 1 : 0;
}
// The four line status routines are similar to the modem
// status routines in that they just check a bit in a data
// member. However, they also have an optional parameter that
// can be used to clear the error flag. This is just a matter of
// clearing the same bit.
int PC8250::ParityError( int reset )
{
int return_value;
if ( error_status < RS232_SUCCESS )
return error_status;
return_value =
( isr_data->line_status & LSR_PARITY_ERROR ) ? 1 : 0;
if ( reset != UNCHANGED && reset != 0 ) {
_disable();
isr_data->line_status &= ~LSR_PARITY_ERROR;
_enable();
}
return return_value;
}
int PC8250::BreakDetect( int reset )
{
int return_value;
if ( error_status < RS232_SUCCESS )
return error_status;
return_value =
( isr_data->line_status & LSR_BREAK_DETECT ) ? 1 : 0;
if ( reset != UNCHANGED && reset != 0 ) {
_disable();
isr_data->line_status &= ~LSR_BREAK_DETECT;
_enable();
}
return return_value;
}
int PC8250::FramingError( int reset )
{
int return_value;
if ( error_status < RS232_SUCCESS )
return error_status;
return_value =
( isr_data->line_status & LSR_FRAMING_ERROR ) ? 1 : 0;
if ( reset != UNCHANGED && reset != 0 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -