home *** CD-ROM | disk | FTP | other *** search
- /* rsdrv.c : interrupt driven serial communiaction routines
- *
- * by J. Leppäjärvi (so-jml@stekt.oulu.fi) (C) 1990
- *
- * Comments, suggestions and bug fixes are welcome.
- *
- * For Borland's turbo C, all memory models.
- *
- * This program can be used, copied and modified freely. It
- * is provided 'as is' with no warranty of any kind.
- *
- * Last update: 1.3. 1990
- */
-
- #include "rsdrv.h"
- #include <dos.h>
-
- /* 8250 registers */
-
- #define ASYNC_BASE (_async_param[_port].async_base)
- #define VECT (_async_param[_port].vect)
- #define PICBIT (_async_param[_port].picbit)
-
- #define TX_BUF ASYNC_BASE /* transmit buffer */
- #define RX_BUF ASYNC_BASE /* receive buffer */
- #define DIVISOR ASYNC_BASE /* 16-bit divisor */
- #define INT_CNTL (ASYNC_BASE + 0x01) /* interrupt enable */
- #define INT_ID (ASYNC_BASE + 0x02) /* interrupt id */
- #define LINE_CNTL (ASYNC_BASE + 0x03) /* line control */
- #define MOD_CNTL (ASYNC_BASE + 0x04) /* modem control */
- #define LINE_STAT (ASYNC_BASE + 0x05) /* line status reg */
-
- /* 8250 interrupt identification flags */
-
- #define INT_DET 0x01 /* detect int bit (0 = int pending) */
- #define INT_MSTAT 0x00 /* modem status */
- #define INT_TX 0x02 /* ready to transfer */
- #define INT_RX 0x04 /* received char waiting */
- #define INT_LSTAT 0x06 /* receiver line status */
-
- /* 8250 int enable flags */
-
- #define ENAB_RX 0x01 /* received char waiting */
- #define ENAB_TX 0x02 /* ready to trasfer */
- #define ENAB_LSTAT 0x04 /* line status */
- #define ENAB_MSTAT 0x08 /* modem status */
-
- /* 8250 modem control bits */
-
- #define MCNTL_OUT2 0x08
- #define MCNTL_OUT1 0x04
- #define MCNTL_RTS 0x02
- #define MCNTL_DTR 0x01
-
- /* 8250 status reg bits */
-
- #define RX_READY 0x01
- #define TX_IDLE 0x20
-
- /* line control reg */
-
- #define BREAK 0x40
- #define DLAB 0x80
-
- /* parameters for com1 - com4 */
-
- static struct param
- {
- unsigned async_base;
- unsigned char vect;
- unsigned char picbit;
- } _async_param[4] = {\
- {0x3f8,0x0c,0x10}, \
- {0x2f8,0x0b,0x08}, \
- {0x3e8,0x0c,0x10}, \
- {0x2e8,0x0b,0x08}};
-
- static int _port; /* port number (0 - 3) */
- static void interrupt (*_oldvect)(); /* int vector previous value */
-
- /* in / out fifos */
-
- #define RXBUF 0x80
- #define TXBUF 0x80
-
- static unsigned char _rx_top[RXBUF];
- static unsigned char *_rx_end = &_rx_top[RXBUF - 1];
- static unsigned char *_rx_head = _rx_top;
- static unsigned char *_rx_tail = _rx_top;
-
- static unsigned char _tx_top[TXBUF];
- static unsigned char *_tx_end = &_tx_top[TXBUF - 1];
- static unsigned char *_tx_head = _tx_top;
- static unsigned char *_tx_tail = _tx_top;
-
-
- /* The interrupt handler is invoked every time a char arrives
- * or when the 8250's transmit buffer becomes empty. Incoming
- * characters are placed in a fifo buffer from which they can
- * be read with rsgetc(). The characters to be sent are placed
- * in their fifo with rsputc() and sent by the interrupt handler,
- * if they cannot be sent right away (transmitter busy).
- *
- * The oldest entry in the input fifo is lost if the fifo
- * overflows.
- *
- * An interrupt condition triggered by the 8250 might occur
- * during the processing of a previous interrupt from the
- * same source since both rx and tx interrupts are used.
- * The PIC, however, ignores further interrupts from the
- * same source. This combination causes a situation in which
- * the interrupts from the 8250 might 'dry up', because the
- * interrupt identification register is never read, and so
- * further interrupts from the 8250 are effectively disabled.
- * To correct this, the interrupt handler polls the int id reg
- * until no interrupts are pending. It is essential to do this
- * check after EOI to PIC. On the other hand, no extra EOI's are
- * send to the PIC. (This is the cause of using the somewhat
- * clumsy eoi_flag.) This techique creates a situation in which
- * the actual interrupt calls might nest. However, the same cause
- * shouldn't trigger an interrupt before a communication speed
- * depend time constant has elapsed. In general, this constant must
- * be significantly greater than the time spend in the interrupt
- * routine for this to work anyway, so I figure this is ok.
- */
-
- static void interrupt rstrap()
- {
- unsigned char int_id, eoi_flag = 0;
-
- enable();
-
- while (!((int_id = inportb(INT_ID)) & INT_DET))
- {
- switch(int_id)
- {
- case INT_TX:
- if (_tx_tail != _tx_head)
- {
- outportb(TX_BUF,*_tx_tail);
- if ((++_tx_tail) > _tx_end)
- _tx_tail = _tx_top;
- }
- break;
-
- case INT_RX:
- *(_rx_head++) = inportb(RX_BUF);
- if (_rx_head > _rx_end)
- _rx_head = _rx_top;
-
- if (_rx_head == _rx_tail)
- if (++_rx_tail > _rx_end)
- _rx_tail = _rx_top;
- break;
- }
-
- /* a non-specific eoi to PIC in the first round */
-
- if (!eoi_flag)
- {
- outportb(0x20,0x20);
- eoi_flag = 1;
- }
- }
- }
-
-
- /* rsputc() transmits the character c, either directly
- * or by placing it in the transmit buffer. The interrupts
- * are disabled during this function, since the tx interrupt
- * might occur during the fifo manipulation.
- *
- * returns: 0 success
- * -1 buffer full, can't transmit
- */
-
- int rsputc(c)
- unsigned char c;
- {
- disable();
-
- /* write directly to 8250 if nothing is currently being sent */
-
- if (inportb(LINE_STAT) & TX_IDLE)
- outportb(TX_BUF,c);
- else
- {
- *_tx_head = c;
- if ((++_tx_head) > _tx_end)
- _tx_head = _tx_top;
- if (_tx_head == _tx_tail)
- {
- _tx_head--;
- enable();
- return(-1);
- }
- }
- enable();
- return(0);
- }
-
-
- /* rsgetc() reads a character from the incoming character fifo.
- * Since the rx interrupt might occur during the call, interrupts
- * are disabled.
- *
- * returns : the character read, -1 if none available
- */
-
- int rsgetc()
- {
- unsigned char ch;
-
- disable();
-
- if (_rx_head != _rx_tail)
- {
- ch = *(_rx_tail++);
- if (_rx_tail > _rx_end)
- _rx_tail = _rx_top;
- enable();
- return(ch);
- }
- enable();
- return(-1);
- }
-
-
- /* rsbreak() is used to transmit break. (Timing is up to
- * the caller, 250 ms should be ok.)
- *
- * isbreak = 1 : activate break
- * = 0 : end break
- */
-
- void rsbreak(isbreak)
- int isbreak;
- {
- unsigned char pbyte;
-
- pbyte = inportb(LINE_CNTL);
-
- if (isbreak) pbyte |= BREAK;
- else pbyte &= ~BREAK;
-
- outportb(LINE_CNTL,pbyte);
- }
-
-
- /* rsinit() intializes the serial port and sets up the interrupt handler.
- *
- * port : 1 = com1, 2 = com2, 3 = com3, 4 = com4
- * speed : communication speed
- * pbyte : mode byte, see rsio.h for constants to
- * join together with bitwize-or
- *
- * returns: zero on success, -1 if port number out of range
- */
-
- int rsinit(port,speed,pbyte)
- int port, speed;
- unsigned char pbyte;
- {
- if (port < 1 || port > 4)
- return(-1);
-
- _port = port - 1;
-
- /* store, then modify the int vector */
-
- _oldvect = getvect(VECT);
- setvect(VECT,rstrap);
-
- /* set communication parameters */
-
- speed = 0x180 / (speed / 300);
- outportb(LINE_CNTL,DLAB);
- outport(DIVISOR,speed);
- outportb(LINE_CNTL,pbyte & ~DLAB);
-
- /* set DTR, RTS and OUT2 in modem control register */
-
- outportb(MOD_CNTL,MCNTL_DTR | MCNTL_RTS | MCNTL_OUT2);
-
- /* enable interrupts from 8250 and PIC 8259A */
-
- outportb(INT_CNTL,ENAB_RX | ENAB_TX);
- outportb(0x21,~PICBIT & inportb(0x21));
-
- return(0);
- }
-
-
- /* rsend() 'turns off' the serial communications interrupt and restores
- * the related interrupt vector.
- */
-
- void rsend()
- {
- outportb(INT_CNTL,0x00);
- outportb(0x21,PICBIT | inportb(0x21));
- setvect(VECT,_oldvect);
- }