Listing 3 (uartlow. c) Low-Level Device Driver Routines for UART

#define UARTMAIN 1

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <dos.h>

#include "uart.h"
#include "uartmacs.c"

/*
 *  Compile Options  :  /AL /Ox /Zp /c
 *           (Large library, optimization
 *            for maxium speed, pack structures).
 *           Note: /Ox does the following optimizations
 *              Relaxed alias checking
 *              Loop optimization
 *              Intrinsic substitution
 *              Optimization for speed
 *              Removing stack probing
*/
#pragma check_stack(off)

void interrupt far Asynch_driver(void)
{
   register int value_from_port;
   register int cur_char;
   static unsigned char near cur_port;
   static struct t_port_info *p_cur_port_info;
   static int near interrupts_processed;
   static int near i;
   static char *p_temp;
   static char tx_char;

   /* Note - interrupts are automatically disabled by
            CPU before entering interrupt routine. */

   do
   {   /* Loop until we make one complete pass
         without processing any interrupts */

      /* Set flag indicating that we haven't
         processed any interrupts on this pass
         through the loop yet. */
      interrupts_processed = 0;

      /* Address the port entry of the first port in
         the system. */
      p_cur_port_info = gp_port_info;
      for (cur_port=1;cur_port<=g_num_ports;
          cur_port++)
      {   /* Loop through all the ports */

         /* read port's interrupt identification
register */
         value_from_port = inp(p_cur_port_info->
            base_address+INTERRUPT_IDENT_REGISTER);

         if ( value_from_port & 0x01 )
         {   /* No interrupt pending */

            /* If less than MAX_RCVFIFO characters
               have been read without receiving a
               receive data interrupt, check line
               status register to see if there is
               still data in the FIFO. This code
               will function correctly even if there
               isn't a FIFO. */
            if (p_cur_port_info->non_int_chars <
                MAX_RCVFIFO)
            {
                /* read port's line status register */
                value_from_port =
                  inp(p_cur_port_info->
                     base_address +
                     LINE_STATUS_REGISTER);

                /* Delay for port to catch up */
                I0_DELAY(1);

                if ( value_from_port & 0x01 )
                {   /* Data is ready */

                  /* Increment number of chars
                     processed from fifo without
                     interrupt */
                  p_cur_port_info->
                     non_int_chars++;

                  /* We have found at least one
                     interrupt pending so we will
                     have to make at least one
                     more pass. */
                  interrupts_processed = 1;

                  /* Skip normal interrupt
                     identification logic, and go
                     right to reading the data. */
                  goto force read data;
               }  /* End data is ready */

            }  /* End p_cur_port_info->
                 non_int_chars < MAX_RCVFIFO) */
         }  /* end no interrupt pending */
         else
         {  /* There is an interrupt pending
               from this port */
            /* We have found at least one interrupt
               pending so we will have to make at
               least one more pass. */
            interrupts_processed = 1;

            /* Mask out unwanted bits from interrupt
               identification. */
            value_from_port &= IIR_MASK;

            switch (value_from_port)
            {   /* Conditional execution depending
                  on type of interrupt */

                /* Interrupt for Receiver Line
                  Status */
                case 0x06 :

                  /* Clear receive register */
                  cur_char =
                     inp(p_cur_port_info->
                        base address +
                        RECEIVE_REGISTER);

                  /* Clear high word of character
                     before breaking down error */
                  cur_char &= 0x00ff;

                  /* read port's line status
                     register */
                  value_from_port =
                     inp(p_cur_port_info->
                        base_address +
                        LINE_STATUS_REGISTER);

                  if (value_from_port & 0x10)
                  {   /* Received 'break' */
                     cur_char |= BREAK_SIGNAL;
                  }

                  if (value_from_port & 0x08)
                  {   /* framing error */
                     cur_char |= FRAMING_ERROR;
                  }

                  if (value_from_port & 0x04)
                  {   /* parity error */
                     cur_char |= PARITY_ERROR;
                  }

                  if (value_from_port & 0x02)
                  {   /* overrun error */
                     cur_char |= OVERRUN_ERROR;
                  }

                  goto add_char_to_rx_buf;

              /* Interrupt for data received  */
              case 0x04 :

                  /* Check line status register to
                     see if data is ready */
                  value_from_port =
                     inp(p_cur_port_info->
                        base_address +
                        LINE_STATUS_REGISTER);
                  if ( !(value_from_port & Ox01) )
                     break;
                  /* Set number of characters
                     received without data receive
                     interrupt to 0. */
                  p_cur_port_info->
                     non_int_chars = 0;

                  force_read_data:

                  /* read received data
                     register */
                  cur_char =
                     inp(p_cur_port_info->
                        base_address+
                        RECEIVE_REGISTER);

                  /* Clear high word of character
                     before breaking down error  */
                  cur_char &= 0x00ff;

                  add_char_to_rx_buf:

                  /* Calculate pointer to next
                     position in circular
                     receive buffer */
                  p_temp = p_cur_port_info->
                      a_receive_buffer +
                      p_cur_port_info->rx_tail;

                  /* Calculate next tail position
                     in receive buffer */
                  p_cur_port_info->rx_tail += 2;
                  if (p_cur_port_info->rx_tail >=
                       (BUFFER_SIZE * 2) )
                     p_cur_port_info->rx_tail = 0;

                  /* Check for receive buffer
                     overflow */
                  if (p_cur_port_info->rx_tail ==
                     p_cur_port_info->rx_head)
                     {   /* Receive buffer has
                           overflowed. Update
                           flag to reflect this,
                           and advance head
                           (losing one char).  */
                        cur_char |=
                           BUFFER_OVERRUN;

                        p_cur_port_info->rx_head
                           += 2;
                        if (p_cur_port_info->
                           rx_head >=
                           (BUFFER_SIZE * 2) )
                           p_cur_port_info->
                              rx_head = 0;
                     }

                  /* Add character with coded
                    error condition flag to
                    circular buffer  */
                  *( (int *) p_temp) = cur_char;

                  break;

              /* Interrupt for Transmit Holding
                 Register Emtpy */
              case 0x02 :
                  if (p_cur_port_info->tx_head !=
                     p_cur_port_info->tx_tail )
                  {  /* Have data to transmit  */

                     if (p_cur_port_info->
                        obey_rts_cts &&
                       !p_cur_port_info->
                        cts_state)
                     {   /* Require Clear To
                           Send before
                           transmitting  */

                        /* Raise the Request To
                           Send signal  */
                        RAISE_RTS;

                        break;
                     }

                     /* read port's line status
                        register */
                     value_from_port =
                        inp(p_cur_port_info->
                           base_address +
                           LINE_STATUS_REGISTER);

                     /* Delay for port to catch
                        up  */
                     I0_DELAY(2);

                     /* Make sure transmit
                        holding register is
                        really empty.  */
                     if ( !(value_from_port
                           & 0x20) )
                        break;
                     /* Transmit as many chars as
                        port can handle (depends
                        on presence/absence of
                        fifo), until transmit
                        circular buffer is
                        empty */
                     for (i=0;
                         (i <p_cur_port_info->
                            max_tx_chars) &&
                         (p_cur_port_info->
                          tx_head !=
                          p_cur_port_info->
                          tx_tail ) ;
                         i++)
                     {
                        /* Get next character
                           to transmit and
                           increment transmit
                           data pointer. */
                        tx_char =
                           p_cur_port_info->
                           a_transmit_buffer[
                             p_cur_port_info->
                               tx_head++];
                        if (p_cur_port_info->
                           tx_head >=
                           BUFFER_SIZE)
                           p_cur_port_info->
                           tx_head = 0;

                        /* Transmit the
                           character */
                        outp(p_cur_port_info->
                            base_address +
                            TRANSMIT_REGISTER,
                            tx_char);
                     }  /* End for TX loop */

                   }  /* end if data to
                        transmit */
                  else
                  {   /* No data to send */

                     if (p_cur_port_info->
                        obey_rts_cts)
                     {   /* No data to send, port
                           is using flow
                           control */

                        /* Turn off the Request
                           To Send signal */
                        DROP_RTS;
                     }

                  }  /* End no data to send */

                  break;

               /* Interrupt for modem status
                  change */
               case 0x00 :
                   /* read port's modem status
                      register */
                   value_from_port =
                      inp(p_cur_port_info->
                         base_address +
                         MODEM_STATUS_REGISTER);

                   if (value_from_port & 0x02)
                   {   /* Change in Data Set Ready */

                      if (IS_DSR (value_from_port))
                         /* DSR came on */
                         p_cur_port_info->
                             dsr_state = 1;
                      else
                         /* DSR went off */
                         p_cur_port_info->
                            dsr_state = 0;

                   }  /* End if delta data set ready */

                   if (value_from_port & 0x08)
                   {      /* Change In Data
                            Carrier Detect */

                      if (IS_DCD(value_from_port))
                         /* DCD came on */
                         p_cur_port_info->
                            dcd_state = 1;
                      else
                         /* DCD went off */
                         p_cur_port_info->
                            dcd_state = 0;

                   } /* End if delta carrier detect */

                   if (value_from_port & 0x01)
                   {       /* Change in Clear To Send */

                      if (IS_CTS(value_from_port))
                         {
                            /* CTS came on */
                            p_cur_port_info->
                               cts_state = 1;

                            /* Generate a
                               Transmit Holding
                               Register Empty
                               Interrupt to allow
                               data to be sent */
                            BOUNCE_THRE;
                         }
                      else
                         /* CTS went off */
                         p_cur_port_info->
                            cts_state = 0;

                   } /* End if delta clear to send */

                   if (value_from_port & 0x04)
                   {       /* Change in Ring Indicator state */

                    if (IS_RING(value_from_port))
                    {    /* Ringing in */

                        /* Put ring indication
                           into circular buffer
                           */
                        cur_char =
                           RING_INDICATION;
                        goto add_char_to_rx_buf;
                    }
                  } /* End if delta ring indicator */
                  break;

                /* Unkown interrupt */
                default : break;

            }   /* End switch on type of
                  interrupt */

         }   /* end interrupt pending */

         p_cur_port_info++;      /* Point at next port */

      }   /* End loop through all ports */

   } while (interrupts_processed == 0);

   /* Output non-specific End Of Interrupt to 8259
      interrupt controller for IRQs 0 - 7 */
   outp(R8259A_CONTROL,0x20);

   /* Note - Interrupts are not re-enabled to ensure
            that the IRET will be executed before the
            next interrupt */

   return;
}
/* End of File */