SIO.C
上传用户:sunrenlu
上传日期:2022-06-13
资源大小:1419k
文件大小:20k
- /*
- * SIO - Serial port I/O
- * Copyright (c) 1990, 2000 Erick Engelke
- * Utilizes special interrupt handling to support
- * - multiple COM ports per irq
- * - low latency for other interrupts on system board
- * - does not disable interrupts for long like most serial code
- */
- #define TERM
- #define NEW
- #include <dos.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <rtos.h>
- #include <sio.h>
- #include <mem.h>
- #ifdef __DJGPP__
- #include <go32.h>
- #include <pc.h>
- #endif
- //#define FIFO /* Use FIFO if found */
- #define RBR 0 /* receive buffer register */
- #define THR 0 /* transmit buffer register */
- #define IER 1 /* interrupt enable register */
- #define IIR 2 /* interrupt ID register */
- #define LCR 3 /* line control register */
- #define MCR 4 /* modem control register */
- #define LSR 5 /* line status register */
- #define MSR 6 /* modem status register */
- #define DLL 0 /* divisor latch LSB when DLAB=1 */
- #define DLM 1 /* divisor latch MSB when DLAB=1 */
- #define DLAB 0x80 /* divisor latch bit */
- #define OUT2 0x8 /* interrupt enable on PC compatibles */
- #define IIRMASK 0x7 /* valid bits in the IIR register */
- #define IERENABLE 0xf /* enable all SIO interrupt conditions */
- #define FIFOMASK 0x8
- #define DTR 0x01 /* data terminal ready */
- #define RTS 0x02 /* ready to send */
- #define CTS 0x10 /* ready to send */
- #define DELTA_CTS 0x01 /* change in CTS */
- #define DELTA_DSR 0x02 /* change in DSR */
- #define SETBRK 0x40 /* Break control */
- #define OCW1 0x21 /* 8259 */
- #define OCW2 0x20
- /* Line Status Register bits
- */
- #define LSR_DR 0x01 /* receive data ready */
- #define LSR_OVRRUN 0x02
- #define LSR_PARITY 0x04
- #define LSR_FRAME 0x08
- #define LSR_BREAK 0x10
- #define LSR_THRE 0x20 /* transmit holding register empty */
- #define LSR_TSRE 0x40
- #define TX_READY (LSR_THRE|LSR_TSRE)
- #define MAXIRQS 15
- #if defined(__DJGPP__)
- #define IntrHandler struct irq_handler_info
- #define NULL_ISR { {0,0}, {0,0} }
- #elif defined(__TURBOC__)
- typedef void interrupt (*IntrHandler)(void);
- #define NULL_HANDLER ((void far*)0)
- #define NULL_ISR ((void far*)0)
- #elif defined(__BORLANDC__)
- typedef void (cdecl interrupt *IntrHandler)(void);
- #define NULL_HANDLER ((void far*)0)
- #define NULL_ISR ((void far*)0)
- #else
- #error Unsupported compiler
- #endif
- static IntrHandler oldisrs[ MAXIRQS + 1 ] =
- { NULL_ISR, NULL_ISR, NULL_ISR, NULL_ISR,
- NULL_ISR, NULL_ISR, NULL_ISR, NULL_ISR,
- NULL_ISR, NULL_ISR, NULL_ISR, NULL_ISR,
- NULL_ISR, NULL_ISR, NULL_ISR, NULL_ISR };
- typedef struct _sio_str {
- WORD sio_port;
- WORD sio_irq;
- WORD sio_int; /* 8 + irq or ... */
- IntrHandler sio_previsr;
- bq_str *sio_rbq; /* receive byte queue */
- bq_str *sio_tbq; /* transmit byte queue */
- thread_x *sio_notify;
- int sio_message;
- BYTE sio_hwflow; // Hardware flow control enable
- BYTE sio_modemstatus;
- int sio_highwater; // Receive buffer high water mark
- int sio_lowwater; // Receive buffer low water mark
- } sio_str;
- static sio_str sio[ MAXSIO ] = {
- {0, 0, 0, NULL_ISR, NULL, NULL, NULL, 0, 0, 0, 0, 0},
- {0, 0, 0, NULL_ISR, NULL, NULL, NULL, 0, 0, 0, 0, 0},
- {0, 0, 0, NULL_ISR, NULL, NULL, NULL, 0, 0, 0, 0, 0},
- {0, 0, 0, NULL_ISR, NULL, NULL, NULL, 0, 0, 0, 0, 0}};
- static char *sio_err = "SIO call with com port out of range";
- static struct {
- DWORD num_rm_intr;
- DWORD num_pm_intr;
- DWORD num_thre;
- DWORD num_rx_intr;
- DWORD num_fifo_rx_intr;
- DWORD num_modem_stat;
- struct {
- DWORD general;
- DWORD overrun;
- DWORD parity;
- DWORD framing;
- DWORD breaking;
- } num_line_stat;
- } sio_stats [MAXSIO];
- #define test_com_valid( n ) do {
- if (!n || (n > MAXSIO))
- rt_halt (sio_err);
- } while (0)
- #if defined(__BORLANDC__) || defined(__TURBOC__)
- #pragma option -N- /* disable stack checking */
- #endif
- static int sio_isr(int irq)
- {
- int com;
- int handled = 0;
- sio_str *s;
- WORD intid;
- WORD base;
- BYTE b;
- BYTE status;
- for ( com = 0 ; com < MAXSIO ; ++com ) {
- s = &sio[ com ];
- base = s->sio_port;
- if ( base == 0 )
- continue;
- if ( s->sio_irq != irq )
- continue;
- #ifdef __DJGPP__
- sio_stats[com].num_pm_intr++; /* to-do: use a bimodal handler */
- #else
- sio_stats[com].num_rm_intr++;
- #endif
- while ( 1 ) {
- intid = inportb( base + IIR );
- if ( intid & 1 )
- break; /* no interrupt pending */
- switch ( intid & IIRMASK ) {
- case 0 : /* change in modem status */
- s->sio_modemstatus = inportb( base + MSR );
- sio_stats[com].num_modem_stat++;
- if (s->sio_notify)
- ksendmessage (s->sio_notify , s->sio_message+2, s->sio_modemstatus);
- if (s->sio_hwflow) {
- if (s->sio_modemstatus & CTS) {
- if (inportb (base + LSR) & LSR_THRE) {
- if (bq_getbyte (s->sio_tbq, &b))
- outportb (base + THR, b);
- }
- }
- }
- break;
- case 2 : /* ready to transmit */
- sio_stats[com].num_thre++;
- if (s->sio_hwflow) {
- if (!(s->sio_modemstatus & CTS))
- break;
- }
- if ( bq_getbyte( s->sio_tbq, &b ) )
- outportb( base + THR, b );
- break;
- case 4 : /* received data */
- if (intid & FIFOMASK)
- sio_stats[com].num_fifo_rx_intr++;
- else sio_stats[com].num_rx_intr++;
- b = inportb( base + RBR );
- bq_sendbyte( s->sio_rbq , b );
- #if 0
- if (s->sio_notify)
- ksend1message (s->sio_notify , s->sio_message, b);
- #endif
- if (s->sio_hwflow) {
- if (bq_readcount (s->sio_rbq) >= s->sio_highwater) {
- status = inportb (base + MCR);
- outportb (base + MCR, status & ~RTS);
- }
- }
- break;
- case 6 : /* line status */
- b = inportb( base + LSR );
- sio_stats[com].num_line_stat.general++;
- if (b & LSR_OVRRUN)
- sio_stats[com].num_line_stat.overrun++;
- if (b & LSR_PARITY)
- sio_stats[com].num_line_stat.parity++;
- if (b & LSR_FRAME)
- sio_stats[com].num_line_stat.framing++;
- if (b & LSR_BREAK)
- sio_stats[com].num_line_stat.breaking++;
- #if 0
- if (s->sio_notify)
- ksendmessage (s->sio_notify , s->sio_message+1, b);
- #endif
- break;
- default : /* ! added */
- (void) inportb (base + LSR);
- (void) inportb (base + MSR);
- handled--;
- break;
- }
- handled++;
- }
- }
- return (handled);
- }
- //-----------------------------------------------------------------------
- // ----------------------------------------------------------------------
- #define IEC_PRIMARY 0x21
- #define IEC_SECONDARY 0xA1
- #if defined(__DJGPP__)
- #define do_chain(irq) ((void)0) /* never chain */
- #elif defined(__BORLANDC__) || defined(__TURBOC__)
- #define do_chain(irq) if (oldisrs[irq]) (*oldisrs[irq])()
- #endif
- /* this code produces low latency for other interrupt handlers */
- #define UART_INT( irqlevel, iec )
- void interrupt uart_int##irqlevel ( void )
- {
- kinisr++;
- outportb( iec , inportb( iec ) | ( 1 << irqlevel ));
- if ( iec == IEC_SECONDARY )
- outportb( 0xA0, 0x60|irqlevel); /* specific EOI */
- outportb( 0x20, 0x60|irqlevel);
- enable();
-
- /*!! Shouldn't EOI be sent after our work is done? */
- if (!sio_isr( irqlevel ))
- do_chain (irqlevel);
- else {
- disable();
- outportb( iec, inportb( iec ) & ~ ( 1 << irqlevel ));
- }
- kinisr--;
- }
- UART_INT( 3, IEC_PRIMARY )
- UART_INT( 4, IEC_PRIMARY )
- UART_INT( 5, IEC_PRIMARY )
- UART_INT( 6, IEC_PRIMARY )
- UART_INT( 7, IEC_PRIMARY )
- UART_INT( 9, IEC_SECONDARY )
- UART_INT(10, IEC_SECONDARY )
- UART_INT(11, IEC_SECONDARY )
- UART_INT(12, IEC_SECONDARY )
- UART_INT(13, IEC_SECONDARY )
- UART_INT(14, IEC_SECONDARY )
- UART_INT(15, IEC_SECONDARY )
- #if defined(__BORLANDC__) || defined(__TURBOC__)
- #pragma option -N. /* default stack checking */
- #endif
- //---------------------------------------------------------------------------
- int uart_detect( WORD base )
- {
- BYTE x, ov, scr = 1;
- // check for LCR
- ov = inportb( base + 3 );
- outportb( base + 3, 0x1b );
- if ( inportb( base + 3 ) != 0x1b ) return -1;
- outportb( base + 3, 0x03 );
- if ( inportb( base + 3 ) != 0x03 ) return -1;
- // restore its value
- outportb( base + 3, ov );
- // look for scratch register
- ov = inportb( base + 7 );
- outportb( base + 7, 0x55 );
- if ( inportb( base + 7 ) != 0x55 ) scr = 0;
- outportb( base + 7, 0xAA ) ;
- if ( inportb( base + 7 ) != 0xAA ) scr = 0;
- outportb( base + 7, ov );
- // look for FIFO
- outportb( base +2, 0x01);
- x = inportb( base + 2 );
- outportb( base + 2, 0 );
- if ( ! ( x & 0x80 ) ) return scr;
- if ( ! ( x & 0x40 ) ) return scr + 2;
- return scr + 4;
- }
- int uart_enable_fifo( WORD base, WORD trigger )
- {
- // 0 on success or no FIFO
- // -1 on error
- BYTE x;
- outportb( base + 2, 1 ); // key to getting C0 right
- x = inportb( base + 2 ) & 0xC0 ;
- if ( x == 0 ) return( -1 );
- if ( x == 0x80 ) {
- outportb( base + 2, 0 );
- return -1; // bad fifo, disabled it
- }
- outportb( base + 2, trigger & (0xc0 | 0x07) );
- return 0 ;
- }
- void uart_disable_fifo( WORD base )
- {
- outportb( base + 2, 0 );
- }
- //--------------------------------------------------------------------
- void sio_print_stats (int com)
- {
- if (com < 1 || com >= MAXSIO-1)
- return;
- com--;
- printf ("COM%d stats: intr %lu/%lu, THRE %lu, Rx-intr %lu, FIFO %lu, "
- "modem %lu, line %lun",
- com+1, sio_stats[com].num_rm_intr, sio_stats[com].num_pm_intr,
- sio_stats[com].num_thre, sio_stats[com].num_rx_intr,
- sio_stats[com].num_fifo_rx_intr, sio_stats[com].num_modem_stat,
- sio_stats[com].num_line_stat.general);
- printf (" (overrun %lu, parity %lu, framing %lu, breaking %lu)n",
- sio_stats[com].num_line_stat.overrun,
- sio_stats[com].num_line_stat.parity,
- sio_stats[com].num_line_stat.framing,
- sio_stats[com].num_line_stat.breaking);
- }
- //----------------------------------------------------------------------
- void
- sio_init (int com, int port, int irq, int rbufsize, int tbufsize, thread_x *notify, int message)
- {
- static int atexit_done = 0;
- WORD flags;
- sio_str *s;
- void interrupt (*isr)() = NULL;
- sio_close( com ); /* clean up any previous stuff */
- test_com_valid (com);
- rt_cpu_block( &flags );
- s = &sio[ com - 1 ];
- s->sio_port = port;
- s->sio_irq = irq;
- s->sio_notify = notify;
- s->sio_message = message;
- s->sio_highwater = (int)(((long)rbufsize * 90L)/100L);
- s->sio_lowwater = (int)(((long)rbufsize * 10L)/100L);
- s->sio_hwflow = 0;
- s->sio_modemstatus = 0;
- if ( s->sio_rbq != NULL ) bq_free( s->sio_rbq );
- if ( s->sio_tbq != NULL ) bq_free( s->sio_tbq );
- s->sio_rbq = bq_alloc( rbufsize );
- s->sio_tbq = bq_alloc( tbufsize );
- /* disable ints */
- outportb (port + IER , 0);
- switch ( irq ) {
- case 3 : isr = uart_int3; break;
- case 4 : isr = uart_int4; break;
- case 5 : isr = uart_int5; break;
- case 6 : isr = uart_int6; break;
- case 7 : isr = uart_int7; break;
- case 9 : isr = uart_int9; break;
- case 10 : isr = uart_int10; break;
- case 11 : isr = uart_int11; break;
- case 12 : isr = uart_int12; break;
- case 13 : isr = uart_int13; break;
- case 14 : isr = uart_int14; break;
- case 15 : isr = uart_int15; break;
- }
- #ifdef OLD
- switch (com)
- {
- case 1:
- isr = sio_isr1;
- break;
- case 2:
- isr = sio_isr2;
- break;
- }
- #endif
- #ifdef __DJGPP__
- if ( oldisrs[ irq ].new_handler.pm_offset == 0 ) {
- irq_handler_info *inf = rt_enableirq( irq, isr );
- if (!inf)
- {
- fprintf (stderr,"called to hook IRQ %dn", irq);
- return;
- }
- oldisrs[irq] = *inf;
- }
- #else
- if ( oldisrs[ irq ] == NULL_HANDLER ) {
- oldisrs[irq] = rt_enableirq( irq, isr );
- }
- // s->sio_previsr = rt_enableirq (irq, isr);
- #endif
- if (!atexit_done)
- atexit (sio_exit);
- atexit_done = 1;
- rt_cpu_unblock( &flags );
- }
- void sio_close( int com )
- {
- WORD base;
- sio_str *s;
- int tempcom, count;
- test_com_valid( com );
- s = &sio[ com - 1 ];
- base = s->sio_port;
- if ( base ) {
- /* disable sio ints */
- outportb( base + IER, 0 );
- for ( tempcom = count = 0; tempcom < MAXSIO ; ++tempcom )
- if ( sio[ tempcom ].sio_irq == s->sio_irq ) count++;
- /* only remove ISR if we are the last one using it */
- if ( count == 1 )
- {
- #ifdef __DJGPP__
- if ( oldisrs[ s->sio_irq].new_handler.pm_offset)
- {
- rt_disableirq ( s->sio_irq, &oldisrs[ s->sio_irq ] );
- oldisrs[ s->sio_irq ].new_handler.pm_offset = 0;
- }
- #else
- if ( oldisrs[ s->sio_irq] != NULL_HANDLER )
- {
- rt_disableirq ( s->sio_irq, oldisrs[ s->sio_irq ] );
- oldisrs[ s->sio_irq ] = NULL_HANDLER;
- }
- #endif
- }
- /* bring down com line */
- outportb (base + MCR , 0);
- #ifdef FIFO
- uart_disable_fifo( base );
- #endif
- s->sio_port = 0;
- s->sio_irq = 0; /* so last sio_close() on that IRQ knows it */
- }
- if ( s->sio_rbq != NULL ) {
- bq_free( s->sio_rbq );
- s->sio_rbq = NULL;
- }
- if ( s->sio_tbq != NULL ) {
- bq_free( s->sio_tbq );
- s->sio_tbq = NULL;
- }
- }
- void
- sio_setup (int com, DWORD baud, int bits, int parity, int stop, BYTE hwflow)
- {
- WORD flags;
- WORD divisor;
- WORD base;
- BYTE parmbyte;
- sio_str *s;
- BYTE b;
- test_com_valid( com );
- #define LOW( x ) ( x & 255 )
- #define HIGH( x ) ( x >> 8 )
- s = &sio[ com - 1];
- divisor = (WORD) (115200L / baud);
- /* word length from [5,8] gives results [0,3] */
- if ( bits == 0 ) bits = 8;
- parmbyte = bits - 5;
- parmbyte |= (stop -1) << 2;
- if (parity) parmbyte |= 8;
- if (parity == SIO_PARITY_EVEN) parmbyte |= 16;
- rt_cpu_block( &flags );
- base = s->sio_port;
- s->sio_hwflow = hwflow; // establish hardware flow control
- /* disable sio int */
- outportb( base + IER, 0 ); /* disable all serial interrupts */
- /* disable adapter, DTR RTS */
- outportb( base + MCR , 0 );
- /* set DLAB for a moment */
- outportb( base + LCR , inportb( base + LCR ) | DLAB );
- /* pass the divisor */
- outportb( base + DLL , LOW( divisor ));
- outportb( base + DLM , HIGH( divisor ));
- /* clear DLAB */
- outportb( base + LCR , inportb( base + LCR ) & ~DLAB );
- /* set word length and parity */
- outportb( base + LCR, parmbyte );
- /* clear any trash */
- while (inportb (base + LSR) & LSR_DR)
- inportb (base + RBR);
- // Send the initial notification of the line status
- b = inportb (base + LSR);
- if (s->sio_notify)
- ksendmessage (s->sio_notify , s->sio_message+1, b);
- // Send the initial notification of the modem status
- s->sio_modemstatus = inportb( base + MSR );
- if (s->sio_notify)
- ksendmessage (s->sio_notify , s->sio_message+2,
- s->sio_modemstatus | DELTA_CTS | DELTA_DSR);
- /* enable adapter, DTR RTS */
- outportb( base + MCR , DTR | RTS | OUT2 );
- /* enable sio int */
- outportb( base + IER, IERENABLE ); /* enable all serial interrupts */
- #ifdef FIFO
- uart_enable_fifo( base, 0xC0 );
- #endif
- rt_cpu_unblock( &flags );
- }
- int sio_recv_waiting( int com )
- {
- test_com_valid( com );
- return( bq_readcount( sio[ com - 1 ].sio_rbq ) );
- }
- int sio_tran_waiting( int com )
- {
- test_com_valid( com );
- return( bq_readcount( sio[ com - 1 ].sio_tbq ) );
- }
- BYTE sio_readbyte( int com )
- {
- BYTE x;
- BYTE status;
- WORD base;
- sio_str *s;
- test_com_valid( com );
- s = &sio[ com - 1];
- base = s->sio_port;
- bq_readbyte( s->sio_rbq , &x ) ;
- if (s->sio_hwflow)
- {
- if (bq_readcount (s->sio_rbq) <= s->sio_lowwater)
- {
- status = inportb (base + MCR);
- outportb (base + MCR, status | RTS);
- }
- }
- return( x );
- }
- void sio_writebyte (int com, BYTE b)
- {
- sio_str *s;
- BYTE status;
- WORD base;
- WORD flags;
- test_com_valid (com);
- s = &sio[com - 1];
- base = s->sio_port;
- /* always send it to the queue */
- bq_writebyte (s->sio_tbq, b);
- /* now check if we need to start the UART siphon */
- rt_cpu_block( &flags );
- if (!(s->sio_hwflow) || (s->sio_modemstatus & CTS)) {
- status = inportb (base + LSR); /* get UART status */
- if (status & LSR_THRE) {
- /* holding register is empty, fill it if data is available */
- if (bq_getbyte (s->sio_tbq, &b))
- outportb (base + THR, b);
- }
- }
- rt_cpu_unblock (&flags);
- }
- void sio_exit (void)
- {
- int i;
- for (i = 1 ; i < MAXSIO ; ++i)
- {
- if (kdebug > 2)
- sio_print_stats (i);
- sio_close (i);
- }
- }
- void sio_msg( int com, int xmit, int recv )
- {
- sio_str *s;
- test_com_valid( com );
- s = &sio[ com - 1 ];
- if ( xmit ) bq_msg( s->sio_tbq, EMSG_BQ_AVAIL );
- if ( recv ) bq_msg( s->sio_rbq, EMSG_BQ_WAITING );
- }
- BYTE sio_getmcr (int com)
- {
- sio_str *s;
- test_com_valid (com);
- s = &sio[com - 1];
- return (inportb (s->sio_port + MCR));
- }
- void sio_setmcr (int com, BYTE mcr)
- {
- sio_str *s;
- test_com_valid (com);
- s = &sio[com - 1];
- outportb (s->sio_port + MCR, mcr);
- }
- void sio_setbreak (int com, BYTE brk)
- {
- sio_str *s;
- test_com_valid (com);
- s = &sio[com - 1];
- outportb (s->sio_port + LCR,
- (inportb (s->sio_port + LCR) & ~SETBRK) | (brk & SETBRK));
- }