_IDENTIFYING SERIAL PORT IRQs_ by John Ridley Listing One #ifndef _SERPORT_CONSTANTS_ #define _SERPORT_CONSTANTS_ #define COM1 0 #define COM2 1 #define COM3 2 #define COM4 3 #define NOPORT 0 /*types of UARTS we recognize */ #define T8250 1 #define T16450 2 #define T16550 3 #define T16550AF 4 #define COM1port 0x3f8 #define COM2port 0x2f8 #define COM3port 0x3e8 #define COM4port 0x2e8 #define HW_IRQ_OFF 8 /* Add to IRQ# to get software IRQ vector */ #define LOOPBIT 0x10 /* MCR reg loopback bit */ #define DLAB 0x80 /* DLAB flag position */ /************************ 8250 family UART registers ************************/ /*************** 0 1 2 3 4 5 6 7 */ #define RBR 0 /* ==========Receiver Buffer Register (read only)========== */ #define THR 0 /* ========Transmitter Holding Register (write only)======= */ #define DLL 0 /* Divisor Latch LSB: load these bytes with 115200 / BAUD */ #define DLM 1 /* Divisor Latch MSB: */ /* NOTE: Set DLAB in LCR to write DLL,DLM, then clear DLAB */ #define IER 1 /* ===============Interrupt Enable Register================ */ /* Rcv Xmitr Line Modem 0 0 0 0 */ /* Data Empty Status Status */ #define IIR 2 /* ======Interrupt Identification Register (read only)===== */ /* 0=IRQ ---IRQ ID-- 0 0 0 0 0 */ /* pending bit 0 Bit 1 */ #define LCR 3 /* ================Line Control Register========== Div.lat.*/ /* -Word Length- Stop Parity Even Stick Set AccessBt*/ /* bit 0 bit 1 Bits Enable Parity Parity Break (DLAB) */ #define MCR 4 /* ================Modem Control Register================== */ /* DTR RTS Out1 Out2 Loopback 0 0 0 */ /* NOTE: Out2 must be set (1) on PC's to enable serial port */ #define LSR 5 /* =================Line Status Register=================== */ /* Data Overrun Parity Framing Break Xmit Xmit 0 */ /* Ready Error Error Error Interpt Hold Empty */ /* Empty */ #define MSR 6 /* ===============Modem Status Register==================== */ /* Delta Delta TrailEdg Delta CTS DSR RI CD */ /* CTS DSR RingInd. CD */ #define SCR 7 /* Scratch Register: not present in early 8250's (8250A) */ /****************************************************************************/ /**************************** 8259 control ports ****************************/ /* Generally you only need to deal with OCW1 and OCW2. There is an OCW3 but */ /* it sets up low-level stuff you usually don't want to mess with on a PC. */ /****************************************************************************/ /*************** 0 1 2 3 4 5 6 7 */ #define OCW1 0x21 /* Operation Control Word 1 */ /* Zero in these bits enables the respective IRQ (0-7) */ #define OCW2 0x20 /* Operation Control Word 2 */ /* 001xxxxx : Non-Specific End Of Interrupt (EOI) */ /* 011xxxxx : Specific EOI */ /* 101xxxxx : Rotate on Non-Specific EOI */ /* 100xxxxx : Rotate in Automatic EOI mode (SET) */ /* 000xxxxx : Rotate in Automatic EOI mode (CLEAR) */ /* 11100vvv : Rotate on Specific EOI, vvv indicates IRQ */ /* 11000vvv : Set Priority Command " " " */ /* 010xxxxx : No Operation */ /****************************************************************************/ short IsUART(short which); short WhichUART(short which); #endif Listing Two #include "serport.h" /********************************** IsUART **********************************/ /* Looks for a UART at the specified port by enabling loopback, shoving */ /* a RTS out, and seeing if we get back a CTS */ /****************************************************************************/ short IsUART(short PortAddr) { short HoldMCR, HoldMSR, Port, retval; HoldMCR = _inp(PortAddr+MCR); /* Get Modem Control contents */ _outp(PortAddr+MCR, HoldMCR | LOOPBIT); /* Turn on loopback */ HoldMSR = _inp(PortAddr+MSR); /* Get Modem Status Register */ /* so we can restore MCR later */ _outp(PortAddr+MCR, 0x0a | LOOPBIT); /* Turn on RTS */ if ((_inp(PortAddr+MSR) & 0xf0) == 0x90) /* If CTS is on, there's a UART */ retval = TRUE; else retval = FALSE; _outp(PortAddr+MSR, HoldMSR); /* Restore MCR/MSR */ _outp(PortAddr+MCR, HoldMCR); /* Turn off loopback */ return retval; } /******************************** WhichUART *********************************/ /* You can tell the newer National chips (16550) by reading the high order */ /* bits of the interrupt ID register, which are forced 0 in older chips. */ /****************************************************************************/ short WhichUART(short PortAddr) { short Port, Temp, retval=-1; Temp = _inp(PortAddr+SCR) ^ 0xff; /* Check scratch register */ _outp(PortAddr+SCR, Temp); /* Output complement */ if (_inp(PortAddr+SCR) != Temp) /* check for same return val */ return(T8250); /* No scratch reg: 8250 */ _outp(PortAddr+IIR,7); /* Enable FIFO */ switch(_inp(PortAddr+IIR) & 0xc0) /* Check high bits of IRQ ID reg*/ { case 0: retval = T16450; break; case 0x80: retval = T16550; break; case 0xc0: retval = T16550AF; break; } _outp(PortAddr+IIR,0); /*turn of FIFO */ return retval; } /************************* IRQ Identification code **************************/ /* The remainder of this module deals with identifying which IRQ a */ /* specified UART is set to */ /****************************************************************************/ short CurPortBase; /* shared info: port base addr */ void (__interrupt __far *old_irq[4])(void); /* save old IRQ vectors */ short IRQ_Happened; /* bitmap: which IRQ happened? */ #define IRQbit 0x3c /* 8259 enable IRQ 2-5 */ void __interrupt __far our_irq2() { _enable(); /* Enable CPU interrupts */ IRQ_Happened |= 4; /* Flag which IRQ happened */ _outp(CurPortBase+1, 0x0); /* Mom,make him shut up! */ _outp(OCW2,0x20); /* EOI to 8259 */ } void __interrupt __far our_irq3() /* Same thing for IRQ3-5 */ { _enable(); IRQ_Happened |= 8; _outp(CurPortBase+1, 0x0); _outp(OCW2,0x20); } void __interrupt __far our_irq4() { _enable(); IRQ_Happened |= 16; _outp(CurPortBase+1, 0x0); _outp(OCW2,0x20); } void __interrupt __far our_irq5() { _enable(); IRQ_Happened |= 32; _outp(CurPortBase+1, 0x0); _outp(OCW2,0x20); } void GrabAllIRQ() { short IRQ; for (IRQ = 2; IRQ <= 5; IRQ++) #pragma warning(disable:4113) old_irq[IRQ-2] = _dos_getvect((unsigned)IRQ+HW_IRQ_OFF); #pragma warning(default:4113) _dos_setvect((unsigned)2+HW_IRQ_OFF,our_irq2); _dos_setvect((unsigned)3+HW_IRQ_OFF,our_irq3); _dos_setvect((unsigned)4+HW_IRQ_OFF,our_irq4); _dos_setvect((unsigned)5+HW_IRQ_OFF,our_irq5); } void ReleaseAllIRQ() { short IRQ; for (IRQ = 2; IRQ <= 5; IRQ++) _dos_setvect((unsigned)IRQ+HW_IRQ_OFF,old_irq[IRQ-2]); } unsigned short WhichIRQ(short PortAddr) { short IRQ, HoldOCW1, HoldIER, HoldMCR; if (!IsUART(PortAddr)) /* Don't bother testing */ return 0; CurPortBase = PortAddr; HoldOCW1 = _inp(OCW1); /* remember status of 8259 */ HoldIER = _inp(CurPortBase+IER); /* and of our UART */ HoldMCR = _inp(CurPortBase+MCR); _disable(); /* Be safe... */ _outp(CurPortBase+MCR, 0x03 | 0x08); /* enable port */ GrabAllIRQ(); /* We see all now! */ _outp(OCW1, HoldOCW1 & (~IRQbit)); /* enable 8259 */ _enable(); /* Clear pending IRQ's */ _disable(); /* Ready for the real thing now */ IRQ_Happened = 0; /* Clear bitmap */ _outp(CurPortBase+IER, 0x02); /* enable xmt empty int. */ _enable(); /* BANG! */ _disable(); /* OK, we're done. */ _outp(OCW1, (_inp(OCW1) & (~IRQbit)) | /* Restore 8259 */ (HoldOCW1 & IRQbit)); _outp(CurPortBase+IER, HoldIER); /* Restore our UART */ _outp(CurPortBase+MCR, HoldMCR); ReleaseAllIRQ(); /* Let go of IRQ vectors */ _enable(); /* and relinquish control */ return IRQ_Happened; /* Send back bitmap */ } Listing Three /******************************** PORTINFO.C ********************************/ /* A demo program to utilize SPINFO.C. It scans normal IBM PC serial port */ /* addresses COM1-COM4, determines if there is an 8250 or compatible UART */ /* at the location, what kind of UART it is, and which IRQ it is set to. */ /****************************************************************************/ #include #include #include #include #include #define TRUE 1 #define FALSE 0 #include "spinfo.c" short PortBases[]={COM1port,COM2port,COM3port,COM4port}; char *PortType[]={"NO PORT","8250","16450","16550","16550AF"}; void main() { short Portnum, PortAddr; unsigned short IRQ,IRQ_bitmap; for (Portnum=COM1; Portnum <=COM4; Portnum++) { PortAddr = PortBases[Portnum]; if (IsUART(PortAddr)) { printf("COM%d: ",Portnum+1); printf("%s ",PortType[WhichUART(PortAddr)]); IRQ_bitmap = WhichIRQ(PortAddr); if (IRQ_bitmap == 0) printf("Unable to detect IRQ"); else for (IRQ = 0; IRQ < 16; IRQ++) if (IRQ_bitmap & (1 << IRQ)) printf("IRQ%d ",IRQ); printf("\n"); } } }