// // ISR_8250.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. // // All of the code used in the 8250 interrupt service // routine is found in this file. The Queue class inline // functions are pulled in from QUEUE.H #include #include "pc8250.h" #include "_pc8250.h" #include "ascii.h" // Prototypes for the internal handlers called by the ISR. void handle_modem_status_interrupt( struct isr_data_block *data ); void handle_tx_interrupt( struct isr_data_block *data ); void handle_rx_interrupt( struct isr_data_block *data ); // This is the main body of the 8250 interrupt handler. It // sits in a loop, repeatedly reading the Interrupt ID // Register, and dispatching a handler based on the // interrupt type. The line status interrupt is so simple // that it doesn't merit its own handler. void isr_8250( struct isr_data_block * data ) { _enable(); for ( ; ; ) { switch( inp( data->uart + INTERRUPT_ID_REGISTER ) & 7 ) { case IIR_MODEM_STATUS_INTERRUPT : handle_modem_status_interrupt( data ); break; case IIR_TX_HOLDING_REGISTER_INTERRUPT : handle_tx_interrupt( data ); break; case IIR_RX_DATA_READY_INTERRUPT : handle_rx_interrupt( data ); break; case IIR_LINE_STATUS_INTERRUPT : data->ls_int_count++; data->line_status |= inp( data->uart + LINE_STATUS_REGISTER ); break; default : return; } } } // The modem status interrupt handler has to do three // things. It has to handle RTS/CTS handshaking. It has // to handle DTR/DSR handshaking, and it has to update the // modem_status member of the isr_data structure. void handle_modem_status_interrupt( struct isr_data_block *data ) { data->ms_int_count++; data->modem_status = (unsigned int) inp( data->uart + MODEM_STATUS_REGISTER ); if ( data->handshaking & rts_cts ) if ( data->modem_status & MSR_DELTA_CTS ) // Has CTS changed? if ( data->modem_status & MSR_CTS ) { if ( data->blocked & rts_cts ) { data->blocked &= ~rts_cts; jump_start( data ); } } else { if ( !( data->blocked & rts_cts ) ) data->blocked |= rts_cts; } if ( data->handshaking & dtr_dsr ) if ( data->modem_status & MSR_DELTA_DSR ) if ( data->modem_status & MSR_DSR ) { if ( data->blocked & dtr_dsr ) { data->blocked &= ~dtr_dsr; jump_start( data ); } } else { if ( !( data->blocked & dtr_dsr ) ) data->blocked |= dtr_dsr; } } // The TX interrupt is fairly simple. All it has to do is // transmit the next character, if one is available. Depending // on whether or not a character is available, it will set or // clear the tx_running member. Note that here and in // jump_start(), the handshake_char gets first shot at going // out. This is normally an XON or XOFF. void handle_tx_interrupt( struct isr_data_block *data ) { int c; data->tx_int_count++; if ( data->send_handshake_char >= 0 ) { outp( data->uart + TRANSMIT_HOLDING_REGISTER, data->send_handshake_char ); data->send_handshake_char = -1; } else if ( data->blocked ) { data->tx_running = 0; } else { c = data->TXQueue.Remove(); if ( c >= 0 ) outp( data->uart + TRANSMIT_HOLDING_REGISTER, c ); else data->tx_running = 0; } } // The RX interrupt handler is divided into two nearly // independent sections. The first section just reads in the // character that has just been received and stores it in a // buffer. If the UART type is a 16550, up to 16 characters // might be read in. The next section of code handles the // possibility that a handshaking trigger has just occurred, // modifies any control lines or sends an XOFF as needed. void handle_rx_interrupt( struct isr_data_block *data ) { int c; int mcr; int lsr; data->rx_int_count++; // The receive data section for ( ; ; ) { c = inp( data->uart + RECEIVE_BUFFER_REGISTER ); if ( data->handshaking & xon_xoff ) { if ( c == XON ) { data->blocked &= ~xon_xoff; jump_start( data ); return; } else if ( c == XOFF ) { data->blocked |= xon_xoff; return; } } if ( !data->RXQueue.Insert( (char) c ) ) data->overflow = 1; if ( data->uart_type == UART_8250 ) break; lsr = inp( data->uart + LINE_STATUS_REGISTER ); data->line_status |= lsr; if ( ( lsr & LSR_DATA_READY ) == 0 ) break; } // The handshaking section if ( data->handshaking ) { if ( data->RXQueue.InUseCount() > HighWaterMark ) { if ( ( data->handshaking & rts_cts ) && !( data->blocking & rts_cts ) ) { mcr = inp( data->uart + MODEM_CONTROL_REGISTER ); mcr &= ~MCR_RTS; outp( data->uart + MODEM_CONTROL_REGISTER, mcr ); data->blocking |= rts_cts; } if ( ( data->handshaking & dtr_dsr ) && !( data->blocking & dtr_dsr ) ) { mcr = inp( data->uart + MODEM_CONTROL_REGISTER ); mcr &= ~MCR_DTR; outp( data->uart + MODEM_CONTROL_REGISTER, mcr ); data->blocking |= dtr_dsr; } if ( ( data->handshaking & xon_xoff ) && !( data->blocking & xon_xoff ) ) { data->blocking |= xon_xoff; if ( data->send_handshake_char == XON ) { data->send_handshake_char = -1; } else { data->send_handshake_char = XOFF; jump_start( data ); } } } } } // Any time transmit interrupts need to be restarted, this // routine is called to do the job. It gets the interrupts // running again by sending a single character out the TX // register manually. When that character is done // transmitting, the next TX interrupt will start. The // tx_running member of the class keeps track of when we can // expect another TX interrupt and when we can't. void jump_start( struct isr_data_block *data ) { int c; // Both tx_running and blocked can change behind my back in the // ISR, so I have to disable interrupts if I want to be able to // count on them. _disable(); if ( !data->tx_running ) { if ( ( c = data->send_handshake_char ) != -1 ) data->send_handshake_char = -1; else if ( !data->blocked ) c = data->TXQueue.Remove(); if ( c >= 0 ) { outp( data->uart, c ); data->tx_running = 1; } } _enable(); } // *********************** END OF ISR_8250.CPP ***********************