home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Zone / VRZONE.ISO / mac / PC / REND386 / JIREND / SERIAL.C < prev    next >
C/C++ Source or Header  |  1993-04-11  |  22KB  |  655 lines

  1. // *********************************
  2. // serial.c
  3. // *********************************
  4. //  DESCRIPTION:
  5. //    This file contains a set of routines for doing low-level
  6. //     serial communications on the IBM PC.  It was translated
  7. //     directly from Wayne Conrad's IBMCOM.PAS version 3.1, with
  8. //     the goal of near-perfect functional correspondence between
  9. //     the Pascal and C versions
  10. //
  11. //  REVISIONS:  18 OCT 89 - RAC - Original translation from IBMCOM.PAS,
  12. //          with liberal plagiarism of comments from the Pascal.
  13. //      31 MAR 93 - J.Isdale - Modified to support multiple lines
  14. //       1 APR 93 - J.Isdale - Modified to dynamically allocate buffers
  15. // *********************************
  16.   
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <dos.h>
  20. #include "serial.h"
  21.   
  22. // *********************************
  23. // *              8250 Definitions
  24. // *********************************
  25.   
  26. // Offsets to various 8250 registers.  Taken from IBM Technical
  27. // Reference Manual, p. 1-225
  28.   
  29. #define TXBUFF  0    /* Transmit buffer register */
  30. #define RXBUFF  0    /* Receive buffer register */
  31. #define DLLSB   0    /* Divisor latch LS byte */
  32. #define DLMSB   1    /* Divisor latch MS byte */
  33. #define IER     1    /* Interrupt enable register */
  34. #define IIR     2    /* Interrupt ID register */
  35. #define LCR     3    /* Line control register */
  36. #define MCR     4    /* Modem control register */
  37. #define LSR     5    /* Line status register */
  38. #define MSR     6    /* Modem status register */
  39.   
  40. // Modem control register bits
  41.   
  42. #define DTR     0x01                    /* Data terminal ready */
  43. #define RTS     0x02                    /* Request to send */
  44. #define OUT1    0x04                    /* Output #1 */
  45. #define OUT2    0x08                    /* Output #2 */
  46. #define LPBK    0x10                    /* Loopback mode bit */
  47.   
  48. // Line status register bits
  49.   
  50. #define RDR     0x01                    /* Receive data ready */
  51. #define ERRS    0x1E                    /* All the error bits */
  52. #define TXR     0x20                    /* Transmitter ready */
  53.   
  54. // Interrupt enable register bits
  55.   
  56. #define DR      0x01                    /* Data ready */
  57. #define THRE    0x02                    /* Tx buffer empty */
  58. #define RLS     0x04                    /* Receive line status */
  59.   
  60. // *********************************
  61. // Names for Numbers
  62. // *********************************
  63.   
  64. #define MAX_PORT  4
  65.   
  66. #define TRUE      1
  67. #define FALSE     0
  68.   
  69. void interrupt com1_interrupt_driver();
  70. void interrupt com2_interrupt_driver();
  71. void interrupt com3_interrupt_driver();
  72. void interrupt com4_interrupt_driver();
  73.   
  74. // *********************************
  75. //      Global Data                *
  76. // *********************************
  77. //#define TX_QUEUE_SIZE   16 /* Transmit queue size.  Change to suit */
  78. //#define RX_QUEUE_SIZE   4096  /* Receive queue size.  Change to suit */
  79.   
  80. typedef struct uart {
  81.    //  UART i/o addresses.  Values depend upon which COMM port is selected
  82.    int   uart_data;     /* Data register */
  83.    int   uart_ier;      /* Interrupt enable register */
  84.    int   uart_iir;      /* Interrupt identification register */
  85.    int   uart_lcr;      /* Line control register */
  86.    int   uart_mcr;      /* Modem control register */
  87.    int   uart_lsr;      /* Line status register */
  88.    int   uart_msr;      /* Modem status register */
  89.   
  90.    char  com_installed;  /* Flag: Communications routines installed */
  91.    int   intnum;         /* Interrupt vector number for chosen port */
  92.    char  i8259bit;       /* 8259 bit mask */
  93.    char  old_i8259_mask; /* Copy as it was when we were called */
  94.    char  old_ier;        /* Modem register contents saved for */
  95.    char  old_mcr;        /*  restoring when we're done */
  96.    void interrupt (*old_vector)();  /* Place to save COM1 vector */
  97.   
  98.    // Transmit queue.
  99.    // Characters are held here until the UART is ready to transmit them.
  100.    char  *tx_queue;     // transmit queue
  101.    int   tx_queue_size;// transmit queue size
  102.    int   tx_in;         /* Index of where to store next character */
  103.    int   tx_out;        /* Index of where to retrieve next character */
  104.    int   tx_chars;      /* Count of characters in queue */
  105.   
  106.    // Receive queue.
  107.    // Received characters are held here until retrieved by com_rx()
  108.    char  *rx_queue;     // receive queue
  109.    int   rx_queue_size;// recieve queue size
  110.    int   rx_in;         /* Index of where to store next character */
  111.    int   rx_out;        /* Index of where to retrieve next character */
  112.    int   rx_chars;      /* Count of characters in queue */
  113. } Uart;
  114.   
  115. // one structure for each port.
  116. // If more than 4, be sure to add proper comN_interrupt_driver()
  117. static Uart ports[MAX_PORT];
  118. #define GetPort(num) (&ports[num-1])
  119.   
  120. // *********************************
  121. //     com_install()
  122. // *********************************
  123. //  DESCRIPTION:   Installs the communications drivers.
  124. //
  125. //  SYNOPSIS:status = com_install(int portnum, int tx_size, int rx_size);
  126. //     int   portnum; Desired port number
  127. //     int   tx_size; size of transmit queue
  128. //     int   rx_size; size of receive queue
  129. //     int   status;
  130. //              0 = Successful installation
  131. //              1 = Invalid port number
  132. //              2 = No UART for specified port
  133. //              3 = Drivers already installed
  134. //              4 = Cant allocate queue buffers
  135. //
  136. //  Note: min size of queues will be 10 characters if give 0
  137. // *********************************
  138.   
  139. const int   uart_base[] =  { 0x3F8, 0x2F8, 0x3E8, 0x2E8 };
  140. const char  intnums[] = { 0x0C,  0x0B,  0x0C,  0x0B };
  141. const char  i8259levels[] =   { 4,     3,     4,     3 };
  142.   
  143. // Install Com handler on Portnum with Queue sizes indicated
  144. int com_install(int portnum, int tx_size, int rx_size)
  145. {
  146.    Uart *p;
  147.   
  148.     // Port number out of bounds
  149.    if ((portnum < 1) || (portnum > MAX_PORT))
  150.       return 1;
  151.    p = GetPort(portnum);
  152.   
  153.    if (p->com_installed)  // Drivers already installed
  154.       return 3;
  155.   
  156.    p->uart_data = uart_base[portnum-1];  // Set UART I/O addresses
  157.    p->uart_ier  = p->uart_data + IER;    /*  for the selected comm */
  158.    p->uart_iir  = p->uart_data + IIR;    /*  port */
  159.    p->uart_lcr  = p->uart_data + LCR;
  160.    p->uart_mcr  = p->uart_data + MCR;
  161.    p->uart_lsr  = p->uart_data + LSR;
  162.    p->uart_msr  = p->uart_data + MSR;
  163.    p->intnum    = intnums[portnum-1];    /* Ditto for interrupt */
  164.    p->i8259bit  = 1 << i8259levels[portnum-1]; //vector and 8259 bit mask
  165.   
  166.    p->old_ier = inportb(p->uart_ier);    /* Return an error if we */
  167.    outportb(p->uart_ier, 0);       /*  can't access the UART */
  168.    if (inportb(p->uart_ier) != 0)
  169.       return 2;
  170.   
  171.    if (tx_size)
  172.    {
  173.       p->tx_queue = malloc(tx_size);
  174.       p->tx_queue_size = tx_size;
  175.    }
  176.    else
  177.    {
  178.       p->tx_queue = malloc(10);
  179.       p->tx_queue_size = tx_size;
  180.    }
  181.    if (!p->tx_queue)
  182.       return 4;
  183.    if (rx_size)
  184.    {
  185.       p->rx_queue = malloc(rx_size);
  186.       p->rx_queue_size = rx_size;
  187.    }
  188.    else
  189.    {
  190.       p->rx_queue = malloc(10);
  191.       p->rx_queue_size = rx_size;
  192.    }
  193.    if (!p->rx_queue)
  194.    {
  195.       free(p->tx_queue);
  196.       p->tx_queue=NULL;
  197.       return 4;
  198.    }
  199.    disable();
  200.     // Save the original 8259 mask, then disable the 8259 for this interrupt
  201.    p->old_i8259_mask = inportb(0x21);
  202.    outportb(0x21, p->old_i8259_mask | p->i8259bit);
  203.    enable();
  204.   
  205.    com_flush_tx(portnum);           // Clear the transmit and
  206.    com_flush_rx(portnum);           //  receive queues
  207.   
  208.     // Save old COMM vector, & install new one
  209.    p->old_vector = getvect(p->intnum);
  210.    switch (portnum)
  211.    {
  212.       case 1:
  213.          setvect(p->intnum, com1_interrupt_driver);
  214.          break;
  215.       case 2:
  216.          setvect(p->intnum, com2_interrupt_driver);
  217.          break;
  218.       case 3:
  219.          setvect(p->intnum, com3_interrupt_driver);
  220.          break;
  221.       case 4:
  222.          setvect(p->intnum, com4_interrupt_driver);
  223.          break;
  224.    }
  225.    p->com_installed = TRUE;        /*  and note that we did */
  226.   
  227.     // 8 data, no parity, 1 stop */
  228.    outportb(p->uart_lcr, DATA8 + NOPAR + STOP1);
  229.   
  230.    disable();             /* Save MCR, then enable */
  231.    p->old_mcr = inportb(p->uart_mcr);    /*  interrupts onto the bus, */
  232.    outportb(p->uart_mcr,           /*  activate RTS and leave */
  233.    (p->old_mcr & DTR) | (OUT2 + RTS));  /*  DTR the way it was */
  234.    enable();
  235.   
  236.    outportb(p->uart_ier, DR);         /* Enable receive interrupts */
  237.   
  238.     // Now enable the 8259 for this interrupt
  239.    disable();
  240.    outportb(0x21, inportb(0x21) & ~(p->i8259bit));
  241.    enable();
  242.   
  243.     // Successful installation
  244.    return 0;
  245. }
  246.   
  247. // *********************************
  248. //            com_deinstall()
  249. // *********************************
  250. //  DESCRIPTION:   Denstalls the communications drivers completely,
  251. //     without changing the baud rate or DTR.  It tries to leave the
  252. //     interrupt vectors and enables and everything else as they
  253. //     were when the driver was installed.
  254. //
  255. //  NOTE: This function MUST be called before returning to DOS, so the
  256. //     interrupt vector won't point to our driver anymore, since it
  257. //     will surely get overwritten by some other transient program
  258. //     eventually.
  259. //
  260. // *********************************
  261.   
  262. void com_deinstall(int portnum)
  263. {
  264.    Uart *p= GetPort(portnum);
  265.   
  266.    if (p->com_installed) {         /* Don't de-install twice! */
  267.       outportb(p->uart_mcr, p->old_mcr);      /* Restore the UART */
  268.       outportb(p->uart_ier, p->old_ier);      /*  registers ... */
  269.       disable();
  270.       outportb(0x21,                          /*  ... the 8259 interrupt */
  271.       (inportb(0x21)  & ~p->i8259bit) |   /*  mask ... */
  272.       (p->old_i8259_mask &  p->i8259bit));
  273.       enable();
  274.       setvect(p->intnum, p->old_vector);      /*  ... and the comm */
  275.       p->com_installed = FALSE;               /*  interrupt vector */
  276.         // free queues
  277.       free(p->tx_queue); p->tx_queue = NULL;
  278.       free(p->rx_queue); p->rx_queue = NULL;
  279.    }
  280. }
  281.   
  282. // *********************************
  283. //           com_set_speed()
  284. // *********************************
  285. //  DESCRIPTION:   Sets the baud rate.
  286. //
  287. //  SYNOPSIS:   void com_set_speed(unsigned speed);
  288. //     unsigned speed;         Desired baud rate
  289. //
  290. //  NOTES:   The input parameter can be anything between 2 and 65535.
  291. //     However, I (Wayne) am not sure that extremely high speeds
  292. //     (those above 19200) will always work, since the baud rate
  293. //     divisor will be six or less, where a difference of one can
  294. //     represent a difference in baud rate of 3840 bits per second
  295. //     or more.)
  296. //
  297. // *********************************
  298.   
  299. void com_set_speed(int portnum, long speed)
  300. {
  301.    unsigned   divisor;       /* A local temp */
  302.    register Uart *p= GetPort(portnum);
  303.   
  304.    if (p->com_installed) {
  305.       if (speed < 1) speed = 1;    /* Force proper input */
  306.   
  307.       divisor = 119000L / speed;   /* Recond baud rate divisor 152000 */
  308.       disable();                   /* Interrupts off */
  309.       outportb(p->uart_lcr,           /* Set up to load baud rate */
  310.       inportb(p->uart_lcr) | 0x80);   /*  divisor into UART */
  311.       outport(p->uart_data, divisor); /* Do so */
  312.       outportb(p->uart_lcr,           /* Back to normal UART ops */
  313.       inportb(p->uart_lcr) & ~0x80);
  314.       enable();                    /* Interrupts back on */
  315.    }
  316. }
  317.   
  318. // *********************************
  319. //  com_set_parity()               *
  320. // *********************************
  321. //  DESCRIPTION: Sets the parity and stop bits.
  322. //
  323. //  SYNOPSIS:   void com_set_parity(enum par_code parity, int stop_bits);
  324. //     int   code;
  325. //              COM_NONE = 8 data bits, no parity
  326. //              COM_EVEN = 7 data, even parity
  327. //              COM_ODD  = 7 data, odd parity
  328. //              COM_ZERO = 7 data, parity bit = zero
  329. //              COM_ONE  = 7 data, parity bit = one
  330. //     int   stop_bits;  Must be 1 or 2
  331. //
  332. // *********************************
  333.   
  334. const char  lcr_vals[] = {
  335.    DATA8 + NOPAR,
  336.    DATA7 + EVNPAR,
  337.    DATA7 + ODDPAR,
  338.    DATA7 + STKPAR,
  339.    DATA7 + ZROPAR
  340. } ;
  341.   
  342. void com_set_parity(int portnum, enum par_code parity, int stop_bits)
  343. {
  344.    Uart *p= GetPort(portnum);
  345.    if (!p->com_installed) return;
  346.   
  347.    disable();
  348.    outportb(p->uart_lcr, lcr_vals[parity] | ((stop_bits == 2)?STOP2:STOP1));
  349.    enable();
  350. }
  351.   
  352. void com_pulse_rts(int portnum)
  353. {
  354.    Uart *p= GetPort(portnum);
  355.    if (!p->com_installed) return;
  356.   
  357.    outportb(p->uart_mcr,0x00);     /* clear RTS */
  358.    delay(500);
  359.    outportb(p->uart_mcr,0x02);     /* set RTS   */
  360.    delay(500);
  361. }
  362.   
  363. // *********************************
  364. // com_raise_dtr()                 *
  365. // com_lower_dtr()                 *
  366. // *********************************
  367. //  DESCRIPTION:   These routines raise and lower the DTR line.
  368. //     Lowering DTR causes most modems to hang up.
  369. //
  370. // *********************************
  371.   
  372. void com_lower_dtr(int portnum)
  373. {
  374.    Uart *p= GetPort(portnum);
  375.   
  376.    if (p->com_installed) {
  377.       disable();
  378.       outportb(p->uart_mcr, inportb(p->uart_mcr) & ~DTR);
  379.       enable();
  380.    }
  381. }
  382.   
  383. void com_raise_dtr(int portnum)
  384. {
  385.    Uart *p= GetPort(portnum);
  386.   
  387.    if (p->com_installed) {
  388.       disable();
  389.       outportb(p->uart_mcr, inportb(p->uart_mcr) | DTR);
  390.       enable();
  391.    }
  392. }
  393.   
  394.   
  395. // *********************************
  396. // com_tx()
  397. // com_tx_string()
  398. // *********************************
  399. //  DESCRIPTION: Transmit routines.  com_tx() sends a single character by     *
  400. //     waiting until the transmit buffer isn't full, then putting
  401. //     the character into it.  The interrupt driver will then send
  402. //     the character once it is at the head of the transmit queue
  403. //     and a transmit interrupt occurs.  com_tx_string() sends a
  404. //     string by repeatedly calling com_tx().
  405. //
  406. //  SYNOPSES:   void  com_tx(char c);      Send the character c
  407. //     void  com_tx_string(char *s); Send the string s
  408. //
  409. // *********************************
  410.   
  411. void com_tx(int portnum, char c)
  412. {
  413.    Uart *p= GetPort(portnum);
  414.   
  415.    if (p->com_installed)
  416.    {
  417.       while (!com_tx_ready(portnum))
  418.          ;     /* Wait for non-full buffer */
  419.       disable();   // Interrupts off
  420.       p->tx_queue[p->tx_in++] = c;      // Stuff character in queue
  421.       if (p->tx_in == p->tx_queue_size)
  422.          p->tx_in = 0; // Wrap index if needed
  423.       p->tx_chars++;   // Number of char's in queue
  424.         // Enable UART tx interrupt
  425.       outportb(p->uart_ier, inportb(p->uart_ier) | THRE);
  426.       enable(); // Interrupts back on */
  427.    }
  428. }
  429.   
  430. void com_tx_string(int portnum, char *s)
  431. {
  432.    Uart *p= GetPort(portnum);
  433.   
  434.    if (!p->com_installed) return;
  435.   
  436.    while (*s) com_tx(portnum, *s++);  // Send the string!
  437. }
  438.   
  439. // *********************************
  440. //          com_rx()               *
  441. // *********************************
  442. //  DESCRIPTION:   Returns the next character from the receive buffer,
  443. //     or a NULL character ('\0') if the buffer is empty.
  444. //
  445. //  SYNOPSIS:   c = com_rx();
  446. //     char  c;       The returned character
  447. //
  448. // *********************************
  449.   
  450. unsigned char  com_rx(int portnum)
  451. {
  452.    Uart *p= GetPort(portnum);
  453.    char rv;            /* Local temp */
  454.   
  455.    if (!p->rx_chars || !p->com_installed)   /* Return NULL if receive */
  456.       return '\0';                          /*  buffer is empty */
  457.    disable();                               /* Interrupts off */
  458.    rv = p->rx_queue[p->rx_out++];           /* Grab char from queue */
  459.    if (p->rx_out == p->rx_queue_size)       /* wrap index if needed */
  460.       p->rx_out = 0;
  461.    p->rx_chars--;                           /* One less char in queue */
  462.    enable();                                /* Interrupts back on */
  463.    return rv;                               /* The answer! */
  464. }
  465.   
  466. // *********************************
  467. // *            Queue Status Routines
  468. // **********************************
  469. //  DESCRIPTION:   Small routines to return status of the transmit
  470. //     and receive queues.
  471. //
  472. // **********************************
  473.   
  474. int com_tx_ready(int portnum)
  475. {         /* Return TRUE if the */
  476.    Uart *p= GetPort(portnum);
  477.    return ((p->tx_chars < p->tx_queue_size) || /*  transmit queue can */
  478.    (!p->com_installed));
  479. }
  480.   
  481. int com_tx_empty(int portnum)
  482. {  // Return TRUE if the transmit queue is empty
  483.    Uart *p= GetPort(portnum);
  484.    return (!p->tx_chars || (!p->com_installed));
  485. }
  486.   
  487. int com_rx_empty(int portnum)
  488. { // Return TRUE if the receive queue is empty
  489.    Uart *p= GetPort(portnum);
  490.    return (!p->rx_chars || (!p->com_installed));
  491. }
  492.   
  493. int com_rx_count(int portnum)
  494. { // Return TRUE if the receive queue is empty
  495.    Uart *p= GetPort(portnum);
  496.    if (!p->com_installed) return(0);
  497.    return p->rx_chars;
  498. }
  499.   
  500. // ****************************************
  501. //           com_flush_tx()               *
  502. //           com_flush_rx()               *
  503. // ****************************************
  504. //  DESCRIPTION:   Buffer flushers!  These guys just initialize the transmit
  505. //     and receive queues (respectively) to their empty state.
  506. //                               *
  507. // *********************************
  508.   
  509. void com_flush_tx(int portnum)
  510. {
  511.    register Uart *p= GetPort(portnum);
  512.    disable();
  513.    p->tx_chars = p->tx_in = p->tx_out = 0;
  514.    enable();
  515. }
  516.   
  517. void com_flush_rx(int portnum)
  518. {
  519.    register Uart *p= GetPort(portnum);
  520.    disable();
  521.    p->rx_chars = p->rx_in = p->rx_out = 0;
  522.    enable();
  523. }
  524.   
  525. // ***********************************
  526. //           com_carrier()
  527. // ***********************************
  528. // DESCRIPTION:   Returns TRUE if a carrier is present.
  529. //
  530. // *********************************
  531.   
  532. int com_carrier(int portnum)
  533. {
  534.    register Uart *p= GetPort(portnum);
  535.    if (p->com_installed)
  536.       return(inportb(p->uart_msr) & RLSD);
  537.    else return(0);
  538. }
  539.   
  540. int com_cts(int portnum)
  541. {
  542.    register Uart *p= GetPort(portnum);
  543.    if (p->com_installed)
  544.       return(inportb(p->uart_msr) & CTS);
  545.    else return(0);
  546. }
  547.   
  548. int com_modem_stat(int portnum)
  549. {
  550.    register Uart *p= GetPort(portnum);
  551.    if (p->com_installed)
  552.       return(inportb(p->uart_msr));
  553.    else return(0);
  554. }
  555.   
  556. // *********************************
  557. // *           com_interrupt_driver()
  558. // *********************************
  559. // DESCRIPTION:   Handles communications interrupts.
  560. //    The UART will interruptwhenever a character has been received
  561. //    or when it is ready to transmit another character.  This
  562. //    routine responds by sticking received characters into the
  563. //    receive queue and yanking characters to be transmitted
  564. //    from the transmit queue
  565. //
  566. // REVISIOSN:  18 OCT 89 - RAC - Translated from the Pascal.
  567. // *********************************
  568. void com_driver(register Uart *p);
  569.   
  570. void interrupt com1_interrupt_driver()
  571. {
  572.    disable();
  573.    com_driver(&ports[0]);
  574.    enable();
  575. }
  576.   
  577. void interrupt com2_interrupt_driver()
  578. {
  579.    disable();
  580.    com_driver(&ports[1]);
  581.    enable();
  582. }
  583.   
  584. void interrupt com3_interrupt_driver()
  585. {
  586.    disable();
  587.    com_driver(&ports[2]);
  588.    enable();
  589. }
  590.   
  591. void interrupt com4_interrupt_driver()
  592. {
  593.    disable();
  594.    com_driver(&ports[3]);
  595.    enable();
  596. }
  597.   
  598. void com_driver(register Uart *p)
  599. {
  600.    char iir;           /* Local copy if IIR */
  601.    char c;          /* Local character variable */
  602.   
  603. // While bit 0 of the IIR is 0, there remains an interrupt to process
  604.   
  605.     // While there is an int ...
  606.    while (!((iir = inportb(p->uart_iir)) & 1))
  607.    {
  608.         // Branch on interrupt type
  609.       switch (iir)
  610.       {
  611.          case 0: // Modem status interrupt
  612.             inportb(p->uart_msr);      // Just clear the interrupt
  613.             break;
  614.   
  615.          case 2:          /* Transmit register empty */
  616. // *********************************
  617. // NOTE:  The test of the line status register is to see if the transmit
  618. //    holding register is truly empty.  Some UARTS seem to cause
  619. //    transmit interrupts when the holding register isn't empty,
  620. //    causing transmitted characters to be lost.
  621. // *********************************
  622.             if (p->tx_chars <= 0)
  623.                     // If tx buffer empty, turn off transmit interrupts
  624.                outportb(p->uart_ier,  inportb(p->uart_ier) & ~2);
  625.             else
  626.             {   // Tx buffer not empty
  627.                if (inportb(p->uart_lsr) & TXR) {
  628.                   outportb(p->uart_data, p->tx_queue[p->tx_out++]);
  629.                   if (p->tx_out == p->tx_queue_size)
  630.                      p->tx_out = 0;
  631.                   p->tx_chars--;
  632.                }
  633.             }   // End 'tx buffer not empty
  634.             break;
  635.   
  636.          case 4: // Received data interrupt
  637.             c = inportb(p->uart_data);    // Grab received character
  638.             if (p->rx_chars < p->rx_queue_size)
  639.             {   // If queue not full, save the new character
  640.                p->rx_queue[p->rx_in++] = c;
  641.                if (p->rx_in == p->rx_queue_size) // wrap index if needed
  642.                   p->rx_in = 0;
  643.                p->rx_chars++;         // Count the new character
  644.             }  /* End queue not full */
  645.             break;
  646.   
  647.          case 6:  // Line status interrupt
  648.             inportb(p->uart_lsr); // Just clear the interrupt
  649.             break;
  650.   
  651.       } /* End switch */
  652.    } /* End 'is an interrupt' */
  653.    outportb(0x20, 0x20);        /* Send EOI to 8259 */
  654. }
  655.