From ab69652a723576d70de627cf41cfed8c5124d896 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 10 Mar 2026 20:09:48 -0500 Subject: [PATCH] Added RS232 library. --- README.md => dvx/README.md | 0 rs232/Makefile | 38 + rs232/rs232.c | 1388 ++++++++++++++++++++++++++++++++++++ rs232/rs232.h | 89 +++ 4 files changed, 1515 insertions(+) rename README.md => dvx/README.md (100%) create mode 100644 rs232/Makefile create mode 100644 rs232/rs232.c create mode 100644 rs232/rs232.h diff --git a/README.md b/dvx/README.md similarity index 100% rename from README.md rename to dvx/README.md diff --git a/rs232/Makefile b/rs232/Makefile new file mode 100644 index 0000000..d41c736 --- /dev/null +++ b/rs232/Makefile @@ -0,0 +1,38 @@ +# RS-232 Serial Library Makefile for DJGPP cross-compilation + +DJGPP_PREFIX = $(HOME)/djgpp/djgpp +DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib +CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc +AR = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar +RANLIB = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ranlib +CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 + +OBJDIR = ../obj/rs232 +LIBDIR = ../lib + +SRCS = rs232.c +OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) +TARGET = $(LIBDIR)/librs232.a + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(OBJS) | $(LIBDIR) + $(AR) rcs $@ $(OBJS) + $(RANLIB) $@ + +$(OBJDIR)/%.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(LIBDIR): + mkdir -p $(LIBDIR) + +# Dependencies +$(OBJDIR)/rs232.o: rs232.c rs232.h + +clean: + rm -rf $(OBJDIR) $(TARGET) diff --git a/rs232/rs232.c b/rs232/rs232.c new file mode 100644 index 0000000..9293f14 --- /dev/null +++ b/rs232/rs232.c @@ -0,0 +1,1388 @@ +// 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; +} diff --git a/rs232/rs232.h b/rs232/rs232.h new file mode 100644 index 0000000..0d1549e --- /dev/null +++ b/rs232/rs232.h @@ -0,0 +1,89 @@ +// RS-232 Serial Port Library for DJGPP +// Ported from DOS Serial Library 1.4 by Karl Stenerud (MIT License) +// +// ISR-driven UART communication with ring buffers and flow control. +// Supports up to 4 simultaneous COM ports with auto-detected IRQ. + +#ifndef RS232_H +#define RS232_H + +#include + +// COM Ports +#define RS232_COM1 0 +#define RS232_COM2 1 +#define RS232_COM3 2 +#define RS232_COM4 3 + +// Handshaking Modes +#define RS232_HANDSHAKE_NONE 0 +#define RS232_HANDSHAKE_XONXOFF 1 +#define RS232_HANDSHAKE_RTSCTS 2 +#define RS232_HANDSHAKE_DTRDSR 3 + +// Error Codes +#define RS232_SUCCESS 0 +#define RS232_ERR_UNKNOWN -1 +#define RS232_ERR_NOT_OPEN -2 +#define RS232_ERR_ALREADY_OPEN -3 +#define RS232_ERR_NO_UART -4 +#define RS232_ERR_INVALID_PORT -5 +#define RS232_ERR_INVALID_BASE -6 +#define RS232_ERR_INVALID_IRQ -7 +#define RS232_ERR_INVALID_BPS -8 +#define RS232_ERR_INVALID_DATA -9 +#define RS232_ERR_INVALID_PARITY -10 +#define RS232_ERR_INVALID_STOP -11 +#define RS232_ERR_INVALID_HANDSHAKE -12 +#define RS232_ERR_INVALID_FIFO -13 +#define RS232_ERR_NULL_PTR -14 +#define RS232_ERR_IRQ_NOT_FOUND -15 +#define RS232_ERR_LOCK_MEM -16 + + +// Buffer management +int rs232ClearRxBuffer(int com); +int rs232ClearTxBuffer(int com); + +// Open/close +int rs232Close(int com); +int rs232Open(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake); + +// Getters +int rs232GetBase(int com); +int32_t rs232GetBps(int com); +int rs232GetCts(int com); +int rs232GetData(int com); +int rs232GetDsr(int com); +int rs232GetDtr(int com); +int rs232GetHandshake(int com); +int rs232GetIrq(int com); +int rs232GetLsr(int com); +int rs232GetMcr(int com); +int rs232GetMsr(int com); +char rs232GetParity(int com); +int rs232GetRts(int com); +int rs232GetRxBuffered(int com); +int rs232GetStop(int com); +int rs232GetTxBuffered(int com); + +// Read/write +int rs232Read(int com, char *data, int len); +int rs232Write(int com, const char *data, int len); +int rs232WriteBuf(int com, const char *data, int len); + +// Setters +int rs232Set(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake); +int rs232SetBase(int com, int base); +int rs232SetBps(int com, int32_t bps); +int rs232SetData(int com, int dataBits); +int rs232SetDtr(int com, int dtr); +int rs232SetFifoThreshold(int com, int threshold); +int rs232SetHandshake(int com, int handshake); +int rs232SetIrq(int com, int irq); +int rs232SetMcr(int com, int mcr); +int rs232SetParity(int com, char parity); +int rs232SetRts(int com, int rts); +int rs232SetStop(int com, int stopBits); + +#endif