/************************************************************** * * * Serielle interrupt rutiner * * ***************************************************************/ #include #include #include #include "uart.h" #define __Seg0040 0x40 #define RBR 0 /* register offsets in 8250 UART */ #define THR 0 /* - - - - - - - */ #define IER 1 /* - - - - - - - */ #define IIR 2 /* - - - - - - - */ #define LCR 3 /* - - - - - - - */ #define MCR 4 /* - - - - - - - */ #define LSR 5 /* - - - - - - - */ #define MSR 6 /* - - - - - - - */ #define DLL 0 /* - - - - - - - */ #define DLM 1 /* - - - - - - - */ #define PIC_MASK (unsigned short) 0x21 /* port address, 8259 mask reg. */ #define PIC_EOI (unsigned short) 0x20 /* port address, 8259 eoi reg. */ #define EOI3 0x63 /* 8259 EOI instruction */ #define EOI4 0x64 /* 8259 EOI instruction */ #define INT3_MASK 0x08 /* 8259 mask for irq3 */ #define INT4_MASK 0x10 /* 8259 mask for irq4 */ #define DTR (unsigned char) 0x01 #define RTS (unsigned char) 0x02 #define CTS (unsigned char) 0x10 #define DSR (unsigned char) 0x20 #define RI (unsigned char) 0x40 #define DCD (unsigned char) 0x80 #define OUT2 (unsigned char) 0x08 #define LOOP (unsigned char) 0x10 #define DLAB (unsigned char) 0x80 #define RXIRQ (unsigned char) 0x01 #define TXIRQ (unsigned char) 0x02 #define RXTXIRQ (unsigned char) 0x03 #define NOIRQ (unsigned char) 0x00 #define DR (unsigned char) 0x01 #define TEMT (unsigned char) 0x40 #define THRE (unsigned char) 0x20 #define ERROR (unsigned char) 0x0E #define BREAK (unsigned char) 0x40 #define inport inp #define outport outp #define inportw inpw #define outportw outpw #define inportb inp #define outportb outp typedef struct { unsigned short base_addr; void (far *rx_func)(int,char); short (far *tx_func)(int); } PORT_TABLE; typedef struct { unsigned int num_a; unsigned int num_b; void (interrupt far *old_vec)(void); int old_mask; } IRQ_TABLE; static void far dummysejr( int no, char temp ) { printf("dummy sej %d %c",no,temp); } static short far dummysejw( int no ) { printf("dummy sej %d %c",no); return(-1); } IRQ_TABLE far irq3,irq4; PORT_TABLE far ports[5]; void InitPortTable ( void (far *rx_handler)(int,char),\ short (far *tx_handler)(int)); void InitPortTable ( void (far *rx_handler)(int,char),\ short (far *tx_handler)(int)) { int n; for (n=0; n<5; n++) { ports[n].base_addr = 0; ports[n].rx_func= rx_handler; ports[n].tx_func= tx_handler; } } #ifndef enable void enable(void); void disable(void); void enable( void) { _enable; } void disable (void) { _disable; } #endif void InstallCharHandler (int com_no,\ void (far *rx_handler)(int,char),\ short (far *tx_handler)(int)) { InitPortTable( &dummysejr, &dummysejw); if (com_no > 0 && com_no < 5) { disable(); ports[com_no].rx_func = rx_handler; ports[com_no].tx_func = tx_handler; enable(); } } static int far UartHandler (int no) { int retval=1; unsigned short base; unsigned char irq_type, tmp; union { unsigned short user_answer; struct { unsigned char lo, hi; } parts; } next_tx; base = ports[no].base_addr; disable(); irq_type = inportb(base+IIR); /* read interrupt ident. register */ switch (irq_type) { /* is an interrupt pending ? */ case 0x06: /* receiver line status error */ tmp = inportb(base+LSR); /* read line status register */ break; case 0x04: /* received data available */ tmp = inportb(base+RBR); /* read receiver buffer register */ (*ports[no].rx_func)(no,tmp);/* call rx char handler with char */ break; case 0x02: /* transmitter hold. register empty */ if( Test_CTS(no) ) { next_tx.user_answer = (*ports[no].tx_func)(no); /* get next char*/ if (next_tx.parts.hi!=0) { /* if hibyte is NOT cleared then */ tmp = inportb(base+IER); tmp &= ~TXIRQ; outportb(base+IER,tmp); /* disable transmitter interrupt */ } else { /* else transmit the character */ outportb(base+THR,next_tx.parts.lo); } } break; case 0x00: /* modem status */ tmp = inportb(base+MSR); /* read modem status register */ break; default: retval=0; break; } tmp = inportb(base+IER); /* rearm PIC if more irq is pending */ outportb(base+IER,NOIRQ); /* clear interrupt output */ outportb(base+IER,tmp); /* enable interrupt again */ enable(); return retval; } static void far interrupt Handle_Irq3 (void) { int active,handled=0; enable(); /* enable other irq's */ active = irq3.num_a; if (active) { /* find comport and */ handled += UartHandler(active); /* call handler */ } active = irq3.num_b; if (active) { /* find comport and */ handled += UartHandler(active); /* call handler */ } #if 0 if( !handled ) (*irq3.old_vec)(); else { #endif disable(); outportb (PIC_EOI,EOI3); /* send EOI to 8259 */ #if 0 } #endif } static void far interrupt Handle_Irq4 (void) { int active,handled=0; enable(); /* enable other irq's */ active = irq4.num_a; if (active) { /* find comport and */ handled += UartHandler(active); /* call handler */ } active = irq4.num_b; if (active) { /* find comport and */ handled += UartHandler(active); /* call handler */ } #if 0 if( !handled ) (*irq4.old_vec)(); else { #endif disable(); outportb (PIC_EOI,EOI4); /* send EOI to 8259 */ #if 0 } #endif } void Install_IrqLevel (int level, int port_a, int port_b) { unsigned short base; unsigned char tmp; if (level == 3) { if (port_a > 0 && port_a < 5) { base = peek(__Seg0040,(port_a-1)<<1); /* base from DOS low memory */ if (base) { disable(); irq3.num_a = port_a; /* insert in interrupt table */ ports[port_a].base_addr = base; tmp = inportb(base+MCR); tmp |= OUT2; outportb(base+MCR,tmp); /* enable IRQ signal via OUT2 */ enable(); } else irq3.num_a = 0; } if (port_b > 0 && port_b < 5) { base = peek(__Seg0040,(port_b-1)<<1); /* base from DOS low memory */ if (base) { disable(); irq3.num_b = port_b; /* insert in interrupt table */ ports[port_b].base_addr = base; tmp = inportb(base+MCR); tmp |= OUT2; outportb(base+MCR,tmp); /* enable IRQ signal via OUT2 */ enable(); } else irq3.num_b = 0; } disable(); irq3.old_vec = _dos_getvect(0x0B); _dos_setvect(0x0B, Handle_Irq3); /* switch interrupt vectors */ tmp = inportb(PIC_MASK); irq3.old_mask = tmp; tmp &= ~INT3_MASK; outportb(PIC_MASK,tmp); /* enable IRQ3 in PIC */ enable(); } if (level == 4) { if (port_a > 0 && port_a < 5) { base = peek(__Seg0040,(port_a-1)<<1); /* base from DOS low memory */ if (base) { disable(); irq4.num_a = port_a; /* insert in interrupt table */ ports[port_a].base_addr = base; tmp = inportb(base+MCR); tmp |= OUT2; outportb(base+MCR,tmp); /* enable IRQ signal via OUT2 */ enable(); } else irq4.num_a = 0; } if (port_b > 0 && port_b < 5) { base = peek(__Seg0040,(port_b-1)<<1); /* base from DOS low memory */ if (base) { disable(); irq4.num_b = port_b; /* insert in interrupt table */ ports[port_b].base_addr = base; tmp = inportb(base+MCR); tmp |= OUT2; outportb(base+MCR,tmp); /* enable IRQ signal via OUT2 */ enable(); } else irq4.num_b = 0; } disable(); irq4.old_vec = _dos_getvect(0x0C); _dos_setvect(0x0C, Handle_Irq4); /* switch interrupt vectors */ tmp = inportb(PIC_MASK); irq4.old_mask = tmp; tmp &= ~INT4_MASK; outportb(PIC_MASK,tmp); /* enable IRQ4 in PIC */ enable(); } } void Remove_IrqLevel (int level) { unsigned char tmp; if (level == 3) { disable(); tmp = inportb(PIC_MASK); tmp |= INT3_MASK; outportb(PIC_MASK,irq3.old_mask); /* restore IRQ3 in PIC */ _dos_setvect(0x0B, irq3.old_vec); /* restore interrupt vectors */ enable(); } if (level == 4) { disable(); tmp = inportb(PIC_MASK); tmp |= INT4_MASK; outportb(PIC_MASK,irq4.old_mask); /* restore IRQ4 in PIC */ _dos_setvect(0x0C, irq4.old_vec); /* restore interrupt vectors */ enable(); } } void Set_RTS (int com_no, int on_off) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { if (on_off) { tmp = inportb(base+MCR); tmp |= RTS; outportb(base+MCR,tmp); /* set RTS active */ } else { tmp = inportb(base+MCR); tmp &= ~RTS; outportb(base+MCR,tmp); /* set RTS passive */ } } } } void Set_DTR (int com_no, int on_off) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { if (on_off) { tmp = inportb(base+MCR); tmp |= DTR; outportb(base+MCR,tmp); /* set DTR active */ } else { tmp = inportb(base+MCR); tmp &= ~DTR; outportb(base+MCR,tmp); /* set DTR passive */ } } } } int Test_DCD (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+MSR); tmp &= DCD; return (tmp > 0); } else return 0; } else return 0; } int Test_DSR (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+MSR); tmp &= DSR; return (tmp > 0); } else return 0; } else return 0; } int Test_CTS (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+MSR); tmp &= CTS; return (tmp > 0); } else return 0; } else return 0; } int Test_RI (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+MSR); tmp &= RI; return (tmp > 0); } else return 0; } else return 0; } void SetLoopBack (int com_no, int on_off) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { /* NB: loopback disconnects all */ base = ports[com_no].base_addr; /* output pins, thereby disabling */ if (base) { /* interrupts via OUT2!! */ disable(); /* This means, you have to use the */ if (on_off) { /* manual (polling) functions below. */ tmp = inportb(base+MCR); tmp |= LOOP; outportb(base+MCR,tmp); /* set LOOP-bit active */ } else { tmp = inportb(base+MCR); tmp &= ~LOOP; outportb(base+MCR,tmp); /* set LOOP-bit passive */ } enable(); } } } void SetBreak (int com_no, int on_off) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { /* */ base = ports[com_no].base_addr; /* */ if (base) { /* */ disable(); /* */ if (on_off) { /* . */ tmp = inportb(base+LCR); tmp |= BREAK; outportb(base+LCR,tmp); /* set BREAK-bit active */ } else { tmp = inportb(base+LCR); tmp &= ~BREAK; outportb(base+LCR,tmp); /* set BREAK-bit passive */ } enable(); } } } int Test_TxEmpty (int com_no) /* Manual function */ { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+LSR); tmp &= TEMT; return (tmp > 0); } else return 0; } else return 0; } int Test_RxFull (int com_no) /* Manual function */ { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+LSR); tmp &= DR; return (tmp > 0); } else return 0; } else return 0; } char Get_RxData (int com_no) /* Manual function */ { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { tmp = inportb(base+LSR); tmp &= DR; if (tmp) { /* if data is ready */ tmp = inportb(base+RBR); /* then get it */ return (tmp); } else return 0; /* else return empty */ } else return 0; } else return 0; } void Put_TxData (int com_no, char ch) /* Manual function */ { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { do { tmp = inportb(base+LSR); tmp &= THRE; } while (!tmp); /* wait until the transmitter is ready */ outportb(base+THR,ch); /* before the character is transmitted */ } } } void GetBaudrate (int com_no, BAUD *result) { unsigned short base,baudrate,divisor; unsigned char tmp,data,par,stop; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); /* no irq when DLAB is set! */ tmp = inportb(base+LCR); tmp |= DLAB; outportb(base+LCR,tmp); /* set DLAB bit */ divisor = inport(base+DLL); /* read word from DLL and DLM */ baudrate = 11520/divisor; /* find tenth of baudrate */ result->bps_10 = baudrate; tmp = inportb(base+LCR); tmp &= ~DLAB; outportb(base+LCR,tmp); /* clear DLAB bit */ enable(); data = (tmp & 0x03) +5; /* find number of databits */ result->databits = data; stop = ((tmp & 0x04)>>2) +1; /* find number of stopbits */ result->stopbits = stop; par = (tmp & 0x08); /* find parity */ if (par) { par = (tmp & 0x10); /* even or odd? */ if (par) result->parity = 'E'; else result->parity = 'O'; } else result->parity = 'N'; } else { result->bps_10 = 0; /* if port not installed */ result->databits = 0; /* then return empty data */ result->stopbits = 0; result->parity = '?'; } } } void SetBaudrate (int com_no, unsigned short rate_10, char parity,\ int databits, int stopbits) { unsigned short base,baudrate,divisor; unsigned char tmp,data,par,stop; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); /* no irq when DLAB is set! */ tmp = inportb(base+LCR); tmp |= DLAB; outportb(base+LCR,tmp); /* set DLAB bit */ divisor = 11520/rate_10; /* find divisor */ outport(base+DLL,divisor); /* write word to divisorregs. */ tmp = inportb(base+LCR); /* read line control register */ tmp &= ~DLAB; outportb(base+LCR,tmp); /* clear DLAB bit */ if (databits < 5) { data = 0; } else { if (databits > 8) { data = 3; } else data = databits - 5; } if ((stopbits == 1) || (stopbits ==2)) { stop = (stopbits-1)<<2; } else stop = 4; if ((parity == 'E') || (parity =='e')) { par = 0x18; } else { if ((parity == 'O') || (parity =='o')) { par = 0x08; } else { par = 0x00; } } tmp = inportb(base+LCR); tmp = data | stop | par; outportb(base+LCR,tmp); /* set parity etc. */ tmp = inportb(base+RBR); /* clear receiver register */ enable(); } } } void EnableRxIrq (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); tmp = inportb(base+RBR); /* clear receiver register */ tmp = inportb(base+RBR); /* clear receiver register */ tmp = inportb(base+IER); tmp |= RXIRQ; outportb(base+IER,NOIRQ); /* clear interrupt output */ outportb(base+IER,tmp); /* enable rx data interrupt */ enable(); } } } void DisableRxIrq (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); tmp = inportb(base+IER); tmp &= ~RXIRQ; outportb(base+IER,tmp); /* disable rx data interrupt */ enable(); } } } void far EnableTxIrq (int com_no) { unsigned short base; unsigned char tmp; union { unsigned short user_answer; struct { unsigned char lo, hi; } parts; } next_tx; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); tmp = inportb(base+IER); if (!(tmp & TXIRQ)) { tmp |= TXIRQ; outportb(base+IER,NOIRQ); /* clear interrupt output */ outportb(base+IER,tmp); /* enable tx data interrupt */ } enable(); } } } void DisableTxIrq (int com_no) { unsigned short base; unsigned char tmp; if (com_no > 0 && com_no < 5) { base = ports[com_no].base_addr; if (base) { disable(); tmp = inportb(base+IER); tmp &= ~TXIRQ; outportb(base+IER,tmp); /* disable tx data interrupt */ enable(); } } } #ifndef peek arrrgh /* ERAJ */ int peek(unsigned int seg, unsigned int ofs) { unsigned char sej; int res; unsigned long adr; adr = seg*0x10+ofs; sej = (*(unsigned char far *) adr); res= sej; adr = seg*0x10+ofs+1; sej = (*(unsigned char far *) adr); res = res +0x100 * sej; return(res); } #endif