// RS-232 Serial Port Library for DJGPP // Ported from DOS Serial Library 1.4 by Karl Stenerud (MIT License) #include #include #include #include #include #include #include "rs232.h" // ======================================================================== // Defines and macros // ======================================================================== // Port I/O helpers (outportb returns void in DJGPP, but macros need the value) #define OUTP(a, b) (outportb((a), (b)), (b)) #define OUTPW(a, w) (outportw((a), (w)), (w)) // Buffer sizes (power of 2) #define RX_BUFFER_BITS 11 // 2048 #define TX_BUFFER_BITS 11 // 2048 #define RX_BUFFER_SIZE (1L << RX_BUFFER_BITS) #define TX_BUFFER_SIZE (1L << TX_BUFFER_BITS) #define RX_BUFFER_MASK ((1U << RX_BUFFER_BITS) - 1) #define TX_BUFFER_MASK ((1U << TX_BUFFER_BITS) - 1) // Flow control watermarks (percentage of buffer) #define RX_HIGH_PERCENT 80 #define RX_LOW_PERCENT 20 #define TX_HIGH_PERCENT 80 #define TX_LOW_PERCENT 20 #define RX_HIGH_WATER (RX_BUFFER_SIZE * RX_HIGH_PERCENT / 100UL) #define RX_LOW_WATER (RX_BUFFER_SIZE * RX_LOW_PERCENT / 100UL) #define TX_HIGH_WATER (TX_BUFFER_SIZE * TX_HIGH_PERCENT / 100UL) #define TX_LOW_WATER (TX_BUFFER_SIZE * TX_LOW_PERCENT / 100UL) // RX buffer operations #define RX_READ(C) (C)->rxBuff[(C)->rxTail = ((C)->rxTail + 1) & RX_BUFFER_MASK] #define RX_WRITE(C, D) (C)->rxBuff[(C)->rxHead = ((C)->rxHead + 1) & RX_BUFFER_MASK] = (D) #define RX_INIT(C) (C)->rxHead = (C)->rxTail = 0 #define RX_EMPTY(C) ((C)->rxHead == (C)->rxTail) #define RX_FULL(C) ((((C)->rxHead + 1) & RX_BUFFER_MASK) == (C)->rxTail) #define RX_COUNT(C) (((C)->rxHead - (C)->rxTail) & RX_BUFFER_MASK) #define RX_LOWATER(C) (RX_COUNT(C) < RX_LOW_WATER) #define RX_HIWATER(C) (RX_COUNT(C) > RX_HIGH_WATER) // TX buffer operations #define TX_READ(C) (C)->txBuff[(C)->txTail = ((C)->txTail + 1) & TX_BUFFER_MASK] #define TX_WRITE(C, D) (C)->txBuff[(C)->txHead = ((C)->txHead + 1) & TX_BUFFER_MASK] = (D) #define TX_INIT(C) (C)->txHead = (C)->txTail = 0 #define TX_EMPTY(C) ((C)->txHead == (C)->txTail) #define TX_FULL(C) ((((C)->txHead + 1) & TX_BUFFER_MASK) == (C)->txTail) #define TX_COUNT(C) (((C)->txHead - (C)->txTail) & TX_BUFFER_MASK) #define TX_LOWATER(C) (TX_COUNT(C) < TX_LOW_WATER) #define TX_HIWATER(C) (TX_COUNT(C) > TX_HIGH_WATER) // XON/XOFF #define XON 0x11 #define XOFF 0x13 // Default IRQs #define COM1_DEFAULT_IRQ 4 #define COM2_DEFAULT_IRQ 3 #define COM3_DEFAULT_IRQ 4 #define COM4_DEFAULT_IRQ 3 #define COM_MIN RS232_COM1 #define COM_MAX RS232_COM4 #define IRQ_MIN 3 #define IRQ_MAX 15 #define IRQ_NONE 0xFF // PIC registers #define INT_VECTOR_OFFSET 8 #define PIC_MASTER 0x20 #define PIC_SLAVE 0xA0 #define PIC_EOI 0x20 #define PIC_RR 0x02 #define PIC_WRITE_IMR(P, D) outportb((P) | 1, (D)) #define PIC_WRITE_OCW2(P, D) outportb((P), (D)) #define PIC_WRITE_OCW3(P, D) outportb((P), (D) | 8) #define PIC_READ_IMR(P) inportb((P) | 1) #define PIC_END_IRQ(P) PIC_WRITE_OCW2((P), PIC_EOI) #define PIC_ENABLE_IRQ(I) \ { if ((I) <= 7) { PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) & ~(1 << (I))); } \ else { PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) & ~(1 << ((I) - 7))); } } #define PIC_DISABLE_IRQ(I) \ { if ((I) <= 7) { PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) | (1 << (I))); } \ else { PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) | (1 << ((I) - 7))); } } // UART register offsets #define UART_RX 0 #define UART_TX 0 #define UART_IER 1 #define UART_DLL 0 // divisor latch LSB (when DLAB=1) #define UART_DLM 1 // divisor latch MSB (when DLAB=1) #define UART_DLW 0 // divisor latch word (when DLAB=1) #define UART_IIR 2 #define UART_FCR 2 #define UART_LCR 3 #define UART_MCR 4 #define UART_LSR 5 #define UART_MSR 6 // UART read macros #define UART_READ_DATA(C) inportb((C)->base + UART_RX) #define UART_READ_IER(C) inportb((C)->base + UART_IER) #define UART_READ_IIR(C) inportb((C)->base + UART_IIR) #define UART_READ_LCR(C) inportb((C)->base + UART_LCR) #define UART_READ_MCR(C) inportb((C)->base + UART_MCR) #define UART_READ_LSR(C) ((C)->lsr = inportb((C)->base + UART_LSR)) #define UART_READ_MSR(C) ((C)->msr = inportb((C)->base + UART_MSR)) #define UART_READ_BPS(C) ((OUTP((C)->base + UART_LCR, inportb((C)->base + UART_LCR) | LCR_DLAB) & 0) | inportw((C)->base + UART_DLW) | (OUTP((C)->base + UART_LCR, inportb((C)->base + UART_LCR) & ~LCR_DLAB) & 0)) // UART write macros #define UART_WRITE_DATA(C, D) outportb((C)->base + UART_TX, (D)) #define UART_WRITE_IER(C, D) ((C)->ier = OUTP((C)->base + UART_IER, (D))) #define UART_WRITE_FCR(C, D) ((C)->fcr = OUTP((C)->base + UART_FCR, (D))) #define UART_WRITE_LCR(C, D) ((C)->lcr = OUTP((C)->base + UART_LCR, (D))) #define UART_WRITE_MCR(C, D) ((C)->mcr = OUTP((C)->base + UART_MCR, (D))) #define UART_WRITE_BPS(C, D) \ { outportb((C)->base + UART_LCR, inportb((C)->base + UART_LCR) | LCR_DLAB); \ (C)->dlatch = OUTPW((C)->base + UART_DLW, (D)); \ outportb((C)->base + UART_LCR, inportb((C)->base + UART_LCR) & ~LCR_DLAB); } // IER bits #define IER_DATA_READY 0x01 #define IER_TX_HOLD_EMPTY 0x02 #define IER_ERRORS 0x04 #define IER_MODEM_STATUS 0x08 // IIR bits #define IIR_NO_INT_PENDING 0x01 #define IIR_ID0 0x02 #define IIR_ID1 0x04 #define IIR_ID2 0x08 #define IIR_MASK (IIR_NO_INT_PENDING | IIR_ID0 | IIR_ID1) #define IIR_MODEM_STATUS 0x00 #define IIR_TX_HOLD_EMPTY IIR_ID0 #define IIR_DATA_READY IIR_ID1 #define IIR_LINE_STATUS (IIR_ID0 | IIR_ID1) #define IIR_NO_INTERRUPT IIR_NO_INT_PENDING // FCR bits #define FCR_ENABLE 0x01 #define FCR_RX_RESET 0x02 #define FCR_TX_RESET 0x04 #define FCR_DMA_SELECT 0x08 #define FCR_TRIGGER_0 0x40 #define FCR_TRIGGER_1 0x80 #define FCR_TRIGGER_AT_1 0x00 #define FCR_TRIGGER_AT_4 FCR_TRIGGER_0 #define FCR_TRIGGER_AT_8 FCR_TRIGGER_1 #define FCR_TRIGGER_AT_14 (FCR_TRIGGER_0 | FCR_TRIGGER_1) #define FIFO_DEFAULT_THRESHOLD 14 // LCR bits #define LCR_DLAB 0x80 #define LCR_BREAK 0x40 #define LCR_STICK_PARITY 0x20 #define LCR_EVEN_PARITY 0x10 #define LCR_PARITY_ENABLE 0x08 #define LCR_STOP 0x04 #define LCR_WORD1 0x02 #define LCR_WORD0 0x01 #define DATA_5 0x00 #define DATA_6 LCR_WORD0 #define DATA_7 LCR_WORD1 #define DATA_8 (LCR_WORD0 | LCR_WORD1) #define DATA_MASK (LCR_WORD0 | LCR_WORD1) #define PARITY_NONE 0x00 #define PARITY_ODD LCR_PARITY_ENABLE #define PARITY_EVEN (LCR_PARITY_ENABLE | LCR_EVEN_PARITY) #define PARITY_MARK (LCR_PARITY_ENABLE | LCR_STICK_PARITY) #define PARITY_SPACE (LCR_PARITY_ENABLE | LCR_EVEN_PARITY | LCR_STICK_PARITY) #define PARITY_MASK (LCR_PARITY_ENABLE | LCR_EVEN_PARITY | LCR_STICK_PARITY) #define STOP_1 0x00 #define STOP_2 LCR_STOP #define STOP_MASK LCR_STOP // MCR bits #define MCR_DTR 0x01 #define MCR_RTS 0x02 #define MCR_OUT1 0x04 #define MCR_OUT2 0x08 #define MCR_LOOPBACK 0x10 #define MCR_MASK (MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2 | MCR_LOOPBACK) // LSR bits #define LSR_DATA_READY 0x01 #define LSR_OVERRUN 0x02 #define LSR_PARITY_ERR 0x04 #define LSR_FRAME_ERR 0x08 #define LSR_BREAK 0x10 #define LSR_TX_HOLD_EMPTY 0x20 #define LSR_TX_EMPTY 0x40 #define LSR_FIFO_ERR 0x80 // MSR bits #define MSR_DLT_CTS 0x01 #define MSR_DLT_DSR 0x02 #define MSR_NEW_RING 0x04 #define MSR_DLT_DCD 0x08 #define MSR_CTS 0x10 #define MSR_DSR 0x20 #define MSR_RI 0x40 #define MSR_DCD 0x80 // BPS divisors (1.8432 MHz crystal) #define BPS_50 2304 #define BPS_75 1536 #define BPS_110 1047 #define BPS_150 768 #define BPS_300 384 #define BPS_600 192 #define BPS_1200 96 #define BPS_1800 64 #define BPS_2400 48 #define BPS_3800 32 #define BPS_4800 24 #define BPS_7200 16 #define BPS_9600 12 #define BPS_19200 6 #define BPS_38400 3 #define BPS_57600 2 #define BPS_115200 1 #define FIFO_SIZE 16 #define ISR_SIZE 2048 // ======================================================================== // Types // ======================================================================== typedef struct { uint8_t port; uint8_t defaultIrq; uint8_t irq; uint8_t isOpen; uint8_t ier; uint8_t fcr; uint8_t lcr; uint8_t mcr; uint8_t lsr; uint8_t msr; uint8_t flowMode; uint8_t rxFlowOn; uint8_t txFlowOn; uint8_t rxBuff[(uint16_t)RX_BUFFER_SIZE]; uint8_t txBuff[(uint16_t)TX_BUFFER_SIZE]; uint32_t base; uint32_t dlatch; uint32_t rxHead; uint32_t txHead; uint32_t rxTail; uint32_t txTail; } Rs232StateT; // ======================================================================== // Static globals // ======================================================================== static _go32_dpmi_seginfo sOldIsrs[16]; static uint8_t sIsrsTaken[16] = {0}; static _go32_dpmi_seginfo sIsrAddr; static uint32_t sIsrsCount = 0; static Rs232StateT sComPorts[COM_MAX + 1] = { { .port = 0, .defaultIrq = COM1_DEFAULT_IRQ, .irq = IRQ_NONE }, { .port = 1, .defaultIrq = COM2_DEFAULT_IRQ, .irq = IRQ_NONE }, { .port = 2, .defaultIrq = COM3_DEFAULT_IRQ, .irq = IRQ_NONE }, { .port = 3, .defaultIrq = COM4_DEFAULT_IRQ, .irq = IRQ_NONE } }; // ======================================================================== // Static prototypes (alphabetical) // ======================================================================== static void comGeneralIsr(void); static void dpmiGetPvect(int vector, _go32_dpmi_seginfo *info); static int dpmiLockMemory(void); static void dpmiSetPvect(int vector, _go32_dpmi_seginfo *info); static void dpmiUnlockMemory(void); static int findIrq(int com); static void freeIrq(int com); static int installIrqHandler(int irq); static uint8_t picReadIrr(uint16_t port); static void removeIrqHandler(int irq); // ======================================================================== // Interrupt service routine // ======================================================================== static void comGeneralIsr(void) { Rs232StateT *comMin = &sComPorts[0]; Rs232StateT *comMax = &sComPorts[COM_MAX]; uint8_t intId; uint8_t data; uint8_t slaveTriggered = 0; Rs232StateT *com; // Disable IRQs for all open COM ports, then re-enable CPU interrupts // so other faster devices can be serviced for (com = comMin; com <= comMax; com++) { if (com->isOpen) { if (com->irq > 7) { slaveTriggered = 1; } PIC_DISABLE_IRQ(com->irq); } } asm("STI"); // Process all pending interrupts on all open ports for (com = comMin; com <= comMax; com++) { if (com->isOpen) { while ((intId = UART_READ_IIR(com) & IIR_MASK) != IIR_NO_INTERRUPT) { switch (intId) { case IIR_DATA_READY: // Read all available data from the UART while (UART_READ_LSR(com) & LSR_DATA_READY) { data = UART_READ_DATA(com); // Handle XON/XOFF flow control (TX direction) if (com->flowMode == RS232_HANDSHAKE_XONXOFF && (data == XOFF || data == XON)) { com->txFlowOn = (data == XON); if (!TX_EMPTY(com) && com->txFlowOn) { UART_WRITE_IER(com, UART_READ_IER(com) | IER_TX_HOLD_EMPTY); } } else if (!RX_FULL(com)) { // Store data if room in buffer RX_WRITE(com, data); // RX flow control: turn off if buffer almost full if (com->rxFlowOn && RX_HIWATER(com)) { com->rxFlowOn = 0; switch (com->flowMode) { case RS232_HANDSHAKE_XONXOFF: UART_WRITE_DATA(com, XOFF); break; case RS232_HANDSHAKE_RTSCTS: UART_WRITE_MCR(com, UART_READ_MCR(com) & ~MCR_RTS); break; case RS232_HANDSHAKE_DTRDSR: UART_WRITE_MCR(com, UART_READ_MCR(com) & ~MCR_DTR); break; case RS232_HANDSHAKE_NONE: break; } } } } break; case IIR_LINE_STATUS: UART_READ_LSR(com); break; case IIR_MODEM_STATUS: UART_READ_MSR(com); // Handle RTS/CTS or DTR/DSR flow control (TX direction) if (com->flowMode == RS232_HANDSHAKE_RTSCTS) { com->txFlowOn = (com->msr & MSR_CTS) != 0; } else if (com->flowMode == RS232_HANDSHAKE_DTRDSR) { com->txFlowOn = (com->msr & MSR_DSR) != 0; } if (!TX_EMPTY(com) && com->txFlowOn) { UART_WRITE_IER(com, UART_READ_IER(com) | IER_TX_HOLD_EMPTY); } break; case IIR_TX_HOLD_EMPTY: { int cnt; for (cnt = 0; cnt < FIFO_SIZE && com->txFlowOn && !TX_EMPTY(com); cnt++) { UART_WRITE_DATA(com, TX_READ(com)); } if (TX_EMPTY(com) || !com->txFlowOn) { UART_WRITE_IER(com, UART_READ_IER(com) & ~IER_TX_HOLD_EMPTY); } break; } } } } } asm("CLI"); // End the interrupt on the PIC if (slaveTriggered) { PIC_END_IRQ(PIC_SLAVE); } PIC_END_IRQ(PIC_MASTER); // Re-enable all COM port interrupts for (com = comMin; com <= comMax; com++) { if (com->isOpen) { PIC_ENABLE_IRQ(com->irq); } } // Must explicitly STI before IRET because IRET won't always // restore interrupts in a virtual environment asm("STI"); } // ======================================================================== // DPMI utility functions // ======================================================================== static void dpmiGetPvect(int vector, _go32_dpmi_seginfo *info) { _go32_dpmi_get_protected_mode_interrupt_vector(vector, info); } static int dpmiLockMemory(void) { unsigned long dataAddr; unsigned long codeAddr; __dpmi_meminfo dataRegion; __dpmi_meminfo codeRegion; if (__dpmi_get_segment_base_address(_my_cs(), &codeAddr) == 0 && __dpmi_get_segment_base_address(_my_ds(), &dataAddr) == 0) { codeRegion.handle = 0; codeRegion.size = ISR_SIZE; codeRegion.address = codeAddr + (unsigned long)comGeneralIsr; dataRegion.handle = 0; dataRegion.size = sizeof(sComPorts); dataRegion.address = dataAddr + (unsigned long)sComPorts; if (__dpmi_lock_linear_region(&codeRegion) == 0) { if (__dpmi_lock_linear_region(&dataRegion) == 0) { sIsrAddr.pm_offset = (unsigned long)comGeneralIsr; sIsrAddr.pm_selector = _go32_my_cs(); if (_go32_dpmi_allocate_iret_wrapper(&sIsrAddr) == 0) { return 1; } __dpmi_unlock_linear_region(&dataRegion); } __dpmi_unlock_linear_region(&codeRegion); } } return 0; } static void dpmiSetPvect(int vector, _go32_dpmi_seginfo *info) { _go32_dpmi_set_protected_mode_interrupt_vector(vector, info); } static void dpmiUnlockMemory(void) { unsigned long baseAddr; __dpmi_meminfo region; if (__dpmi_get_segment_base_address(_my_ds(), &baseAddr) == 0) { region.handle = 0; region.size = sizeof(sComPorts); region.address = baseAddr + (unsigned long)sComPorts; __dpmi_unlock_linear_region(®ion); } if (__dpmi_get_segment_base_address(_my_cs(), &baseAddr) == 0) { region.handle = 0; region.size = ISR_SIZE; region.address = baseAddr + (unsigned long)comGeneralIsr; __dpmi_unlock_linear_region(®ion); } _go32_dpmi_free_iret_wrapper(&sIsrAddr); } // ======================================================================== // IRQ management // ======================================================================== static int findIrq(int comport) { Rs232StateT *com = &sComPorts[comport]; uint8_t imrM = PIC_READ_IMR(PIC_MASTER); uint8_t imrS = PIC_READ_IMR(PIC_SLAVE); uint8_t irrM; uint8_t irrS; // Set up the UART UART_WRITE_MCR(com, MCR_OUT2); UART_WRITE_FCR(com, 0); // Wait until TX hold register is empty while ((UART_READ_LSR(com) & LSR_TX_HOLD_EMPTY) == 0) { } asm("CLI"); // Allow any interrupt on PIC PIC_WRITE_IMR(PIC_MASTER, 0); PIC_WRITE_IMR(PIC_SLAVE, 0); // Initial polls to let things settle UART_WRITE_IER(com, IER_TX_HOLD_EMPTY); picReadIrr(PIC_MASTER); picReadIrr(PIC_SLAVE); UART_WRITE_IER(com, 0); picReadIrr(PIC_MASTER); picReadIrr(PIC_SLAVE); // Generate an interrupt and record all active IRQs UART_WRITE_IER(com, IER_TX_HOLD_EMPTY); irrM = picReadIrr(PIC_MASTER); irrS = picReadIrr(PIC_SLAVE); // Remove the interrupt and mask out all IRQs still active UART_WRITE_IER(com, 0); irrM &= ~picReadIrr(PIC_MASTER); irrS &= ~picReadIrr(PIC_SLAVE); // Interrupt again to confirm UART_WRITE_IER(com, IER_TX_HOLD_EMPTY); irrM &= picReadIrr(PIC_MASTER); irrS &= picReadIrr(PIC_SLAVE); // Restore everything PIC_WRITE_IMR(PIC_MASTER, imrM); PIC_WRITE_IMR(PIC_SLAVE, imrS); UART_WRITE_IER(com, 0); asm("STI"); switch (irrM) { case 0x01: return 0; case 0x02: return 1; case 0x04: switch (irrS) { case 0x01: return 8; case 0x02: return 9; case 0x04: return 10; case 0x08: return 11; case 0x10: return 12; case 0x20: return 13; case 0x40: return 14; case 0x80: return 15; default: return 2; } case 0x08: return 3; case 0x10: return 4; case 0x20: return 5; case 0x40: return 6; case 0x80: return 7; } return RS232_ERR_IRQ_NOT_FOUND; } static void freeIrq(int comport) { Rs232StateT *com = &sComPorts[comport]; Rs232StateT *comMin = &sComPorts[0]; Rs232StateT *comMax = &sComPorts[COM_MAX]; uint32_t irq = com->irq; Rs232StateT *ptr; if (irq == IRQ_NONE) { return; } asm("CLI"); // Disable interrupts from the UART UART_WRITE_IER(com, 0); UART_WRITE_MCR(com, MCR_OUT2); // Clear the FIFO and RX registers UART_WRITE_FCR(com, 0); for (int i = 0; i < 14; i++) { UART_READ_DATA(com); } UART_READ_IIR(com); UART_READ_LSR(com); UART_READ_DATA(com); UART_READ_IIR(com); UART_READ_MSR(com); UART_READ_IIR(com); UART_READ_LSR(com); UART_READ_IIR(com); com->irq = IRQ_NONE; asm("STI"); // Check if any other port shares this IRQ for (ptr = comMin; ptr <= comMax; ptr++) { if (ptr != com && ptr->irq == irq) { return; } } // No other port uses this IRQ, so restore the old handler PIC_DISABLE_IRQ(irq); removeIrqHandler(irq); } static int installIrqHandler(int irq) { if (!sIsrsTaken[irq]) { if (sIsrsCount++ == 0) { // Lock memory used by interrupt handler in DPMI mode if (!dpmiLockMemory()) { --sIsrsCount; return RS232_ERR_LOCK_MEM; } } dpmiGetPvect(irq + INT_VECTOR_OFFSET, &sOldIsrs[irq]); dpmiSetPvect(irq + INT_VECTOR_OFFSET, &sIsrAddr); sIsrsTaken[irq] = 1; } return RS232_SUCCESS; } static uint8_t picReadIrr(uint16_t port) { PIC_WRITE_OCW3(port, PIC_RR); return inportb(port); } static void removeIrqHandler(int irq) { if (sIsrsTaken[irq]) { dpmiSetPvect(irq + INT_VECTOR_OFFSET, &sOldIsrs[irq]); sIsrsTaken[irq] = 0; if (--sIsrsCount == 0) { dpmiUnlockMemory(); } } } // ======================================================================== // Public functions (alphabetical) // ======================================================================== int rs232ClearRxBuffer(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } asm("CLI"); RX_INIT(port); asm("STI"); return RS232_SUCCESS; } int rs232ClearTxBuffer(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } asm("CLI"); TX_INIT(port); asm("STI"); return RS232_SUCCESS; } int rs232Close(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } // Restore old interrupt handler freeIrq(com); // Turn everything off UART_WRITE_IER(port, 0); UART_WRITE_MCR(port, 0); rs232SetFifoThreshold(com, 0); port->isOpen = 0; return RS232_SUCCESS; } int rs232GetBase(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (port->base < 1 || port->base > 0xFFF) { return RS232_ERR_INVALID_BASE; } return port->base; } int32_t rs232GetBps(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (UART_READ_BPS(port)) { case BPS_115200: return 115200L; case BPS_57600: return 57600L; case BPS_38400: return 38400L; case BPS_19200: return 19200L; case BPS_9600: return 9600L; case BPS_7200: return 7200L; case BPS_4800: return 4800L; case BPS_3800: return 3800L; case BPS_2400: return 2400L; case BPS_1800: return 1800L; case BPS_1200: return 1200L; case BPS_600: return 600L; case BPS_300: return 300L; case BPS_150: return 150L; case BPS_110: return 110L; case BPS_75: return 75L; case BPS_50: return 50L; } return RS232_ERR_INVALID_BPS; } int rs232GetCts(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return (port->msr & MSR_CTS) != 0; } int rs232GetData(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (UART_READ_LCR(port) & DATA_MASK) { case DATA_5: return 5; case DATA_6: return 6; case DATA_7: return 7; case DATA_8: return 8; } return RS232_ERR_INVALID_DATA; } int rs232GetDsr(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return (port->msr & MSR_DSR) != 0; } int rs232GetDtr(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return (port->mcr & MCR_DTR) != 0; } int rs232GetHandshake(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return port->flowMode; } int rs232GetIrq(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (port->irq < IRQ_MIN || port->irq > IRQ_MAX) { return RS232_ERR_INVALID_IRQ; } return port->irq; } int rs232GetLsr(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return port->lsr; } int rs232GetMcr(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return port->mcr; } int rs232GetMsr(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return port->msr; } char rs232GetParity(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (UART_READ_LCR(port) & PARITY_MASK) { case PARITY_NONE: return 'n'; case PARITY_EVEN: return 'e'; case PARITY_ODD: return 'o'; case PARITY_MARK: return 'm'; case PARITY_SPACE: return 's'; } return RS232_ERR_INVALID_PARITY; } int rs232GetRts(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } return (port->mcr & MCR_RTS) != 0; } int rs232GetRxBuffered(int com) { Rs232StateT *port = &sComPorts[com]; int count; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } asm("CLI"); count = RX_COUNT(port); asm("STI"); return count; } int rs232GetStop(int com) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (UART_READ_LCR(port) & STOP_MASK) { case STOP_1: return 1; case STOP_2: return 2; } return RS232_ERR_INVALID_STOP; } int rs232GetTxBuffered(int com) { Rs232StateT *port = &sComPorts[com]; int count; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } asm("CLI"); count = TX_COUNT(port); asm("STI"); return count; } int rs232Open(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake) { Rs232StateT *port = &sComPorts[com]; int rc = RS232_SUCCESS; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (port->isOpen) { return RS232_ERR_ALREADY_OPEN; } port->isOpen = 1; port->rxFlowOn = 1; port->txFlowOn = 1; RX_INIT(port); TX_INIT(port); // Read COM base address from BIOS data area (0040:0000-0040:0007) if (rs232SetBase(com, _farpeekw(_dos_ds, 0x400 + (com << 1))) != RS232_SUCCESS) { return RS232_ERR_NO_UART; } // Turn off UART interrupts UART_WRITE_IER(port, 0); // Auto-detect IRQ rc = findIrq(com); if (rc < 0) { rc = port->defaultIrq; } rc = rs232SetIrq(com, rc); if (rc != RS232_SUCCESS) { return rc; } // Turn off PIC interrupts for this IRQ PIC_DISABLE_IRQ(port->irq); // Configure the port rc = rs232Set(com, bps, dataBits, parity, stopBits, handshake); if (rc == RS232_SUCCESS) { UART_WRITE_MCR(port, MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2); rc = rs232SetFifoThreshold(com, FIFO_DEFAULT_THRESHOLD); // Read initial status UART_READ_LSR(port); UART_READ_MSR(port); } // Enable interrupts UART_WRITE_IER(port, IER_DATA_READY | IER_MODEM_STATUS | IER_ERRORS); PIC_ENABLE_IRQ(port->irq); if (rc != RS232_SUCCESS) { rs232Close(com); } return rc; } int rs232Read(int com, char *data, int len) { Rs232StateT *port = &sComPorts[com]; int i; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (data == 0) { return RS232_ERR_NULL_PTR; } asm("CLI"); for (i = 0; !RX_EMPTY(port) && i < len; i++) { data[i] = RX_READ(port); } // RX flow control: turn back on if buffer almost empty if (!port->rxFlowOn && RX_LOWATER(port)) { port->rxFlowOn = 1; if (port->flowMode == RS232_HANDSHAKE_XONXOFF) { UART_WRITE_DATA(port, XON); } else if (port->flowMode == RS232_HANDSHAKE_RTSCTS) { UART_WRITE_MCR(port, UART_READ_MCR(port) | MCR_RTS); } else if (port->flowMode == RS232_HANDSHAKE_DTRDSR) { UART_WRITE_MCR(port, UART_READ_MCR(port) | MCR_DTR); } } asm("STI"); return i; } int rs232Set(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake) { int rc; rc = rs232SetBps(com, bps); if (rc == RS232_SUCCESS) { rc = rs232SetData(com, dataBits); } if (rc == RS232_SUCCESS) { rc = rs232SetParity(com, parity); } if (rc == RS232_SUCCESS) { rc = rs232SetStop(com, stopBits); } if (rc == RS232_SUCCESS) { rc = rs232SetHandshake(com, handshake); } return rc; } int rs232SetBase(int com, int base) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (base < 1 || base > 0xFFF) { return RS232_ERR_INVALID_BASE; } port->base = base; return RS232_SUCCESS; } int rs232SetBps(int com, int32_t bps) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (bps) { case 115200L: UART_WRITE_BPS(port, BPS_115200); return RS232_SUCCESS; case 57600L: UART_WRITE_BPS(port, BPS_57600); return RS232_SUCCESS; case 38400L: UART_WRITE_BPS(port, BPS_38400); return RS232_SUCCESS; case 19200L: UART_WRITE_BPS(port, BPS_19200); return RS232_SUCCESS; case 9600L: UART_WRITE_BPS(port, BPS_9600); return RS232_SUCCESS; case 7200L: UART_WRITE_BPS(port, BPS_7200); return RS232_SUCCESS; case 4800L: UART_WRITE_BPS(port, BPS_4800); return RS232_SUCCESS; case 3800L: UART_WRITE_BPS(port, BPS_3800); return RS232_SUCCESS; case 2400L: UART_WRITE_BPS(port, BPS_2400); return RS232_SUCCESS; case 1800L: UART_WRITE_BPS(port, BPS_1800); return RS232_SUCCESS; case 1200L: UART_WRITE_BPS(port, BPS_1200); return RS232_SUCCESS; case 600L: UART_WRITE_BPS(port, BPS_600); return RS232_SUCCESS; case 300L: UART_WRITE_BPS(port, BPS_300); return RS232_SUCCESS; case 150L: UART_WRITE_BPS(port, BPS_150); return RS232_SUCCESS; case 110L: UART_WRITE_BPS(port, BPS_110); return RS232_SUCCESS; case 75L: UART_WRITE_BPS(port, BPS_75); return RS232_SUCCESS; case 50L: UART_WRITE_BPS(port, BPS_50); return RS232_SUCCESS; } return RS232_ERR_INVALID_BPS; } int rs232SetData(int com, int dataBits) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (dataBits) { case 5: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~DATA_MASK) | DATA_5); return RS232_SUCCESS; case 6: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~DATA_MASK) | DATA_6); return RS232_SUCCESS; case 7: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~DATA_MASK) | DATA_7); return RS232_SUCCESS; case 8: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~DATA_MASK) | DATA_8); return RS232_SUCCESS; } return RS232_ERR_INVALID_DATA; } int rs232SetDtr(int com, int dtr) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (dtr) { UART_WRITE_MCR(port, port->mcr | MCR_DTR); } else { UART_WRITE_MCR(port, port->mcr & ~MCR_DTR); } return RS232_SUCCESS; } int rs232SetFifoThreshold(int com, int threshold) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (threshold) { case 14: UART_WRITE_FCR(port, FCR_ENABLE | FCR_RX_RESET | FCR_TX_RESET | FCR_TRIGGER_AT_14); break; case 8: UART_WRITE_FCR(port, FCR_ENABLE | FCR_RX_RESET | FCR_TX_RESET | FCR_TRIGGER_AT_8); break; case 4: UART_WRITE_FCR(port, FCR_ENABLE | FCR_RX_RESET | FCR_TX_RESET | FCR_TRIGGER_AT_4); break; case 1: UART_WRITE_FCR(port, FCR_ENABLE | FCR_RX_RESET | FCR_TX_RESET | FCR_TRIGGER_AT_1); break; case 0: UART_WRITE_FCR(port, 0); break; default: return RS232_ERR_INVALID_FIFO; } // Clear any garbage in UART FIFO for (int i = 0; i < 16; i++) { UART_READ_DATA(port); } UART_READ_LSR(port); return RS232_SUCCESS; } int rs232SetHandshake(int com, int handshake) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (handshake < RS232_HANDSHAKE_NONE || handshake > RS232_HANDSHAKE_DTRDSR) { return RS232_ERR_INVALID_HANDSHAKE; } port->txFlowOn = 1; port->rxFlowOn = 1; port->flowMode = handshake; return RS232_SUCCESS; } int rs232SetIrq(int com, int irq) { Rs232StateT *port = &sComPorts[com]; int rc; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (irq < IRQ_MIN || irq > IRQ_MAX) { return RS232_ERR_INVALID_IRQ; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } // Remove any ISRs on this port's current IRQ freeIrq(com); rc = installIrqHandler(irq); if (rc == RS232_SUCCESS) { port->irq = irq; PIC_ENABLE_IRQ(port->irq); } return rc; } int rs232SetMcr(int com, int mcr) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } UART_WRITE_MCR(port, mcr & MCR_MASK); return RS232_SUCCESS; } int rs232SetParity(int com, char parity) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (parity) { case 'n': UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~PARITY_MASK) | PARITY_NONE); return RS232_SUCCESS; case 'e': UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~PARITY_MASK) | PARITY_EVEN); return RS232_SUCCESS; case 'o': UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~PARITY_MASK) | PARITY_ODD); return RS232_SUCCESS; case 'm': UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~PARITY_MASK) | PARITY_MARK); return RS232_SUCCESS; case 's': UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~PARITY_MASK) | PARITY_SPACE); return RS232_SUCCESS; } return RS232_ERR_INVALID_PARITY; } int rs232SetRts(int com, int rts) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (rts) { UART_WRITE_MCR(port, port->mcr | MCR_RTS); } else { UART_WRITE_MCR(port, port->mcr & ~MCR_RTS); } return RS232_SUCCESS; } int rs232SetStop(int com, int stopBits) { Rs232StateT *port = &sComPorts[com]; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } switch (stopBits) { case 1: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~STOP_MASK) | STOP_1); return RS232_SUCCESS; case 2: UART_WRITE_LCR(port, (UART_READ_LCR(port) & ~STOP_MASK) | STOP_2); return RS232_SUCCESS; } return RS232_ERR_INVALID_STOP; } int rs232Write(int com, const char *data, int len) { Rs232StateT *port = &sComPorts[com]; int i; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (data == 0) { return RS232_ERR_NULL_PTR; } for (i = 0; i < len; i++) { // Wait until we can write while (!(UART_READ_LSR(port) & LSR_TX_HOLD_EMPTY)) { } if (port->txFlowOn) { UART_WRITE_DATA(port, data[i]); } else { break; } } return i; } int rs232WriteBuf(int com, const char *data, int len) { Rs232StateT *port = &sComPorts[com]; int i; if (com < COM_MIN || com > COM_MAX) { return RS232_ERR_INVALID_PORT; } if (!port->isOpen) { return RS232_ERR_NOT_OPEN; } if (data == 0) { return RS232_ERR_NULL_PTR; } asm("CLI"); for (i = 0; i < len; i++) { if (TX_FULL(port)) { break; } TX_WRITE(port, data[i]); } // If there's data to send, enable TX_HOLD_EMPTY interrupt if (!TX_EMPTY(port)) { UART_WRITE_IER(port, UART_READ_IER(port) | IER_TX_HOLD_EMPTY); } asm("STI"); return i; }