home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / communic / rsdrv / rsdrv.c next >
Encoding:
C/C++ Source or Header  |  1990-03-05  |  7.7 KB  |  303 lines

  1. /* rsdrv.c : interrupt driven serial communiaction routines
  2.  *
  3.  * by J. Leppäjärvi (so-jml@stekt.oulu.fi) (C) 1990
  4.  *
  5.  * Comments, suggestions and bug fixes are welcome.
  6.  *
  7.  * For Borland's turbo C, all memory models.
  8.  *
  9.  * This program can be used, copied and modified freely. It 
  10.  * is provided 'as is' with no warranty of any kind.
  11.  *
  12.  * Last update: 1.3. 1990
  13.  */
  14.  
  15. #include "rsdrv.h"
  16. #include <dos.h>
  17.  
  18. /* 8250 registers */
  19.  
  20. #define ASYNC_BASE (_async_param[_port].async_base)
  21. #define VECT       (_async_param[_port].vect)
  22. #define PICBIT     (_async_param[_port].picbit)
  23.  
  24. #define TX_BUF    ASYNC_BASE             /* transmit buffer  */
  25. #define RX_BUF    ASYNC_BASE             /* receive buffer   */
  26. #define DIVISOR   ASYNC_BASE             /* 16-bit divisor   */
  27. #define INT_CNTL  (ASYNC_BASE + 0x01)    /* interrupt enable */
  28. #define INT_ID    (ASYNC_BASE + 0x02)    /* interrupt id     */
  29. #define LINE_CNTL (ASYNC_BASE + 0x03)    /* line control     */
  30. #define MOD_CNTL  (ASYNC_BASE + 0x04)    /* modem control    */
  31. #define LINE_STAT (ASYNC_BASE + 0x05)    /* line status reg  */
  32.  
  33. /* 8250 interrupt identification flags */
  34.  
  35. #define INT_DET   0x01                   /* detect int bit (0 = int pending) */
  36. #define INT_MSTAT 0x00                   /* modem status */
  37. #define INT_TX    0x02                   /* ready to transfer */
  38. #define INT_RX    0x04                   /* received char waiting */
  39. #define INT_LSTAT 0x06                   /* receiver line status */
  40.  
  41. /* 8250 int enable flags */
  42.  
  43. #define ENAB_RX    0x01                  /* received char waiting */
  44. #define ENAB_TX    0x02                  /* ready to trasfer */
  45. #define ENAB_LSTAT 0x04                  /* line status */
  46. #define ENAB_MSTAT 0x08                  /* modem status */
  47.  
  48. /* 8250 modem control bits */
  49.  
  50. #define MCNTL_OUT2 0x08
  51. #define MCNTL_OUT1 0x04
  52. #define MCNTL_RTS  0x02
  53. #define MCNTL_DTR  0x01
  54.  
  55. /* 8250 status reg bits */
  56.  
  57. #define RX_READY 0x01
  58. #define TX_IDLE  0x20
  59.  
  60. /* line control reg */
  61.  
  62. #define BREAK    0x40
  63. #define DLAB     0x80
  64.  
  65. /* parameters for com1 - com4 */
  66.  
  67. static struct param
  68. {
  69.  unsigned async_base;
  70.  unsigned char vect;
  71.  unsigned char picbit;
  72. } _async_param[4] = {\
  73. {0x3f8,0x0c,0x10}, \
  74. {0x2f8,0x0b,0x08}, \
  75. {0x3e8,0x0c,0x10}, \
  76. {0x2e8,0x0b,0x08}};
  77.  
  78. static int _port;                      /* port number  (0 - 3) */
  79. static void interrupt (*_oldvect)();   /* int vector previous value */
  80.  
  81. /* in / out fifos */
  82.  
  83. #define RXBUF 0x80
  84. #define TXBUF 0x80
  85.  
  86. static unsigned char _rx_top[RXBUF];
  87. static unsigned char *_rx_end = &_rx_top[RXBUF - 1];
  88. static unsigned char *_rx_head = _rx_top;
  89. static unsigned char *_rx_tail = _rx_top;
  90.  
  91. static unsigned char _tx_top[TXBUF];
  92. static unsigned char *_tx_end = &_tx_top[TXBUF - 1];
  93. static unsigned char *_tx_head = _tx_top;
  94. static unsigned char *_tx_tail = _tx_top;
  95.  
  96.  
  97. /* The interrupt handler is invoked every time a char arrives
  98.  * or when the 8250's transmit buffer becomes empty. Incoming
  99.  * characters are placed in a fifo buffer from which they can
  100.  * be read with rsgetc(). The characters to be sent are placed
  101.  * in their fifo with rsputc() and sent by the interrupt handler,
  102.  * if they cannot be sent right away (transmitter busy).
  103.  *
  104.  * The oldest entry in the input fifo is lost if the fifo
  105.  * overflows.
  106.  *
  107.  * An interrupt condition triggered by the 8250 might occur
  108.  * during the processing of a previous interrupt from the
  109.  * same source since both rx and tx interrupts are used.
  110.  * The PIC, however, ignores further interrupts from the
  111.  * same source. This combination causes a situation in which
  112.  * the interrupts from the 8250 might 'dry up', because the
  113.  * interrupt identification register is never read, and so
  114.  * further interrupts from the 8250 are effectively disabled.
  115.  * To correct this, the interrupt handler polls the int id reg
  116.  * until no interrupts are pending. It is essential to do this
  117.  * check after EOI to PIC. On the other hand, no extra EOI's are
  118.  * send to the PIC. (This is the cause of using the somewhat 
  119.  * clumsy eoi_flag.) This techique creates a situation in which
  120.  * the actual interrupt calls might nest. However, the same cause
  121.  * shouldn't trigger an interrupt before a communication speed
  122.  * depend time constant has elapsed. In general, this constant must
  123.  * be significantly greater than the time spend in the interrupt
  124.  * routine for this to work anyway, so I figure this is ok.
  125.  */
  126.  
  127. static void interrupt rstrap()
  128. {
  129.  unsigned char int_id, eoi_flag = 0;
  130.  
  131.  enable();
  132.  
  133.  while (!((int_id = inportb(INT_ID)) & INT_DET))
  134.  {
  135.   switch(int_id)
  136.   {
  137.    case INT_TX:
  138.     if (_tx_tail != _tx_head)
  139.     {
  140.      outportb(TX_BUF,*_tx_tail);
  141.      if ((++_tx_tail) > _tx_end)
  142.       _tx_tail = _tx_top;
  143.     }
  144.    break;
  145.  
  146.    case INT_RX:
  147.     *(_rx_head++) = inportb(RX_BUF);
  148.     if (_rx_head > _rx_end)
  149.      _rx_head = _rx_top;
  150.      
  151.     if (_rx_head == _rx_tail)
  152.      if (++_rx_tail > _rx_end)
  153.       _rx_tail = _rx_top;
  154.    break;
  155.   }
  156.  
  157.   /* a non-specific eoi to PIC in the first round */
  158.  
  159.   if (!eoi_flag)
  160.   {
  161.    outportb(0x20,0x20);
  162.    eoi_flag = 1;
  163.   } 
  164.  }
  165.  
  166.  
  167. /* rsputc() transmits the character c, either directly
  168.  * or by placing it in the transmit buffer. The interrupts
  169.  * are disabled during this function, since the tx interrupt 
  170.  * might occur during the fifo manipulation.
  171.  *
  172.  * returns:  0 success
  173.  *          -1 buffer full, can't transmit
  174.  */
  175.  
  176. int rsputc(c)
  177. unsigned char c;
  178. {
  179.  disable();
  180.  
  181.  /* write directly to 8250 if nothing is currently being sent */
  182.  
  183.  if (inportb(LINE_STAT) & TX_IDLE)
  184.   outportb(TX_BUF,c);
  185.  else
  186.  {
  187.   *_tx_head = c;
  188.   if ((++_tx_head) > _tx_end)
  189.    _tx_head = _tx_top;
  190.   if (_tx_head == _tx_tail)
  191.   {
  192.    _tx_head--;
  193.    enable();
  194.    return(-1);
  195.   }
  196.  }
  197.  enable();
  198.  return(0);
  199. }
  200.  
  201.  
  202. /* rsgetc() reads a character from the incoming character fifo.
  203.  * Since the rx interrupt might occur during the call, interrupts
  204.  * are disabled.
  205.  *
  206.  * returns : the character read, -1 if none available
  207.  */
  208.  
  209. int rsgetc()
  210. {
  211.  unsigned char ch;
  212.  
  213.  disable();
  214.  
  215.  if (_rx_head != _rx_tail)
  216.  {
  217.   ch = *(_rx_tail++);
  218.   if (_rx_tail > _rx_end)
  219.    _rx_tail = _rx_top;
  220.   enable(); 
  221.   return(ch);
  222.  }
  223.  enable();
  224.  return(-1);
  225. }
  226.  
  227.  
  228. /* rsbreak() is used to transmit break. (Timing is up to
  229.  * the caller, 250 ms should be ok.)
  230.  *
  231.  * isbreak = 1 : activate break
  232.  *         = 0 : end break
  233.  */
  234.  
  235. void rsbreak(isbreak)
  236. int isbreak;
  237. {
  238.  unsigned char pbyte;
  239.  
  240.  pbyte = inportb(LINE_CNTL);
  241.  
  242.  if (isbreak) pbyte |= BREAK;
  243.  else pbyte &= ~BREAK;
  244.  
  245.  outportb(LINE_CNTL,pbyte);
  246. }
  247.  
  248.  
  249. /* rsinit() intializes the serial port and sets up the interrupt handler. 
  250.  *
  251.  * port  : 1 = com1, 2 = com2, 3 = com3, 4 = com4
  252.  * speed : communication speed
  253.  * pbyte : mode byte, see rsio.h for constants to
  254.  *         join together with bitwize-or
  255.  *
  256.  * returns: zero on success, -1 if port number out of range
  257.  */
  258.  
  259. int rsinit(port,speed,pbyte)
  260. int port, speed;
  261. unsigned char pbyte;
  262. {
  263.  if (port < 1 || port > 4)
  264.   return(-1);
  265.  
  266.  _port = port - 1;
  267.  
  268.  /* store, then modify the int vector */
  269.  
  270.  _oldvect = getvect(VECT);
  271.  setvect(VECT,rstrap);
  272.  
  273.  /* set communication parameters */
  274.  
  275.  speed = 0x180 / (speed / 300);
  276.  outportb(LINE_CNTL,DLAB);
  277.  outport(DIVISOR,speed);
  278.  outportb(LINE_CNTL,pbyte & ~DLAB);
  279.  
  280.  /* set DTR, RTS and OUT2 in modem control register */
  281.  
  282.  outportb(MOD_CNTL,MCNTL_DTR | MCNTL_RTS | MCNTL_OUT2);
  283.  
  284.  /* enable interrupts from 8250 and PIC 8259A */
  285.  
  286.  outportb(INT_CNTL,ENAB_RX | ENAB_TX);
  287.  outportb(0x21,~PICBIT & inportb(0x21));
  288.  
  289.  return(0);
  290. }
  291.  
  292.  
  293. /* rsend() 'turns off' the serial communications interrupt and restores
  294.  * the related interrupt vector.
  295.  */
  296.  
  297. void rsend()
  298. {
  299.  outportb(INT_CNTL,0x00);
  300.  outportb(0x21,PICBIT | inportb(0x21));
  301.  setvect(VECT,_oldvect);
  302. }