/* -- pcserial.c -- Tony Givargis Interrupt driven PC serial interface. */ /*---------------------------------------------------------------------------*/ #include #include #include #include "pcserial.h" /*---------------------------------------------------------------------------*/ /* serialPortTbl Stores pointers to (EL_SerialPort*) ADT's for access by the interrupt service routines. */ static EL_SerialPort* serialPortTbl[2] = { NULL, NULL }; /*---------------------------------------------------------------------------*/ /* InterruptVector1 Interrupt service routine for UART on com 1. */ void interrupt far __ISR1(void) { /* read and save the character */ EL_QueuePush(serialPortTbl[0]->queue, (void*)inp(serialPortTbl[0]->comAdr)); /* send end of interrupt */ (void)outp(0x20, 0x20); } /*---------------------------------------------------------------------------*/ /* InterruptVector2 Interrupt service routine for UART on com 2. */ void interrupt far __ISR2(void) { /* read and save the character */ EL_QueuePush(serialPortTbl[1]->queue, (void*)inp(serialPortTbl[1]->comAdr)); /* send end of interrupt */ (void)outp(0x20, 0x20); } /*---------------------------------------------------------------------------*/ /* SetUART Activates the UART chip. Returns 1 on success. */ static void SetUART(EL_SerialPort* serialPort) { /* setup interrupt refrence table */ serialPortTbl[serialPort->comAdr == 0x2f8 ? 1 : 0] = serialPort; /* setup interrupt handler */ serialPort->oldISR =(void*)getvect(serialPort->comInt); setvect(serialPort->comInt, serialPort->comAdr == 0x2f8 ?__ISR2 :__ISR1); /* configure the UART */ (void)outp(serialPort->comAdr+3, 0x80); (void)outp(serialPort->comAdr+1, serialPort->baudHi); (void)outp(serialPort->comAdr+0, serialPort->baudLo); (void)outp(serialPort->comAdr+3, 0x00); (void)outp(serialPort->comAdr+3, serialPort->lcr); (void)outp(serialPort->comAdr+4, 0x0f); /* enable serial interrupts */ asm cli (void)outp(0x21, inp(0x21)&serialPort->pic); (void)outp(serialPort->comAdr+1, 0x01); asm sti } /*---------------------------------------------------------------------------*/ /* InitUART De-activates the UART chip. */ static void ClearUART(EL_SerialPort* serialPort) { /* reset modem control register */ (void)outp(serialPort->comAdr+4, 0x00); /* disable serial interrupts */ asm cli (void)outp(0x21, inp(0x21)|~serialPort->pic); (void)outp(serialPort->comAdr+1, 0x00); asm sti /* reset interrupt handler */ setvect(serialPort->comInt, (void interrupt(*)(void))serialPort->oldISR); /* reset interrupt refrence table */ serialPortTbl[serialPort->comAdr == 0x2f8 ? 1 : 0] = NULL; } /*---------------------------------------------------------------------------*/ /* EL_SerialPort Initializes the (port) and returns an EL_SerialPort* on success or NULL otherwise to be use in subsequent calls to serial port functions. */ EL_SerialPort* EL_SerialPortCreate(EL_Port port, EL_Parity parity, long baudRate, int dataBits, int stopBits) { EL_SerialPort* serialPort; /* validate user input */ if( (port != COM1 && port != COM2) || baudRate < 300 || (parity != NONE && parity != ODD && parity != EVEN) || baudRate > 115200L || (dataBits != 7 && dataBits != 8) || (stopBits != 1 && stopBits != 2) || (serialPortTbl[port] != NULL) ) { return NULL; } /* create serialPort ADT */ if( (serialPort = malloc(sizeof(EL_SerialPort))) != NULL ) { if( (serialPort->queue = EL_QueueCreate()) == NULL ) { /* queue allocation failed */ free(serialPort); serialPort = NULL; } else { /* create machine registers and UART masks */ serialPort->comInt = port == COM1 ? 0x00c : 0x00b; serialPort->comAdr = port == COM1 ? 0x3f8 : 0x2f8; serialPort->baudHi = (unsigned)((115200L / baudRate) >> 8); serialPort->baudLo = (unsigned)((115200L / baudRate) & 0x00ff); serialPort->lcr = parity == NONE ? 0x0000 : parity == ODD ? 0x0020 : 0x0030; serialPort->lcr |= dataBits == 7 ? 0x0002 : 0x0003; serialPort->lcr |= stopBits == 1 ? 0x0000 : 0x0004; serialPort->pic = port == COM1 ? 0xef : 0xf7; /* validate user input */ SetUART(serialPort); } } return serialPort; } /*---------------------------------------------------------------------------*/ /* EL_SerialPortClose Destroys (serialPort). */ void EL_SerialPortDestroy(EL_SerialPort* serialPort) { /* clear the UART and de-allocate serialPort */ ClearUART(serialPort); EL_QueueDestroy(serialPort->queue); free(serialPort); } /*---------------------------------------------------------------------------*/ /* EL_SerialPortRead Reads from (serialPort) (szBuf) bytes into (buf). Returns the number of bytes actually read. The use of EL_SerialPortQueueSize before a call to this function is recommended. */ unsigned EL_SerialPortRead(EL_SerialPort* serialPort, void* buf, unsigned szBuf) { unsigned numRead = 0; /* read szBuf bytes while queue is not empty */ while( szBuf-- && EL_QueueSize(serialPort->queue) > 0 ) { ((char*)buf)[numRead++] = (char)EL_QueuePop(serialPort->queue); } return numRead; } /*---------------------------------------------------------------------------*/ /* EL_SerialPortWrite Writes to (serialPort) (szBuf) bytes from (buf). */ void EL_SerialPortWrite(EL_SerialPort* serialPort, void* buf, unsigned szBuf) { /* write szBuf bytes */ while( szBuf-- > 0 ) { /* wait for UART */ while( !(inp(serialPort->comAdr+5) & 0x20) ); /* write a byte */ (void)outp(serialPort->comAdr, *(((char*)buf)++)); } }