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 >
Text File  |  1991-07-27  |  19KB  |  615 lines

  1. /*** LISTING 1 ** UARTAPI.C **************************
  2.    Application Programming Interface for
  3.    UART Asynch Driver.
  4. *****************************************************/
  5.  
  6. #include <conio.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <stddef.h>
  10. #include <dos.h>
  11.  
  12. #include "uart.h"
  13. #include "uartmacs.c"
  14.  
  15. /* unused interrupt vector to redirect Ctrl-Break to */
  16. #define UNUSEDVECT   0x48    
  17.  
  18. /* Ctrl-Break vector */
  19. #define CTRL_BRKVECT 0x23    
  20.  
  21. /* Table for converting baud rate to divisor 
  22.    settings */
  23. static struct 
  24. {
  25.    unsigned long baud_rate;
  26.    unsigned char divisor_high_byte;
  27.    unsigned char divisor_low_byte;
  28. } gas_baud_to_divisor[] = 
  29.    {
  30.        {   50L,        0x09,   0x00 },
  31.        {   75L,        0x06,   0x00 },
  32.        {   110L,       0x04,   0x17 },
  33.        {   134L,       0x03,   0x59 },
  34.        {   150L,       0x03,   0x00 },
  35.        {   300L,       0x01,   0x80 },
  36.        {   600L,       0x00,   0xC0 },
  37.        {   1200L,      0x00,   0x60 },
  38.        {   1800L,      0x00,   0x40 },
  39.        {   2000L,      0x00,   0x3A },
  40.        {   2400L,      0x00,   0x30 },
  41.        {   3600L,      0x00,   0x20 },
  42.        {   4800L,      0x00,   0x18 },
  43.        {   7200L,      0x00,   0x10 },
  44.        {   9600L,      0x00,   0x0C },
  45.        {   19200L,     0x00,   0x06 },
  46.        {   38400L,     0x00,   0x03 },
  47.        {   57600L,     0x00,   0x02 },
  48.        {   115200L,    0x00,   0x01 },
  49.        {   0L,         0x00,   0x00 },
  50.    } ;
  51.  
  52. /* Space to save original asynch interrupt vector */
  53. static void (interrupt far * gp_orig_asynch_vector)();       
  54.  
  55. /* to save original Ctrl-Break vector */
  56. static void (interrupt far *gp_ctrlbrk_vector)();    
  57.  
  58. /*****************************************************
  59. rc = Init_UART()
  60. rc = 1 : UART(s) intialized OK
  61. rc = -1: Failure in initialization.
  62.  
  63. Before calling this function, you must allocate and
  64. initialize the gp_port_info array to describe
  65. the UART(s) you want to drive, and you must set
  66. g_uart_irq to the IRQ level used (all ports must
  67. use the same IRQ level).
  68.  
  69. This function latches the UART's interrupt driver
  70. into the system interrupt table and initializes the
  71. UART(s).   If successful, it registers the
  72. Exit_UART function to be called during system
  73. shutdown.
  74. *****************************************************/
  75. int Init_UART(void)
  76. {
  77.    static char a_funcname[] = "Init_UART:";
  78.    register int value_from_port;   
  79.    unsigned int i;         
  80.    int retcode;
  81.    unsigned char cur_port; 
  82.    struct t_port_info *p_cur_port_info; 
  83.    void (interrupt far * p_unusedvect)();       
  84.  
  85.    /* Verify that calling program has initialized
  86.       info structures and variables */
  87.    if (g_num_ports < 1)
  88.    {
  89.        fprintf(stderr,"%s g_num_ports (%u) < 1\n",
  90.                a_funcname,g_num_ports);
  91.        return(-1);
  92.    }
  93.    if (gp_port_info == NULL)
  94.    {
  95.        fprintf(stderr,"%s gp_port_info is NULL\n",
  96.                a_funcname);
  97.        return(-1);
  98.    }
  99.    if ( (g_uart_irq < 3) || (g_uart_irq > 7) )
  100.    {
  101.        fprintf(stderr,"%s g_uart_irq (%u) not 3-7\n",
  102.                a_funcname,g_uart_irq);
  103.        return(-1);
  104.    }
  105.  
  106.  
  107.    /* Point at entry for first port */
  108.    p_cur_port_info = gp_port_info;  
  109.  
  110.  
  111.    /* Loop through array of port info entries and
  112.       initialize each port */
  113.    for (cur_port=1;cur_port<=g_num_ports;cur_port++)
  114.    {
  115.        /* Initialize internal variables in port
  116.           info entry */
  117.        p_cur_port_info->tx_head = 0;
  118.        p_cur_port_info->tx_tail = 0;
  119.        p_cur_port_info->rx_head = 0;
  120.        p_cur_port_info->rx_tail = 0;
  121.        p_cur_port_info->non_int_chars = 0;
  122.  
  123.        /* Check for valid port on this IRQ and at 
  124.           this base address */
  125.        retcode = Test_for_asynch_port(
  126.            p_cur_port_info->base_address,g_uart_irq);
  127.        if (retcode == -1)
  128.        {
  129.            fprintf(stderr,
  130.              "%s port %u-no UART at %.4Xh on IRQ %u\n",
  131.              a_funcname,cur_port,
  132.              p_cur_port_info->base_address,
  133.              g_uart_irq);
  134.            return(-1);
  135.        }
  136.  
  137.        /* Validate modem parameters */
  138.        if ( (p_cur_port_info->stop_bits < 1) ||
  139.             (p_cur_port_info->stop_bits > 2) )
  140.        {
  141.            fprintf(stderr,
  142.               "%s port %u-stop bits (%u) not 1 or 2\n",
  143.               a_funcname,cur_port,
  144.               p_cur_port_info->stop_bits);
  145.               return(-1);
  146.        }
  147.  
  148.        switch (p_cur_port_info->parity)
  149.            {
  150.                case 0 :
  151.                case 1 :
  152.                case 3 :
  153.                    break;
  154.  
  155.                default :
  156.                    fprintf(stderr,
  157.               "%s port %u-parity (%u) not 0,1,or 3\n",
  158.                        a_funcname,cur_port,
  159.                        p_cur_port_info->parity);
  160.                    return(-1);
  161.            }
  162.  
  163.        if ( (p_cur_port_info->data_bits < 5) || 
  164.             (p_cur_port_info->data_bits > 8) )
  165.        {
  166.            fprintf(stderr,
  167.                "%s port %u-data bits (%u) not 5-8\n",
  168.                a_funcname,cur_port,
  169.                p_cur_port_info->data_bits);
  170.            return(-1);
  171.        }
  172.  
  173.        /* Translate specified baud rate into 
  174.           divisor settings */
  175.        for (i=0;
  176.             gas_baud_to_divisor[i].baud_rate != 0L;
  177.             i++)
  178.        {
  179.            if (p_cur_port_info->baud_rate == 
  180.                gas_baud_to_divisor[i].baud_rate)
  181.                break;
  182.        }
  183.        if (gas_baud_to_divisor[i].baud_rate == 0L)
  184.        {
  185.            fprintf(stderr,
  186.                "%s port %u-invalid baud rate %lu\n",
  187.                a_funcname,cur_port,
  188.                p_cur_port_info->baud_rate);
  189.            return(-1);
  190.        }
  191.  
  192.        /* Make sure interrupt generation is disabled */
  193.        outp(p_cur_port_info->base_address+
  194.             INTERRUPT_ENABLE_REGISTER,0x00);
  195.  
  196.        /* Delay for port to catch up */
  197.        IO_DELAY(3);        
  198.  
  199.        /* Enable writing to divisor latch */
  200.        outp(p_cur_port_info->base_address+
  201.             LINE_CONTROL_REGISTER,0x80);    
  202.  
  203.        /* Delay for port to catch up */        
  204.        IO_DELAY(3);         
  205.  
  206.        /* Set the low byte of the divisor latch */
  207.        outp(p_cur_port_info->base_address+
  208.             DIV_LATCH_LOW_BYTE,
  209.             gas_baud_to_divisor[i].divisor_low_byte);
  210.  
  211.        /* Delay for port to catch up */        
  212.        IO_DELAY(3);         
  213.  
  214.        /* Set the low byte of the divisor latch */
  215.        outp(p_cur_port_info->base_address+
  216.             DIV_LATCH_HIGH_BYTE,
  217.             gas_baud_to_divisor[i].divisor_high_byte);
  218.  
  219.        /* Delay for port to catch up */        
  220.        IO_DELAY(3);         
  221.  
  222.        /* Set parity, data bits, stop bits, and 
  223.           disable writing to divisor latch */
  224.        value_from_port = (int) 
  225.           ( ((p_cur_port_info->stop_bits - 1) << 2 ) |
  226.             ( p_cur_port_info->parity << 3 ) |
  227.             ( p_cur_port_info->data_bits - 5 ) ) ;
  228.        outp(p_cur_port_info->base_address + 
  229.            LINE_CONTROL_REGISTER, value_from_port);
  230.  
  231.        /* Delay for port to catch up */        
  232.        IO_DELAY(3);         
  233.  
  234.        /* Initialize Modem Control Register - raise the
  235.           OUT2 signal */
  236.        outp(p_cur_port_info->base_address +
  237.             MODEM_CONTROL_REGISTER,0x08);
  238.  
  239.        /* Delay for port to catch up */        
  240.        IO_DELAY(3);         
  241.  
  242.        /* Enable the FIFO and set it to generate rcv
  243.           interrupts only when fifo has more than 8 
  244.           characters (if UART doesn't have a fifo - 
  245.           it is an older UART - this instruction will 
  246.           do nothing). */
  247.        outp(p_cur_port_info->base_address +
  248.             FIFO_CONTROL_REGISTER,FIFO_ENABLE);
  249.  
  250.        /* Delay for port to catch up */        
  251.        IO_DELAY(3);         
  252.  
  253.        /* Read the interrupt identification register 
  254.           to determine if the FIFO is enabled.  If the 
  255.           fifo is enabled, use it to transmit multiple 
  256.           characters. */
  257.        value_from_port = 
  258.            inp(p_cur_port_info->base_address + 
  259.                INTERRUPT_IDENT_REGISTER);
  260.  
  261.        if ( (value_from_port & IIR_FIFO_ENABLED) == 
  262.              IIR_FIFO_ENABLED)
  263.           /* Port has a fifo so it can transmit 16
  264.              characters at once. */
  265.           p_cur_port_info->max_tx_chars = 16;
  266.        else
  267.           p_cur_port_info->max_tx_chars = 1;
  268.  
  269.        /* If port is not going to use hardware flow
  270.           control, raise the Request To Send signal 
  271.           and keep it on since some modems will not
  272.           transmit unless this signal is raised. */
  273.        if ( !p_cur_port_info->obey_rts_cts)
  274.        {
  275.            RAISE_RTS;
  276.        }
  277.            
  278.        /* Point at next port info struct in array */
  279.        p_cur_port_info++;  
  280.    }       /* End Initialize UART loop */
  281.  
  282.    /* Save current interrupt vector */
  283.    gp_orig_asynch_vector = 
  284.        _dos_getvect( (0x08 + g_uart_irq) );
  285.  
  286.    /* get original Ctrl-Break vector */
  287.    gp_ctrlbrk_vector = _dos_getvect(CTRL_BRKVECT);    
  288.  
  289.    /* get an unused vector to redirect Ctrl-Break to */
  290.    p_unusedvect = _dos_getvect(UNUSEDVECT);    
  291.  
  292.    /* Register UART Shutdown for execution on 
  293.       program exit */
  294.    atexit(Exit_UART);
  295.  
  296.    /* disable interrupts */
  297.    _disable();         
  298.  
  299.    /* set Ctrl_brk to unused vector */
  300.    _dos_setvect(CTRL_BRKVECT, p_unusedvect);   
  301.  
  302.    /* Latch new UART interrupt routine into vector
  303.       table */
  304.    _dos_setvect( (0x08 + g_uart_irq), Asynch_driver ); 
  305.  
  306.    /* Get current 8259 interrupt controller mask */
  307.    value_from_port = inp(R8259A_MASK);     
  308.  
  309.    /* Delay for port to catch up */
  310.    IO_DELAY(3);        
  311.  
  312.    /* Turn off bit for asynch interrupt so that 
  313.       it will be enabled.  Note: 0x01 << g_uart_irq
  314.       is the equivalent of 2 raised to the 
  315.       g_uart_irq-th power */
  316.    value_from_port &= (0xff ^ ( 0x01 << g_uart_irq) );
  317.  
  318.    /* Output new mask to 8259 interrupt controller */
  319.    outp(R8259A_MASK,value_from_port);      
  320.  
  321.    /* Point at entry for first port */
  322.    p_cur_port_info = gp_port_info;  
  323.  
  324.    /* Loop through array of port info entries and
  325.       enable interrupts for each port */
  326.    for (cur_port=1;cur_port<=g_num_ports;cur_port++)
  327.    {
  328.        /* set interrupt enable registers to generate 
  329.           interrupts for everything (i.e., Transmit 
  330.           Holding Register Empty, Character Received, 
  331.           Receive Error, and Modem Status Change). */
  332.        outp(p_cur_port_info->base_address +
  333.             INTERRUPT_ENABLE_REGISTER,0x0F);
  334.  
  335.        /* Delay for port to catch up */
  336.        IO_DELAY(3);        
  337.  
  338.        /* Clear interrupt identification register */
  339.        value_from_port = 
  340.            inp(p_cur_port_info->base_address +
  341.                INTERRUPT_IDENT_REGISTER); 
  342.  
  343.        /* Delay for port to catch up */
  344.        IO_DELAY(3);        
  345.  
  346.        /* Clear receive register. */
  347.        value_from_port = 
  348.            inp(p_cur_port_info->base_address +
  349.            RECEIVE_REGISTER);  
  350.  
  351.        /* Delay for port to catch up */
  352.        IO_DELAY(3);        
  353.  
  354.        /* Clear line status interrupt */
  355.        value_from_port = 
  356.            inp(p_cur_port_info->base_address +
  357.                LINE_STATUS_REGISTER);     
  358.  
  359.        /* Delay for port to catch up */
  360.        IO_DELAY(3);        
  361.  
  362.        /* Clear modem status interrupt and initialize 
  363.           modem status signal flags */
  364.        value_from_port = 
  365.            inp(p_cur_port_info->base_address +
  366.                MODEM_STATUS_REGISTER);    
  367.        if (IS_CTS(value_from_port))
  368.            p_cur_port_info->cts_state = 1;
  369.        else
  370.            p_cur_port_info->cts_state = 0;
  371.        if (IS_DSR(value_from_port))
  372.            p_cur_port_info->dsr_state = 1;
  373.        else
  374.            p_cur_port_info->dsr_state = 0;
  375.        if (IS_DCD(value_from_port))
  376.            p_cur_port_info->dcd_state = 1;
  377.        else
  378.            p_cur_port_info->dcd_state = 0;
  379.  
  380.        /* Initialize modem status signal states */
  381.  
  382.        /* Delay for port to catch up */
  383.        IO_DELAY(3);        
  384.  
  385.        /* Flush transmit and receive fifos */
  386.        outp(p_cur_port_info->base_address + 
  387.             FIFO_CONTROL_REGISTER,
  388.             (FIFO_ENABLE | FCR_FLUSH_XMIT_FIFO | 
  389.              FCR_FLUSH_RCV_FIFO) );
  390.  
  391.        /* Delay for port to catch up */
  392.        IO_DELAY(3);        
  393.  
  394.        /* Turn on the Data Terminal Ready signal */
  395.        RAISE_DTR; 
  396.  
  397.        /* Point at next port info struct in array */
  398.        p_cur_port_info++;      
  399.  
  400.    }   /* End interrupt enable loop */
  401.  
  402.    /* Re-enable interrupts */
  403.    _enable();      
  404.  
  405.    return(1);
  406. }
  407.  
  408.  
  409. /*****************************************************
  410. Exit_UART()
  411.  
  412. Resets UARTs, unlatches interrupt driver, and re-
  413. enables control-break.
  414. *****************************************************/
  415. void Exit_UART(void)
  416. {
  417.    register int value_from_port;
  418.    unsigned char cur_port;
  419.    struct t_port_info *p_cur_port_info; 
  420.  
  421.    /* If asynch not initialized, just return. */
  422.    if (gp_ctrlbrk_vector == NULL)
  423.        return;
  424.  
  425.  
  426.    /* disable interrupts */
  427.    _disable();         
  428.  
  429.    /* Get current 8259 interrupt controller mask */
  430.    value_from_port = inp(R8259A_MASK);     
  431.  
  432.    /* Delay for port to catch up */
  433.    IO_DELAY(3);        
  434.  
  435.    /* Turn on bit for asynch interrupt so that 
  436.       it will be disabled. */
  437.    value_from_port |= ( 0x01 << g_uart_irq);
  438.  
  439.    /* Output new mask to 8259 interrupt controller */
  440.    outp(R8259A_MASK,value_from_port);      
  441.  
  442.    /* Latch original UART interrupt routine into vector
  443.       table */
  444.    _dos_setvect( (0x08 + g_uart_irq), 
  445.        gp_orig_asynch_vector);
  446.  
  447.    /* Point at entry for first port */
  448.    p_cur_port_info = gp_port_info;  
  449.  
  450.    /* Loop through array of port info entries and
  451.       reset interrupt generation and modem signals
  452.       for each port */
  453.    for (cur_port=1;cur_port<=g_num_ports;cur_port++)
  454.    {
  455.        /* disable interrupt generation on UART */
  456.        outp(p_cur_port_info->base_address + 
  457.             INTERRUPT_ENABLE_REGISTER,0x00);
  458.  
  459.        /* Delay for port to catch up */
  460.        IO_DELAY(3);        
  461.  
  462.        /* Reset Line Control Register */
  463.        outp(p_cur_port_info->base_address + 
  464.             LINE_CONTROL_REGISTER,0x00);
  465.  
  466.        /* Delay for port to catch up */
  467.        IO_DELAY(3);        
  468.  
  469.        /* Reset Modem Control Register */
  470.        outp(p_cur_port_info->base_address + 
  471.             MODEM_CONTROL_REGISTER,0x00);
  472.  
  473.        /* Delay for port to catch up */
  474.        IO_DELAY(3);        
  475.  
  476.        /* Reset the FIFO */
  477.        outp(p_cur_port_info->base_address + 
  478.             FIFO_CONTROL_REGISTER,FCR_RESET_FIFO);
  479.  
  480.        /* Delay for port to catch up */
  481.        IO_DELAY(3);        
  482.  
  483.        /* Point at next port info struct in array */
  484.        p_cur_port_info++;      
  485.    }       /* End reset port loop */
  486.  
  487.  
  488.    /* Re-enable control-break by setting its interrupt
  489.       vector back to the original value */
  490.    _dos_setvect(CTRL_BRKVECT, gp_ctrlbrk_vector);
  491.  
  492.    /* Re-enable interrupts */
  493.    _enable();      
  494.  
  495.    return;
  496. }
  497. /*****************************************************
  498. rc = Send_char(unsigned int port_number,
  499.                unsigned char tx_char)
  500. rc = 1 : Character accepted by circular buffer
  501. rc = -1: Character not accepted for transmission
  502.  
  503. Attempts to queue tx_char for transmission on 
  504. port_number (1 to g_num_ports) by adding it to the
  505. circular transmit buffer.  If port is not currently
  506. transmitting, interrupt driver will be 'woken up' by
  507. bouncing the Transmit Holding Register Empty (THRE)
  508. interrup.
  509. *****************************************************/
  510. int Send_char(unsigned int port_number,
  511.               unsigned char tx_char)
  512. {
  513.    static char a_funcname[] = "Send_char:";
  514.    struct t_port_info *p_cur_port_info; 
  515.    unsigned int next_tail,old_tail;
  516.  
  517.    /* Validate port number */
  518.    if ( (port_number < 1) || 
  519.         (port_number > g_num_ports) )
  520.    {
  521.        fprintf(stderr,"%s bad port_number (%u)\n",
  522.                a_funcname,port_number);
  523.        return(-1);
  524.    }
  525.  
  526.    /* Set pointer to port's info entry */
  527.    p_cur_port_info = &(gp_port_info[port_number -1]);
  528.  
  529.    /* Calculate what the new transmit tail will be, and
  530.       see if loading this character would overflow the
  531.       circular transmit buffer */
  532.    old_tail = p_cur_port_info->tx_tail;
  533.    next_tail = old_tail + 1;
  534.    if (next_tail >= BUFFER_SIZE)
  535.        next_tail = 0;
  536.    if (next_tail == p_cur_port_info->tx_head)
  537.    {
  538.        fprintf(stderr,"%s transmit buffer overflow!\n",
  539.                a_funcname);
  540.        return(-1);
  541.    }
  542.  
  543.    /* Load the character into the circular transmit
  544.       buffer */
  545.    p_cur_port_info->a_transmit_buffer[old_tail] = 
  546.        tx_char;
  547.      
  548.    /* Update tail pointer */
  549.    p_cur_port_info->tx_tail = next_tail;
  550.  
  551.    /* If this is now the only character in the transmit
  552.       buffer, wake up the interrupt driver.  This is 
  553.       held off until this point since the interrupt 
  554.       driver may have emptied the list even while the
  555.       earlier parts of this function were executing! */
  556.    if (old_tail == p_cur_port_info->tx_head)
  557.       BOUNCE_THRE;
  558.  
  559.    return(1);
  560. }
  561.  
  562. /*****************************************************
  563. rc = Read_char(unsigned int port_number)
  564. rc = -1: Error reading 
  565. rc = -2: No data available
  566. otherwise, low byte of rc is data character,
  567. high byte is bitmapped flag (see defines in 
  568. UART.H).
  569.  
  570. Attempts to read the next character from port_number
  571. (1 to g_num_ports)'s circular receive buffer.
  572. *****************************************************/
  573. int Read_char(unsigned int port_number)
  574. {
  575.    static char a_funcname[] = "Read_char:";
  576.    struct t_port_info *p_cur_port_info; 
  577.    int new_head,return_char;
  578.    char *p_temp;
  579.  
  580.    /* Validate port number */
  581.    if ( (port_number < 1) || 
  582.         (port_number > g_num_ports) )
  583.    {
  584.        fprintf(stderr,"%s bad port_number (%u)\n",
  585.                a_funcname,port_number);
  586.        return(-1);
  587.    }
  588.  
  589.    /* Set pointer to port's info entry */
  590.    p_cur_port_info = &(gp_port_info[port_number -1]);
  591.  
  592.    /* Check to see if there is any data */
  593.    if (p_cur_port_info->rx_tail == 
  594.        p_cur_port_info->rx_head)
  595.        return(-2);
  596.  
  597.    /* Retrieve the next character from circular
  598.       recieve buffer, along with its status flags */
  599.    p_temp = p_cur_port_info->a_receive_buffer + 
  600.        p_cur_port_info->rx_head;
  601.    return_char = *( (int *) p_temp);
  602.     
  603.  
  604.    /* Calculate next head position in receive buffer.
  605.       This is done in a temporary variable since 
  606.       interrupt driver could interrupt in the middle
  607.       of the sequence. */
  608.    new_head = p_cur_port_info->rx_head += 2;
  609.    if (new_head >= (BUFFER_SIZE * 2) )
  610.       new_head = 0;
  611.    p_cur_port_info->rx_head = new_head;
  612.  
  613.    return(return_char);
  614. }
  615.