From c559d7409b54556e246c1ab7cab0079fa890d9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Czerpak?= Date: Tue, 29 Dec 2015 17:03:40 +0100 Subject: [PATCH] 2015-12-29 17:03 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + src/3rd/hbdossrl/Makefile + src/3rd/hbdossrl/serial.c + src/3rd/hbdossrl/serial.h + src/3rd/hbdossrl/README + added DOS Serial Library - it's much cleaner and simpler then COMLib we used so far in DOS builds. It also supports hardware and software flow control. Many thanks for Karl Stenerud for his wonderful job. * src/rtl/hbcom.c + added support for DOS Serial Library used as low level backend in Harbour DOS builds. * config/dos/djgpp.mk * config/dos/watcom.mk * config/dyn.mk * config/lib.mk * src/3rd/Makefile * src/Makefile * utils/hbmk2/hbmk2.hbp * utils/hbmk2/hbmk2.prg * use DOS Serial Library (hbdossrl) instead of COMLib (hbpmcom) --- ChangeLog.txt | 23 + config/dos/djgpp.mk | 2 +- config/dos/watcom.mk | 2 +- config/dyn.mk | 2 +- config/lib.mk | 2 +- src/3rd/Makefile | 2 +- src/3rd/hbdossrl/Makefile | 29 + src/3rd/hbdossrl/README | 48 ++ src/3rd/hbdossrl/serial.c | 1677 +++++++++++++++++++++++++++++++++++++ src/3rd/hbdossrl/serial.h | 196 +++++ src/Makefile | 2 +- src/rtl/hbcom.c | 572 ++++++++++++- utils/hbmk2/hbmk2.hbp | 2 +- utils/hbmk2/hbmk2.prg | 2 +- 14 files changed, 2551 insertions(+), 10 deletions(-) create mode 100644 src/3rd/hbdossrl/Makefile create mode 100644 src/3rd/hbdossrl/README create mode 100644 src/3rd/hbdossrl/serial.c create mode 100644 src/3rd/hbdossrl/serial.h diff --git a/ChangeLog.txt b/ChangeLog.txt index 082ff60385..612cb9c42e 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -10,6 +10,29 @@ * Change, ! Fix, % Optimization, + Addition, - Removal, ; Comment */ +2015-12-29 17:03 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + + src/3rd/hbdossrl/Makefile + + src/3rd/hbdossrl/serial.c + + src/3rd/hbdossrl/serial.h + + src/3rd/hbdossrl/README + + added DOS Serial Library - it's much cleaner and simpler then COMLib + we used so far in DOS builds. It also supports hardware and software + flow control. Many thanks for Karl Stenerud for his wonderful job. + + * src/rtl/hbcom.c + + added support for DOS Serial Library used as low level backend in + Harbour DOS builds. + + * config/dos/djgpp.mk + * config/dos/watcom.mk + * config/dyn.mk + * config/lib.mk + * src/3rd/Makefile + * src/Makefile + * utils/hbmk2/hbmk2.hbp + * utils/hbmk2/hbmk2.prg + * use DOS Serial Library (hbdossrl) instead of COMLib (hbpmcom) + 2015-12-24 14:53 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * src/vm/hvm.c ! casting for C++ compilation diff --git a/config/dos/djgpp.mk b/config/dos/djgpp.mk index a3d0e08fb0..9e53169f30 100644 --- a/config/dos/djgpp.mk +++ b/config/dos/djgpp.mk @@ -58,7 +58,7 @@ ifneq ($(HB_LINKING_RTL),) SYSLIBPATHS += $(HB_LIB_WATT) SYSLIBS += watt endif - SYSLIBS += hbpmcom + SYSLIBS += hbdossrl endif SYSLIBS += m diff --git a/config/dos/watcom.mk b/config/dos/watcom.mk index d91a981325..fb6322e858 100644 --- a/config/dos/watcom.mk +++ b/config/dos/watcom.mk @@ -67,7 +67,7 @@ ifneq ($(HB_LINKING_RTL),) ifneq ($(HB_HAS_WATT),) LDLIBS += $(HB_LIB_WATT)/wattcpwf endif - LDLIBS += $(LIB_DIR)/hbpmcom + LDLIBS += $(LIB_DIR)/hbdossrl endif # workaround for not included automatically CLIB in pure C mode builds diff --git a/config/dyn.mk b/config/dyn.mk index 2d0665c97f..9cfc945fac 100644 --- a/config/dyn.mk +++ b/config/dyn.mk @@ -67,7 +67,7 @@ ifneq ($(HB_HAS_ZLIB_LOCAL),) HB_DYN_LIBS += hbzlib endif ifeq ($(HB_PLATFORM),dos) - HB_DYN_LIBS += hbpmcom + HB_DYN_LIBS += hbdossrl endif # hbcplr \ diff --git a/config/lib.mk b/config/lib.mk index 502e03c217..66638a8e06 100644 --- a/config/lib.mk +++ b/config/lib.mk @@ -42,7 +42,7 @@ ifneq ($(HB_HAS_ZLIB_LOCAL),) HB_DYN_LIBS += hbzlib endif ifeq ($(HB_PLATFORM),dos) - HB_DYN_LIBS += hbpmcom + HB_DYN_LIBS += hbdossrl endif # Added only for hbpp diff --git a/src/3rd/Makefile b/src/3rd/Makefile index d5d50bd5cb..3c1b03e8d6 100644 --- a/src/3rd/Makefile +++ b/src/3rd/Makefile @@ -1,7 +1,7 @@ ROOT := ../../ DIRS := \ - hbpmcom \ + hbdossrl \ png \ jpeg \ pcre \ diff --git a/src/3rd/hbdossrl/Makefile b/src/3rd/hbdossrl/Makefile new file mode 100644 index 0000000000..17ad19ac9e --- /dev/null +++ b/src/3rd/hbdossrl/Makefile @@ -0,0 +1,29 @@ +ROOT := ../../../ + +include $(TOP)$(ROOT)config/global.mk + +LIBNAME := hbdosser + +#HB_BUILD_WARN := no +HB_BUILD_MODE := c + +C_SOURCES := \ + serial.c \ + +HB_BUILD_DOSSER := no + +ifeq ($(HB_PLATFORM),dos) + ifeq ($(HB_COMPILER),djgpp) + HB_BUILD_DOSSER := yes + endif + ifeq ($(HB_COMPILER),watcom) + HB_BUILD_DOSSER := yes + endif +endif + +ifeq ($(HB_BUILD_DOSSER),yes) + include $(TOP)$(ROOT)config/lib.mk +else + HB_SKIP_REASON := platform or compiler not supported + include $(TOP)$(ROOT)config/none.mk +endif diff --git a/src/3rd/hbdossrl/README b/src/3rd/hbdossrl/README new file mode 100644 index 0000000000..6129a6b57c --- /dev/null +++ b/src/3rd/hbdossrl/README @@ -0,0 +1,48 @@ +DOS Serial Library +================== + +A serial port (UART) library for DOS written back in the days when 16-bit +systems were still a going concern and far pointers were a thing. + +I'm putting it on Github because I still think it's way cool, and who knows... +Perhaps someone, somewhere, is in dire need of a serial library for DOS (I've +needed it from time to time to deal with some DOS based embedded piece of crap). + +Supports simultaneous communication over up to 4 serial ports. + +Have a look at term.c for an example of how to use it. + + + +How do I build it? +================== + +Good question! It was built to compile under Borland C++ 3 and whatever +Microsoft compiler happened to be current at the time. It conforms to ANSI C +(1989) and it doesn't do anything too crazy, so it shouldn't be too bad in a +more modern compiler. Have a go and let me know how it works! + + + +License +======= + +Copyright 1998 Karl Stenerud + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/3rd/hbdossrl/serial.c b/src/3rd/hbdossrl/serial.c new file mode 100644 index 0000000000..09e30b0124 --- /dev/null +++ b/src/3rd/hbdossrl/serial.c @@ -0,0 +1,1677 @@ +#include +#include +#include "serial.h" + +#if defined( __DJGPP__ ) +#include +#include +#include +#define Interrupt +#define Far +#define Farpeekw(s, o) _farpeekw((s),(o)) +#define Outp(a,b) (outp(a,b),(b)) +#define Outpw(a,w) (outpw(a,w),(w)) +#define CPU_DISABLE_INTERRUPTS() asm("CLI") +#define CPU_ENABLE_INTERRUPTS() asm("STI") +#define FP_ADDR(p) ((unsigned long)(p)) +#elif defined( _MSC_VER ) || defined( __WATCOMC__ ) +#define Interrupt __interrupt +#define Far __far +#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o))))) +#define Outp(a,b) outp(a,b) +#define Outpw(a,w) outpw(a,w) +#define CPU_DISABLE_INTERRUPTS() __asm CLI +#define CPU_ENABLE_INTERRUPTS() __asm STI +#else /* __BORLANDC__ */ +#define Interrupt interrupt +#define Far far +#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o))))) +#define Outp(a,b) outp(a,b) +#define Outpw(a,w) outpw(a,w) +#define CPU_DISABLE_INTERRUPTS() asm CLI +#define CPU_ENABLE_INTERRUPTS() asm STI +#endif /* _MSC_VER */ +#if defined( __386__ ) || defined( __DJGPP__ ) +#define PROTECTED_MODE +#endif + +/* ======================================================================== */ +/* =========================== DEFINES & MACROS =========================== */ +/* ======================================================================== */ + + +/* Tweak Values */ +#define SER_RX_BUFFER_SIZE_BITS 11 /* 2048 */ +#define SER_RX_BUFFER_HIGH_PERCENT 80 +#define SER_RX_BUFFER_LOW_PERCENT 20 + +#define SER_TX_BUFFER_SIZE_BITS 11 /* 2048 */ +#define SER_TX_BUFFER_HIGH_PERCENT 80 +#define SER_TX_BUFFER_LOW_PERCENT 20 + +#define UART_FIFO_DEFAULT_THRESHOLD 14 /* 14, 8, 4, 1, or 0 (off) */ + +#define COM1_DEFAULT_IRQ 4 +#define COM2_DEFAULT_IRQ 3 +#define COM3_DEFAULT_IRQ 4 +#define COM4_DEFAULT_IRQ 3 + +#define COM_MIN COM_1 +#define COM_MAX COM_4 + +#define IRQ_MIN 3 +#define IRQ_MAX 15 +#define IRQ_NONE 0xff + + +/* Receive and transmit buffers + * + * Both head and tail work in pre-increment mode: + * + * H->W->W->W + * * * * + * 0 1 2 3 4 5 ... + * * * * + * T->R->R->R + * + * Buffer empty: + * + * H + * ... 4 5 6 7 ... + * T + * + * Buffer full: + * + * H + * ... 4 5 6 7 ... + * T + */ + +#define SER_RX_BUFFER_SIZE (1L<rx_buff[(C)->rx_tail = ((C)->rx_tail+1) & SER_RX_BUFFER_SIZE_MASK] +#define SER_RX_BUFFER_WRITE(C, D) (C)->rx_buff[(C)->rx_head = ((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK] = D +#define SER_RX_BUFFER_INIT(C) (C)->rx_head = (C)->rx_tail = 0 +#define SER_RX_BUFFER_EMPTY(C) ((C)->rx_head == (C)->rx_tail) +#define SER_RX_BUFFER_FULL(C) ((((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK) == (C)->rx_tail) +#define SER_RX_BUFFER_CURRENT(C) (((C)->rx_head - (C)->rx_tail) & SER_RX_BUFFER_SIZE_MASK) +#define SER_RX_BUFFER_LOWATER(C) (SER_RX_BUFFER_CURRENT(C) < SER_RX_BUFFER_LOW) +#define SER_RX_BUFFER_HIWATER(C) (SER_RX_BUFFER_CURRENT(C) > SER_RX_BUFFER_HIGH) + +#define SER_TX_BUFFER_SIZE (1L<tx_buff[(C)->tx_tail = ((C)->tx_tail+1) & SER_TX_BUFFER_SIZE_MASK] +#define SER_TX_BUFFER_WRITE(C, D) (C)->tx_buff[(C)->tx_head = ((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK] = D +#define SER_TX_BUFFER_INIT(C) (C)->tx_head = (C)->tx_tail = 0 +#define SER_TX_BUFFER_EMPTY(C) ((C)->tx_head == (C)->tx_tail) +#define SER_TX_BUFFER_FULL(C) ((((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK) == (C)->tx_tail) +#define SER_TX_BUFFER_CURRENT(C) (((C)->tx_head - (C)->tx_tail) & SER_TX_BUFFER_SIZE_MASK) +#define SER_TX_BUFFER_LOWATER(C) (SER_TX_BUFFER_CURRENT(C) < SER_TX_BUFFER_LOW) +#define SER_TX_BUFFER_HIWATER(C) (SER_TX_BUFFER_CURRENT(C) > SER_TX_BUFFER_HIGH) + + +/* XON/XOFF Flow Control Commands */ +#define SER_XON 0x11 +#define SER_XOFF 0x13 + + +/* PIC Registers & interrupts */ +#define INTERRUPT_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) outp((P)|1, D) /* interrupt masks */ +#define PIC_WRITE_OCW2(P, D) outp(P, D) /* R, SL, EOI, 0, 0, L2, L1, L0 */ +#define PIC_WRITE_OCW3(P, D) outp(P, (D)|8) /* 0, ESMM, SMM, 0, 1, P, RR, RIS */ +#define PIC_READ_IMR(P) inp((P)|1) /* interrupt masks */ +#define PIC_END_INTERRUPT(P) PIC_WRITE_OCW2(P, PIC_EOI) +static unsigned char PIC_READ_IRR(unsigned int port){PIC_WRITE_OCW3(port, PIC_RR); return inp(port);} +#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 Registers */ +#define UART_RX_BUFFER 0 +#define UART_TX_BUFFER 0 +#define UART_INTERRUPT_ENABLE 1 + +#define UART_DIVISOR_LATCH_WORD 0 +#define UART_DIVISOR_LATCH_LSB 0 +#define UART_DIVISOR_LATCH_MSB 1 + +#define UART_INTERRUPT_IDENTIFY 2 +#define UART_FIFO_CONTROL 2 +#define UART_LINE_CONTROL 3 +#define UART_MODEM_CONTROL 4 +#define UART_LINE_STATUS 5 +#define UART_MODEM_STATUS 6 + + +/* UART Read commands (B = UART Base Address ) */ +#define UART_READ_DATA(C) inp((C)->base+UART_RX_BUFFER) +#define UART_READ_INTERRUPT_ENABLE(C) inp((C)->base+UART_INTERRUPT_ENABLE) +#define UART_READ_INTERRUPT_IDENTIFY(C) inp((C)->base+UART_INTERRUPT_IDENTIFY) +#define UART_READ_LINE_CONTROL(C) inp((C)->base+UART_LINE_CONTROL) +#define UART_READ_MODEM_CONTROL(C) inp((C)->base+UART_MODEM_CONTROL) +#define UART_READ_LINE_STATUS(C) ((C)->lsr = inp((C)->base+UART_LINE_STATUS)) +#define UART_READ_MODEM_STATUS(C) ((C)->msr = inp((C)->base+UART_MODEM_STATUS)) +#define UART_READ_BPS(C) ((Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH) & 0) | \ + inpw((C)->base+UART_DIVISOR_LATCH_WORD) | \ + (Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH) & 0)) + + +/* UART Write Commands (B = UART Base Address , D = Data ) */ +#define UART_WRITE_DATA(C, D) outp((C)->base+UART_TX_BUFFER, D) +#define UART_WRITE_INTERRUPT_ENABLE(C, D) ((C)->ier = Outp((C)->base+UART_INTERRUPT_ENABLE, D)) +#define UART_WRITE_FIFO_CONTROL(C, D) ((C)->fcr = Outp((C)->base+UART_FIFO_CONTROL, D)) +#define UART_WRITE_LINE_CONTROL(C, D) ((C)->lcr = Outp((C)->base+UART_LINE_CONTROL, D)) +#define UART_WRITE_MODEM_CONTROL(C, D) ((C)->mcr = Outp((C)->base+UART_MODEM_CONTROL, D)) +#define UART_WRITE_BPS(C, D) {outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH); \ + (C)->dlatch = Outpw((C)->base+UART_DIVISOR_LATCH_WORD, D); \ + outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH);} + + +/* Interrupt Enable Register Components */ +#define UART_IER_MODEM_STATUS 0x08 +#define UART_IER_ERRORS 0x04 +#define UART_IER_TX_HOLD_EMPTY 0x02 +#define UART_IER_DATA_READY 0x01 + + +/* Interrupt Identify Register Components */ +#define UART_IIR_FIFO_ENABLE_1 0x80 +#define UART_IIR_FIFO_ENABLE_0 0x40 +#define UART_IIR_IDENTIFY_2 0x08 +#define UART_IIR_IDENTIFY_1 0x04 +#define UART_IIR_IDENTIFY_0 0x02 +#define UART_IIR_NO_INTERRUPT_PENDING 0x01 + +#define UART_IIR_MASK (UART_IIR_NO_INTERRUPT_PENDING | UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1) +#define UART_IIR_LINE_STATUS (UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1) +#define UART_IIR_DATA_READY UART_IIR_IDENTIFY_1 +#define UART_IIR_TX_HOLD_EMPTY UART_IIR_IDENTIFY_0 +#define UART_IIR_MODEM_STATUS 0x00 +#define UART_IIR_NO_INTERRUPT UART_IIR_NO_INTERRUPT_PENDING + + +/* Fifo Control Register Components */ +#define UART_FCR_TRIGGER_1 0x80 +#define UART_FCR_TRIGGER_0 0x40 +#define UART_FCR_DMA_SELECT 0x08 +#define UART_FCR_TX_FIFO_RESET 0x04 +#define UART_FCR_RX_FIFO_RESET 0x02 +#define UART_FCR_FIFO_ENABLE 0x01 + +#define UART_FCR_TRIGGER_AT_1 0x00 +#define UART_FCR_TRIGGER_AT_4 UART_FCR_TRIGGER_0 +#define UART_FCR_TRIGGER_AT_8 UART_FCR_TRIGGER_1 +#define UART_FCR_TRIGGER_AT_14 (UART_FCR_TRIGGER_0 | UART_FCR_TRIGGER_1) + + +/* Line Control Register Components */ + +#define UART_LCR_DIVISOR_LATCH 0x80 +#define UART_LCR_SET_BREAK 0x40 +#define UART_LCR_STICK_PARITY 0x20 +#define UART_LCR_EVEN_PARITY 0x10 +#define UART_LCR_PARITY_ENABLE 0x08 +#define UART_LCR_STOPBITS 0x04 +#define UART_LCR_WORD_LENGTH_1 0x02 +#define UART_LCR_WORD_LENGTH_0 0x01 + +#define UART_DATA_5 0x00 +#define UART_DATA_6 UART_LCR_WORD_LENGTH_0 +#define UART_DATA_7 UART_LCR_WORD_LENGTH_1 +#define UART_DATA_8 (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1) +#define UART_DATA_MASK (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1) + +#define UART_PARITY_NONE 0x00 +#define UART_PARITY_ODD UART_LCR_PARITY_ENABLE +#define UART_PARITY_EVEN (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY) +#define UART_PARITY_MARK (UART_LCR_PARITY_ENABLE | UART_LCR_STICK_PARITY) +#define UART_PARITY_SPACE (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY) +#define UART_PARITY_MASK (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY) + +#define UART_STOP_1 0x00 +#define UART_STOP_2 UART_LCR_STOPBITS +#define UART_STOP_MASK UART_LCR_STOPBITS + + +/* Modem Control Register Components */ +#define UART_MCR_LOOPBACK 0x10 +#define UART_MCR_OUT2 0x08 +#define UART_MCR_OUT1 0x04 +#define UART_MCR_RTS 0x02 +#define UART_MCR_DTR 0x01 +#define UART_MCR_MASK ( UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2 | UART_MCR_LOOPBACK ) + + +/* Line Status Register Components */ +#define UART_LSR_FIFO_ERROR 0x80 +#define UART_LSR_TX_EMPTY 0x40 +#define UART_LSR_TX_HOLD_EMPTY 0x20 +#define UART_LSR_BREAK_INDICATOR 0x10 +#define UART_LSR_FRAME_ERROR 0x08 +#define UART_LSR_PARITY_ERROR 0x04 +#define UART_LSR_OVERRUN_ERROR 0x02 +#define UART_LSR_DATA_READY 0x01 + + +/* Modem Status Register Components */ +#define UART_MSR_DCD 0x80 +#define UART_MSR_RI 0x40 +#define UART_MSR_DSR 0x20 +#define UART_MSR_CTS 0x10 +#define UART_MSR_DLT_DCD 0x08 +#define UART_MSR_NEW_RING 0x04 +#define UART_MSR_DLT_DSR 0x02 +#define UART_MSR_DLT_CTS 0x01 + + +/* BPS Rates (based on 1.8432 MHz crystal) */ +#define UART_BPS_DIVISOR_50 2304 +#define UART_BPS_DIVISOR_75 1536 +#define UART_BPS_DIVISOR_110 1047 /* 0.026% error */ +#define UART_BPS_DIVISOR_134_5 857 /* 0.058% error */ +#define UART_BPS_DIVISOR_150 768 +#define UART_BPS_DIVISOR_300 384 +#define UART_BPS_DIVISOR_600 192 +#define UART_BPS_DIVISOR_1200 96 +#define UART_BPS_DIVISOR_1800 64 +#define UART_BPS_DIVISOR_2400 48 +#define UART_BPS_DIVISOR_3800 32 +#define UART_BPS_DIVISOR_4800 24 +#define UART_BPS_DIVISOR_7200 16 +#define UART_BPS_DIVISOR_9600 12 +#define UART_BPS_DIVISOR_19200 6 +#define UART_BPS_DIVISOR_38400 3 +#define UART_BPS_DIVISOR_57600 2 +#define UART_BPS_DIVISOR_115200 1 + + +/* ======================================================================== */ +/* ==================== PROTOTYPES, TYPEDEFS & GLOBALS ==================== */ +/* ======================================================================== */ + + +typedef struct +{ + unsigned char port; + unsigned char default_irq; + unsigned char irq; + unsigned char open; + unsigned char ier; + unsigned char fcr; + unsigned char lcr; + unsigned char mcr; + unsigned char lsr; + unsigned char msr; + unsigned char flow_mode; + unsigned char rx_flow_on; + unsigned char tx_flow_on; + unsigned char rx_buff[(unsigned short)SER_RX_BUFFER_SIZE]; + unsigned char tx_buff[(unsigned short)SER_TX_BUFFER_SIZE]; + unsigned int base; + unsigned int dlatch; + unsigned int rx_head; + unsigned int tx_head; + unsigned int rx_tail; + unsigned int tx_tail; +} serial_struct; + + +#if defined( __DJGPP__ ) +typedef _go32_dpmi_seginfo int_handler_ptr; +static _go32_dpmi_seginfo g_old_isrs[16]; +#else +typedef void Interrupt Far (*int_handler_ptr)(void); +static int_handler_ptr g_old_isrs[16]; +#endif +static unsigned char g_isrs_taken[16] = {0}; +#ifdef PROTECTED_MODE +static int_handler_ptr g_isr_addr; +static unsigned int g_isrs_count = 0; +#endif + + +/* serial port data */ +static serial_struct g_comports[COM_MAX+1] = +{ +#if defined( __GNUC__ ) + {port: 0, default_irq: COM1_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 1, default_irq: COM2_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 2, default_irq: COM3_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 3, default_irq: COM4_DEFAULT_IRQ, irq: IRQ_NONE} +#else + {0, COM1_DEFAULT_IRQ, IRQ_NONE}, + {1, COM2_DEFAULT_IRQ, IRQ_NONE}, + {2, COM3_DEFAULT_IRQ, IRQ_NONE}, + {3, COM4_DEFAULT_IRQ, IRQ_NONE} +#endif +}; + + + +/* ======================================================================== */ +/* ====================== INTERRUPT SERVICE ROUTINE ======================= */ +/* ======================================================================== */ + +static void Interrupt com_general_isr(void) +{ + serial_struct* com_min = (serial_struct*)(g_comports); + serial_struct* com_max = com_min + COM_MAX; + unsigned char int_id; + unsigned char data; + unsigned char slave_interrupted = 0; + serial_struct* com; + + /* Disable IRQs for all COM ports, then re-enable interrupts. + * This allows other faster devices to be serviced. + */ + for(com=com_min;com<=com_max;com++) + { + if(com->open) + { + if(com->irq > 7) + slave_interrupted = 1; + PIC_DISABLE_IRQ(com->irq); + } + } + CPU_ENABLE_INTERRUPTS(); + + /* Process all pending interrupts */ + for(com=com_min;com<=com_max;com++) + { + if(com->open) + { + while((int_id=UART_READ_INTERRUPT_IDENTIFY(com) & UART_IIR_MASK) != UART_IIR_NO_INTERRUPT) + { + switch(int_id) + { + case UART_IIR_DATA_READY: + /* Read all data from the UART */ + while(UART_READ_LINE_STATUS(com) & UART_LSR_DATA_READY) + { + data = UART_READ_DATA(com); + + /* Handle XON/XOFF flow control (TX) */ + if(com->flow_mode == SER_HANDSHAKING_XONXOFF && (data == SER_XOFF || data == SER_XON)) + { + com->tx_flow_on = data == SER_XON; + if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + } + + /* Store it if there's room, or throw it out */ + else if(!SER_RX_BUFFER_FULL(com)) + { + SER_RX_BUFFER_WRITE(com, data); + + /* Flow control (RX) - Turn off if buffer almost full */ + if(com->rx_flow_on && SER_RX_BUFFER_HIWATER(com)) + { + com->rx_flow_on = 0; + + switch(com->flow_mode) + { + case SER_HANDSHAKING_RTSCTS: + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_RTS); + break; + case SER_HANDSHAKING_NONE: + break; + case SER_HANDSHAKING_XONXOFF: + /* Wait until tx hold register is empty */ + while(!(UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY)) + {} + break; + case SER_HANDSHAKING_DTRDSR: + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_DTR); + break; + } + } + } + } + break; + /* Change in line status */ + case UART_IIR_LINE_STATUS: + UART_READ_LINE_STATUS(com); + break; + /* Change in modem status */ + case UART_IIR_MODEM_STATUS: + UART_READ_MODEM_STATUS(com); + + /* Handle RTS/CTS or DSR/DTR flow control (TX) */ + if(com->flow_mode == SER_HANDSHAKING_RTSCTS) + com->tx_flow_on = (com->msr & UART_MSR_CTS) != 0; + else if(com->flow_mode == SER_HANDSHAKING_DTRDSR) + com->tx_flow_on = (com->msr & UART_MSR_DSR) != 0; + if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + break; + /* UART is empty */ + case UART_IIR_TX_HOLD_EMPTY: + while(com->tx_flow_on && UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY && !SER_TX_BUFFER_EMPTY(com)) + UART_WRITE_DATA(com, SER_TX_BUFFER_READ(com)); + if(SER_TX_BUFFER_EMPTY(com) || !com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) & ~UART_IER_TX_HOLD_EMPTY); + break; + } + } + } + } + + CPU_DISABLE_INTERRUPTS(); + + /* End the interrupt on the PIC */ + if(slave_interrupted) + PIC_END_INTERRUPT(PIC_SLAVE); + PIC_END_INTERRUPT(PIC_MASTER); + + /* Re-enable all interrupts */ + for(com=com_min;com<=com_max;com++) + if(com->open) + PIC_ENABLE_IRQ(com->irq); + + /* We must explicitely call 'sti' before 'iret' because 'iret' + won't always restore interrupts in a virtual environment */ + CPU_ENABLE_INTERRUPTS(); +} + + + +/* ======================================================================== */ +/* =========================== UTILITY ROUTINES =========================== */ +/* ======================================================================== */ + + +#ifdef PROTECTED_MODE + +/* we do not know the exact size of com_general_isr() function + but for sure it's not longer then 2048 bytes */ +#define ISR_SIZE 2048 + +#if defined( __DJGPP__ ) + +static void serial_dpmi_get_pvect(int vector, _go32_dpmi_seginfo *info) +{ + _go32_dpmi_get_protected_mode_interrupt_vector(vector, info); +} + +static void serial_dpmi_set_pvect(int vector, _go32_dpmi_seginfo *info) +{ + _go32_dpmi_set_protected_mode_interrupt_vector(vector, info); +} + +static int serial_dpmi_lock_memory(void) +{ + unsigned long dataaddr, codeaddr; + __dpmi_meminfo dataregion, 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 + FP_ADDR(com_general_isr); + dataregion.handle = 0; + dataregion.size = sizeof(g_comports); + dataregion.address = codeaddr + FP_ADDR(g_comports); + if(__dpmi_lock_linear_region(&coderegion) == 0) + { + if(__dpmi_lock_linear_region(&dataregion) == 0) + { + g_isr_addr.pm_offset = FP_ADDR(com_general_isr); + g_isr_addr.pm_selector = _go32_my_cs(); + if(_go32_dpmi_allocate_iret_wrapper(&g_isr_addr) == 0) + return SER_SUCCESS; + __dpmi_unlock_linear_region(&dataregion); + } + __dpmi_unlock_linear_region(&coderegion); + } + } + return SER_ERR_LOCK_MEM; +} + +static int serial_dpmi_unlock_memory(void) +{ + int rc = SER_SUCCESS; + unsigned long baseaddr; + __dpmi_meminfo region; + + if(__dpmi_get_segment_base_address(_my_ds(), &baseaddr) == 0) + { + region.handle = 0; + region.size = sizeof(g_comports); + region.address = baseaddr + FP_ADDR(g_comports); + if(__dpmi_unlock_linear_region(®ion) != 0) + rc = SER_ERR_UNLOCK_MEM; + } + else + rc = SER_ERR_UNLOCK_MEM; + if(__dpmi_get_segment_base_address(_my_cs(), &baseaddr) == 0) + { + region.handle = 0; + region.size = ISR_SIZE; + region.address = baseaddr + FP_ADDR(com_general_isr); + if(__dpmi_unlock_linear_region(®ion) != 0) + rc = SER_ERR_UNLOCK_MEM; + } + else + rc = SER_ERR_UNLOCK_MEM; + if(_go32_dpmi_free_iret_wrapper(&g_isr_addr) != 0) + rc = SER_ERR_UNLOCK_MEM; + return rc; +} + +#else /* ! __DJGPP__ */ + +static void serial_dpmi_get_pvect(int vect, int_handler_ptr *handler) +{ + union REGS r; + unsigned short sel; + unsigned long off; + + /* DPMI get protected mode interrupt vector: Int 31H, Fn 0204H */ + r.x.eax = 0x0204; + r.x.ebx = vect; + int386(0x31, &r, &r); + sel = (unsigned short) r.x.ecx; + off = r.x.edx; + + *handler=(int_handler_ptr) MK_FP(sel, off); +} + +static void serial_dpmi_set_pvect(int vect, int_handler_ptr *handler) +{ + union REGS r; + void Far *ptr; + + /* DPMI set protected mode interrupt vector: Int 31H, Fn 0205H */ + ptr = (void Far *)*handler; + r.x.eax = 0x0205; + r.x.ebx = vect; + r.x.ecx = FP_SEG(ptr); + r.x.edx = FP_OFF(ptr); + int386(0x31, &r, &r); +} + +static int serial_dpmi_lock_linear_memory(void Far *ptr, unsigned long size) +{ + union REGS r; + + /* DPMI get segment base address: Int 31H, Fn 0006H */ + r.x.eax = 0x0006; + r.x.ebx = FP_SEG(ptr); + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx); + + /* DPMI lock linear region: Int 31H, Fn 0600H */ + r.x.eax = 0x0600; + r.x.ebx = addr >> 16; + r.x.ecx = addr & 0xFFFF; + r.x.esi = size >> 16; + r.x.edi = size & 0xFFFF; + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + g_isr_addr = com_general_isr; + return SER_SUCCESS; + } + } + return SER_ERR_LOCK_MEM; +} + +static int serial_dpmi_unlock_linear_memory(void Far *ptr, unsigned long size) +{ + union REGS r; + + /* DPMI get segment base address: Int 31H, Fn 0006H */ + r.x.eax = 0x0006; + r.x.ebx = FP_SEG(ptr); + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx); + + /* DPMI unlock linear region: Int 31H, Fn 0601H */ + r.x.eax = 0x0601; + r.x.ebx = addr >> 16; + r.x.ecx = addr & 0xFFFF; + r.x.esi = size >> 16; + r.x.edi = size & 0xFFFF; + int386(0x31, &r, &r); + if(r.w.cflag == 0) + return SER_SUCCESS; + } + return SER_ERR_UNLOCK_MEM; +} + +static int serial_dpmi_lock_memory(void) +{ + int rc; + + if((rc=serial_dpmi_lock_linear_memory(com_general_isr, ISR_SIZE)) == SER_SUCCESS) + { + if((rc=serial_dpmi_lock_linear_memory(g_comports, sizeof(g_comports))) != SER_SUCCESS) + serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE); + } + return rc; +} + +static int serial_dpmi_unlock_memory(void) +{ + int rc1 = serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE), + rc2 = serial_dpmi_unlock_linear_memory(g_comports, sizeof(g_comports)); + + return rc1 != SER_SUCCESS ? rc1 : rc2; +} + +#endif /* ! __DJGPP__ */ + +static int serial_install_irqhandler(int irq) +{ + /* If we haven't taken this IRQ's ISR already, take it */ + if(!g_isrs_taken[irq]) + { + if( g_isrs_count++ == 0 ) + { + int rc; + + /* lock memory used by interrupt handler in DPMI mode */ + if((rc=serial_dpmi_lock_memory()) != SER_SUCCESS) + { + --g_isrs_count; + return rc; + } + } + serial_dpmi_get_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]); + serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_isr_addr); + g_isrs_taken[irq] = 1; + } + return SER_SUCCESS; +} + +static int serial_remove_irqhandler(int irq) +{ + int rc = SER_SUCCESS; + + if(g_isrs_taken[irq]) + { + serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]); + g_isrs_taken[irq] = 0; + + if( --g_isrs_count == 0 ) + { + /* unlock memory used by interrupt handler in DPMI mode */ + rc = serial_dpmi_unlock_memory(); + } + } + return rc; +} + +#else /* ! PROTECTED_MODE */ + +static int serial_install_irqhandler(int irq) +{ + /* If we haven't taken this IRQ's ISR already, take it */ + if(!g_isrs_taken[irq]) + { + g_old_isrs[irq] = _dos_getvect(irq+INTERRUPT_VECTOR_OFFSET); + _dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, com_general_isr); + g_isrs_taken[irq] = 1; + } + return SER_SUCCESS; +} + +static int serial_remove_irqhandler(int irq) +{ + _dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, g_old_isrs[irq]); + g_isrs_taken[irq] = 0; + + return SER_SUCCESS; +} + +#endif /* ! PROTECTED_MODE */ + + +static int serial_find_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + unsigned char imr_m = PIC_READ_IMR(PIC_MASTER); /* Interrupt Mask Registers */ + unsigned char imr_s = PIC_READ_IMR(PIC_SLAVE); + unsigned char irr_m; /* Interrupt Request Registers */ + unsigned char irr_s; + + /* Set up the UART */ + UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2); + UART_WRITE_FIFO_CONTROL(com, 0); + + /* Wait until tx hold register is empty */ + while((UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY) == 0) + {} + + CPU_DISABLE_INTERRUPTS(); + + /* Allow any interrupt on PIC */ + PIC_WRITE_IMR(PIC_MASTER, 0); + PIC_WRITE_IMR(PIC_SLAVE, 0); + + /* Do some initial polls to let things settle (win95 needs this) */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + PIC_READ_IRR(PIC_MASTER); + PIC_READ_IRR(PIC_SLAVE); + UART_WRITE_INTERRUPT_ENABLE(com, 0); + PIC_READ_IRR(PIC_MASTER); + PIC_READ_IRR(PIC_SLAVE); + + /* Generate an interrupt and record all active IRQs */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + irr_m = PIC_READ_IRR(PIC_MASTER); + irr_s = PIC_READ_IRR(PIC_SLAVE); + + /* Remove the interrupt and mask out all IRQs still active */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + irr_m &= ~PIC_READ_IRR(PIC_MASTER); + irr_s &= ~PIC_READ_IRR(PIC_SLAVE); + + /* Interrupt again to make sure */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + irr_m &= PIC_READ_IRR(PIC_MASTER); + irr_s &= PIC_READ_IRR(PIC_SLAVE); + + /* Return everything to normal */ + PIC_WRITE_IMR(PIC_MASTER, imr_m); + PIC_WRITE_IMR(PIC_SLAVE, imr_s); + UART_WRITE_INTERRUPT_ENABLE(com, 0); + + CPU_ENABLE_INTERRUPTS(); + + switch(irr_m) + { + case 0x01: return 0; + case 0x02: return 1; + case 0x04: + switch(irr_s) + { + 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 SER_ERR_IRQ_NOT_FOUND; +} + +static int serial_free_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + serial_struct* com_min = (serial_struct*)(g_comports); + serial_struct* com_max = com_min + COM_MAX; + unsigned int irq = com->irq; + serial_struct* ptr; + + if(irq == IRQ_NONE) + return SER_SUCCESS; + + CPU_DISABLE_INTERRUPTS(); + + /* Disable interrupts from the UART */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2); + /* Clear the FIFO and rx registers */ + UART_WRITE_FIFO_CONTROL(com, 0); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_LINE_STATUS(com); + UART_READ_DATA(com); + /* Clear any other possible interrupt causes */ + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_MODEM_STATUS(com); + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_LINE_STATUS(com); + UART_READ_INTERRUPT_IDENTIFY(com); + + com->irq = IRQ_NONE; + + CPU_ENABLE_INTERRUPTS(); + + for(ptr=com_min;ptr<=com_max;ptr++) + if(ptr != com && ptr->irq == irq) + return SER_SUCCESS; + + /* Disable interrupts from the PIC and restore the old vector */ + PIC_DISABLE_IRQ(irq); + + return serial_remove_irqhandler(irq); +} + + +/* ======================================================================== */ +/* ======================== BASIC SERIAL ROUTINES ========================= */ +/* ======================================================================== */ + +int serial_open(int comport, long bps, int data_bits, char parity, int stop_bits, int handshaking) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int rc = SER_SUCCESS; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->open) + return SER_ERR_ALREADY_OPEN; + + com->open = 1; + + /* Initialize buffers */ + com->rx_flow_on = 1; + com->tx_flow_on = 1; + SER_RX_BUFFER_INIT(com); + SER_TX_BUFFER_INIT(com); + + /* look in bios tables (0040:0000 - 0040:0006) for com base addresses */ + if(serial_set_base(comport, Farpeekw(0x0040, comport<<1)) != SER_SUCCESS) + return SER_ERR_NO_UART; + + /* Turn off interrupts from UART */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + + /* Auto-detect IRQ if we can */ + if((rc=serial_find_irq(comport)) < 0) + rc = com->default_irq; + + if((rc=serial_set_irq(comport, rc)) != SER_SUCCESS) + return rc; + + /* Turn off interrupts from PIC */ + PIC_DISABLE_IRQ(com->irq); + + /* Set the comport */ + if((rc=serial_set(comport, bps, data_bits, parity, stop_bits, handshaking)) == SER_SUCCESS) + { + UART_WRITE_MODEM_CONTROL(com, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2); + rc = serial_set_fifo_threshold(comport, UART_FIFO_DEFAULT_THRESHOLD); + + /* Get some info */ + UART_READ_LINE_STATUS(com); + UART_READ_MODEM_STATUS(com); + } + + /* Re-enable interrupts */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_DATA_READY | UART_IER_MODEM_STATUS | UART_IER_ERRORS); + PIC_ENABLE_IRQ(com->irq); + + if(rc != SER_SUCCESS) /* We failed to set the comport */ + serial_close(comport); + + return rc; +} + +int serial_close(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int rc; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + /* Restore old interrupts */ + rc = serial_free_irq(comport); + + /* Turn everything off */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + UART_WRITE_MODEM_CONTROL(com, 0); + serial_set_fifo_threshold(comport, 0); + + com->open = 0; + + return rc; +} + +int serial_read(int comport, char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + CPU_DISABLE_INTERRUPTS(); + /* Fill in the array given to us */ + for(i=0;!SER_RX_BUFFER_EMPTY(com) && i < len;i++) + data[i] = SER_RX_BUFFER_READ(com); + + /* Flow control (RX) - Turn on if buffer almost empty */ + if(!com->rx_flow_on && SER_RX_BUFFER_LOWATER(com)) + { + com->rx_flow_on = 1; + if(com->flow_mode == SER_HANDSHAKING_XONXOFF) + { + /* Wait until tx hold register is empty */ + while(!(UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY)) + {} + UART_WRITE_DATA(com, SER_XON); + } + else if(com->flow_mode == SER_HANDSHAKING_RTSCTS) + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_RTS); + else if(com->flow_mode == SER_HANDSHAKING_DTRDSR) + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_DTR); + } + CPU_ENABLE_INTERRUPTS(); + + return i; +} + + +int serial_write(int comport, const char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + for(i=0;i < len;i++) + { + /* Wait until we can write */ + while(!(UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY)) + {} + + /* Write 1 char */ + if(com->tx_flow_on) + UART_WRITE_DATA(com, data[i]); + else + break; + } + return i; +} + +int serial_write_buffered(int comport, const char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + CPU_DISABLE_INTERRUPTS(); + for(i=0;i < len ;i++) + { + /* stop when sent buffer is full */ + if(SER_TX_BUFFER_FULL(com)) + break; + /* Write 1 char */ + SER_TX_BUFFER_WRITE(com, data[i]); + } + + /* If there's data to send, enable TX_HOLD_EMPTY interrupt */ + if(!SER_TX_BUFFER_EMPTY(com)) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + CPU_ENABLE_INTERRUPTS(); + + return i; +} + + +int serial_set(int comport, long bps, int data_bits, char parity, int stop_bits, int handshaking) +{ + int rc; + + if((rc=serial_set_bps(comport, bps)) == SER_SUCCESS) + if((rc=serial_set_data(comport, data_bits)) == SER_SUCCESS) + if((rc=serial_set_parity(comport, parity)) == SER_SUCCESS) + if((rc=serial_set_stop(comport, stop_bits)) == SER_SUCCESS) + return serial_set_handshaking(comport, handshaking); + return rc; +} + + +/* ======================================================================== */ +/* ========================== SETTINGS ROUTINES =========================== */ +/* ======================================================================== */ + + +int serial_set_base(int comport, int base) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(base < 1 || base > 0xfff) + return SER_ERR_INVALID_BASE; + + com->base = base; + return SER_SUCCESS; +} + +int serial_set_irq(int comport, int irq) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int rc; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(irq < IRQ_MIN || irq > IRQ_MAX) + return SER_ERR_INVALID_IRQ; + if(!com->open) + return SER_ERR_NOT_OPEN; + + /* Remove any ISRs on this com port's current IRQ */ + serial_free_irq(comport); + + if((rc=serial_install_irqhandler(irq)) == SER_SUCCESS) + { + com->irq = irq; + PIC_ENABLE_IRQ(com->irq); + } + return rc; +} + +int serial_set_fifo_threshold(int comport, int threshold) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(threshold) + { + case 14: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_14); + break; + case 8: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_8); + break; + case 4: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_4); + break; + case 1: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_1); + break; + case 0: + UART_WRITE_FIFO_CONTROL(com, 0); + break; + default: + return SER_ERR_INVALID_FIFO_THRESHOLD; + } + + /* Clear out any garbage in the UART interrupts and FIFO */ + for(i=0;i<16;i++) + UART_READ_DATA(com); + UART_READ_LINE_STATUS(com); + + return SER_SUCCESS; +} + + +int serial_set_bps(int comport, long bps) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(bps) + { + case 115200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_115200); + return SER_SUCCESS; + case 57600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_57600); + return SER_SUCCESS; + case 38400L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_38400); + return SER_SUCCESS; + case 19200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_19200); + return SER_SUCCESS; + case 9600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_9600); + return SER_SUCCESS; + case 7200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_7200); + return SER_SUCCESS; + case 4800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_4800); + return SER_SUCCESS; + case 3800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_3800); + return SER_SUCCESS; + case 1800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_1800); + return SER_SUCCESS; + case 2400L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_2400); + return SER_SUCCESS; + case 1200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_1200); + return SER_SUCCESS; + case 600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_600); + return SER_SUCCESS; + case 300L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_300); + return SER_SUCCESS; + case 150L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_150); + return SER_SUCCESS; + case 110L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_110); + return SER_SUCCESS; + case 75L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_75); + return SER_SUCCESS; + case 50L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_50); + return SER_SUCCESS; + } + return SER_ERR_INVALID_BPS; +} + + +int serial_set_data(int comport, int data_bits) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(data_bits) + { + case 5: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_5); + return SER_SUCCESS; + case 6: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_6); + return SER_SUCCESS; + case 7: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_7); + return SER_SUCCESS; + case 8: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_8); + return SER_SUCCESS; + } + return SER_ERR_INVALID_DATA_BITS; +} + + +int serial_set_parity(int comport, char parity) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(parity) + { + case 'n': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_NONE); + return SER_SUCCESS; + case 'e': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_EVEN); + return SER_SUCCESS; + case 'o': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_ODD); + return SER_SUCCESS; + case 'm': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_MARK); + return SER_SUCCESS; + case 's': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_SPACE); + return SER_SUCCESS; + } + return SER_ERR_INVALID_PARITY; +} + + +int serial_set_stop(int comport, int stop_bits) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(stop_bits) + { + case 1: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_STOP_MASK) | UART_STOP_1); + return SER_SUCCESS; + case 2: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_STOP_MASK) | UART_STOP_2); + return SER_SUCCESS; + } + return SER_ERR_INVALID_STOP_BITS; +} + + +int serial_set_handshaking(int comport, int handshaking) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + com->tx_flow_on = 1; + com->rx_flow_on = 1; + + if(handshaking < SER_HANDSHAKING_NONE || handshaking > SER_HANDSHAKING_DTRDSR) + return SER_ERR_INVALID_HANDSHAKING; + + com->flow_mode = handshaking; + return SER_SUCCESS; +} + + +int serial_set_rts(int comport, int rts) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + if(rts) + UART_WRITE_MODEM_CONTROL(com, com->mcr | UART_MCR_RTS); + else + UART_WRITE_MODEM_CONTROL(com, com->mcr & ~UART_MCR_RTS); + + return SER_SUCCESS; +} + + +int serial_set_dtr(int comport, int dtr) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + if(dtr) + UART_WRITE_MODEM_CONTROL(com, com->mcr | UART_MCR_DTR); + else + UART_WRITE_MODEM_CONTROL(com, com->mcr & ~UART_MCR_DTR); + + return SER_SUCCESS; +} + + +int serial_set_mcr(int comport, int mcr) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + UART_WRITE_MODEM_CONTROL(com, mcr & UART_MCR_MASK ); + + return SER_SUCCESS; +} + + + +int serial_get_base(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->base < 1 || com->base > 0xfff) + return SER_ERR_INVALID_BASE; + + return com->base; +} + +int serial_get_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->irq < IRQ_MIN || com->irq > IRQ_MAX) + return SER_ERR_INVALID_IRQ; + + return com->irq; +} + +long serial_get_bps(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_BPS(com)) + { + case UART_BPS_DIVISOR_115200: + return 115200L; + case UART_BPS_DIVISOR_57600: + return 57600L; + case UART_BPS_DIVISOR_38400: + return 38400L; + case UART_BPS_DIVISOR_19200: + return 19200L; + case UART_BPS_DIVISOR_9600: + return 9600L; + case UART_BPS_DIVISOR_7200: + return 7200L; + case UART_BPS_DIVISOR_4800: + return 4800L; + case UART_BPS_DIVISOR_3800: + return 3800L; + case UART_BPS_DIVISOR_2400: + return 2400L; + case UART_BPS_DIVISOR_1800: + return 1800L; + case UART_BPS_DIVISOR_1200: + return 1200L; + case UART_BPS_DIVISOR_600: + return 600L; + case UART_BPS_DIVISOR_300: + return 300L; + case UART_BPS_DIVISOR_150: + return 150L; + case UART_BPS_DIVISOR_110: + return 110L; + case UART_BPS_DIVISOR_75: + return 75L; + case UART_BPS_DIVISOR_50: + return 50L; + } + return SER_ERR_INVALID_BPS; +} + + +int serial_get_data(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_DATA_MASK) + { + case UART_DATA_5: + return 5; + case UART_DATA_6: + return 6; + case UART_DATA_7: + return 7; + case UART_DATA_8: + return 8; + } + return SER_ERR_INVALID_DATA_BITS; +} + + +char serial_get_parity(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_PARITY_MASK) + { + case UART_PARITY_NONE: + return 'n'; + case UART_PARITY_EVEN: + return 'e'; + case UART_PARITY_ODD: + return 'o'; + case UART_PARITY_MARK: + return 'm'; + case UART_PARITY_SPACE: + return 's'; + } + return SER_ERR_INVALID_PARITY; +} + + +int serial_get_stop(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_STOP_MASK) + { + case UART_STOP_1: + return 1; + case UART_STOP_2: + return 2; + } + return SER_ERR_INVALID_STOP_BITS; +} + + +int serial_get_handshaking(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->flow_mode; +} + + +int serial_get_rts(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->mcr & UART_MCR_RTS) != 0; +} + + +int serial_get_dtr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->mcr & UART_MCR_DTR) != 0; +} + + +int serial_get_mcr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->mcr; +} + + +int serial_get_dsr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->msr & UART_MSR_DSR) != 0; +} + + +int serial_get_cts(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->msr & UART_MSR_CTS) != 0; +} + + +int serial_get_msr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->msr; +} + + +int serial_get_lsr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->lsr; +} + + + +int serial_get_tx_buffered(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int count; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + count = SER_TX_BUFFER_CURRENT(com); + CPU_ENABLE_INTERRUPTS(); + + return count; +} + + +int serial_get_rx_buffered(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int count; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + count = SER_TX_BUFFER_CURRENT(com); + CPU_ENABLE_INTERRUPTS(); + + return count; +} + + +int serial_clear_tx_buffer(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + SER_TX_BUFFER_INIT(com); + CPU_ENABLE_INTERRUPTS(); + + return SER_SUCCESS; +} + + +int serial_clear_rx_buffer(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + SER_RX_BUFFER_INIT(com); + CPU_ENABLE_INTERRUPTS(); + + return SER_SUCCESS; +} diff --git a/src/3rd/hbdossrl/serial.h b/src/3rd/hbdossrl/serial.h new file mode 100644 index 0000000000..ea096f10bb --- /dev/null +++ b/src/3rd/hbdossrl/serial.h @@ -0,0 +1,196 @@ +/* Serial Library 1.4 (22-Jun-2000) (c) 1998 Karl Stenerud + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SERIAL__HEADER +#define SERIAL__HEADER + +/* COM Ports */ +#define COM_1 0 +#define COM_2 1 +#define COM_3 2 +#define COM_4 3 + +/* Handshaking Modes */ +#define SER_HANDSHAKING_NONE 0 +#define SER_HANDSHAKING_XONXOFF 1 +#define SER_HANDSHAKING_RTSCTS 2 +#define SER_HANDSHAKING_DTRDSR 3 + +/* Error Codes */ +#define SER_SUCCESS 0 /* Function completed successfully */ +#define SER_ERR_UNKNOWN -1 /* An unknown error occured */ +#define SER_ERR_NOT_OPEN -2 /* The specified COM port is not opened */ +#define SER_ERR_ALREADY_OPEN -3 /* The specified COM port is already opened */ +#define SER_ERR_NO_UART -4 /* Could not find a UART for this COM port */ +#define SER_ERR_INVALID_COMPORT -5 /* User specified an invalid COM port */ +#define SER_ERR_INVALID_BASE -6 /* User specified an invalid base address */ +#define SER_ERR_INVALID_IRQ -7 /* User specified an invalid IRQ number */ +#define SER_ERR_INVALID_BPS -8 /* User specified an invalid BPS rate */ +#define SER_ERR_INVALID_DATA_BITS -9 /* User specified an invalid number of data bits */ +#define SER_ERR_INVALID_PARITY -10 /* User specified an invalid parity type */ +#define SER_ERR_INVALID_STOP_BITS -11 /* User specified an invalid number of stop bits */ +#define SER_ERR_INVALID_HANDSHAKING -12 /* User specified an invalid handshaking type */ +#define SER_ERR_INVALID_FIFO_THRESHOLD -13 /* User specified an invalid fifo threshold value */ +#define SER_ERR_NULL_PTR -14 /* User specified a buffer address that was NULL */ +#define SER_ERR_IRQ_NOT_FOUND -15 /* Could not find an IRQ for the specified COM port */ +#define SER_ERR_LOCK_MEM -16 /* Could not lock memory in DPMI mode */ +#define SER_ERR_UNLOCK_MEM -17 /* Could not unlock memory in DPMI mode */ + + +/* Name: serial_open() + * + * Desc: Finds and initializes the specified COM port. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * long bps: Bits per second. + * (50, 75, 110, 150, 300, 600, 1200, 2400, 4800, + * 9600, 19200, 38400, 57600, 115200) + * int data_bits: Number of data word bits (5-8) + * char parity: Parity mode ('e', 'o', 'n', 'm', 's') + * int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2, + * actual stop bits is 1.5. + * int handshaking: Handshaking mode + * (SER_HANDSHAKING_NONE, SER_HANDSHAKING_XONXOFF, + * SER_HANDSHAKING_RTSCTS, SER_HANDSHAKING_DTRDSR) + * + * Return: SER_SUCCESS or an error code + */ +int serial_open(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking); + + +/* Name: serial_close() + * + * Desc: Clean up and close the specified serial port. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * + * Return: SER_SUCCESS or an error code + */ +int serial_close(int com); + + +/* Name: serial_read() + * + * Desc: Read data from the specified serial port buffer. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * char* data: Pointer to data buffer + * int len: Number of bytes to read + * + * Return: number of bytes read or an error code + */ +int serial_read(int com, char* data, int len); + + +/* Name: serial_write() and serial_write_buffered() + * + * Desc: Write data to the serial port. + * serial_write() will write the data directly to serial port and will + * block until it has completely sent the data or handshaking has + * stopped output transmission while serial_write_buffered() will copy + * as much as possible data bytes to the transmit buffer and will return + * immediately enabling asynchronous output. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * char* data: Pointer to data buffer + * int len: Number of bytes to write + * + * Return: number of bytes written or an error code + */ +int serial_write (int com, const char* data, int len); +int serial_write_buffered(int com, const char* data, int len); + + +/* Name: serial_set() + * + * Desc: Change the specified serial port's settings. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * long bps: Bits per second. + * (50, 75, 110, 150, 300, 600, 1200, 2400, 4800, + * 9600, 19200, 38400, 57600, 115200) + * int data_bits: Data bits (5, 6, 7, or 8) + * char parity: Parity ('n' = none, 'e' = even, 'o' = odd, 'm' = mark, 's' = space) + * int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2, + * actual stop bits is 1.5. + * int handshaking: Handshaking type (see handshaking modes at top of file) + * + * Return: SER_SUCCESS or an error code + */ +int serial_set(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking); + + +/* serial_set_xxx() functions. These take the same arguments + * as the corresponding ones in serial_set() and return either + * SER_SUCCESS or an error code. + */ +int serial_set_bps (int com, long bps); +int serial_set_parity (int com, char parity); +int serial_set_data (int com, int data); +int serial_set_stop (int com, int stop); +int serial_set_handshaking (int com, int handshaking); + +/* Advanced settings. Use these only if you know what you are doing. */ + +/* UART's FIFO threshold -- 14, 8, 4, 1, or 0 (off) */ +int serial_set_fifo_threshold(int com, int threshold); +/* UART's base address */ +int serial_set_base (int com, int base); +/* UART's IRQ */ +int serial_set_irq (int com, int base); + +int serial_set_rts(int comport, int rts); +int serial_set_dtr(int comport, int dtr); + +int serial_set_mcr(int comport, int mcr); + + +/* serial_get_xxx() functions. These return the same types as are supplied + * to serial_set(). The returned type may be negative, in which case it + * is an error code. + */ +int serial_get_base (int com); +int serial_get_irq (int com); +long serial_get_bps (int com); +char serial_get_parity (int com); +int serial_get_data (int com); +int serial_get_stop (int com); +int serial_get_handshaking (int com); + + +int serial_get_rts(int comport); +int serial_get_dtr(int comport); +int serial_get_cts(int comport); +int serial_get_dsr(int comport); + +int serial_get_mcr(int comport); +int serial_get_msr(int comport); +int serial_get_lsr(int comport); + + +/* get number of bytes or discard data in TX/RX buffers + */ +int serial_get_tx_buffered(int comport); +int serial_get_rx_buffered(int comport); +int serial_clear_tx_buffer(int comport); +int serial_clear_rx_buffer(int comport); + +#endif diff --git a/src/Makefile b/src/Makefile index a1851ec087..67f76e5410 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,7 +38,7 @@ else ifeq ($(HB_PLATFORM),dos) DYNDIRLIST_BASE += src/rtl/gtdos ifneq ($(filter $(HB_COMPILER),watcom djgpp),) - DYNDIRLIST_BASE += src/3rd/hbpmcom + DYNDIRLIST_BASE += src/3rd/hbdossrl endif endif ifeq ($(HB_PLATFORM),os2) diff --git a/src/rtl/hbcom.c b/src/rtl/hbcom.c index 5a9b47d519..975689f3ac 100644 --- a/src/rtl/hbcom.c +++ b/src/rtl/hbcom.c @@ -68,8 +68,9 @@ # define BSD_COMP # endif # endif -#elif defined( HB_OS_DOS ) -# define HB_HAS_PMCOM +#elif defined( HB_OS_DOS ) && \ + ! defined( HB_HAS_PMCOM ) && ! defined( HB_HAS_DOSSRL ) +# define HB_HAS_DOSSRL #endif @@ -88,6 +89,8 @@ # endif #elif defined( HB_HAS_SIOLIB ) # include +#elif defined( HB_HAS_DOSSRL ) +# include "../../src/3rd/hbdossrl/serial.h" #elif defined( HB_HAS_PMCOM ) # include "../../src/3rd/hbpmcom/com.h" #elif defined( HB_OS_WIN ) @@ -3084,6 +3087,571 @@ int hb_comOpen( int iPort ) /* end of HB_OS_OS2 */ +#elif defined( HB_HAS_DOSSRL ) + +static void hb_comSetOsError( PHB_COM pCom, int iError ) +{ + pCom->oserr = iError; + + switch( iError ) + { + case SER_SUCCESS: /* Function completed successfully */ + pCom->error = 0; + break; + case SER_ERR_NOT_OPEN: /* The specified COM port is not opened */ + pCom->error = HB_COM_ERR_CLOSED; + break; + case SER_ERR_ALREADY_OPEN: /* The specified COM port is already opened */ + pCom->error = HB_COM_ERR_ALREADYOPEN; + break; + case SER_ERR_NO_UART: /* Could not find a UART for this COM port */ + pCom->error = HB_COM_ERR_NOCOM; + break; + case SER_ERR_INVALID_COMPORT: /* User specified an invalid COM port */ + pCom->error = HB_COM_ERR_WRONGPORT; + break; + case SER_ERR_INVALID_BPS: /* User specified an invalid BPS rate */ + case SER_ERR_INVALID_DATA_BITS: /* User specified an invalid number of data bits */ + case SER_ERR_INVALID_PARITY: /* User specified an invalid parity type */ + case SER_ERR_INVALID_STOP_BITS: /* User specified an invalid number of stop bits */ + case SER_ERR_INVALID_HANDSHAKING: /* User specified an invalid handshaking type */ + case SER_ERR_INVALID_FIFO_THRESHOLD:/* User specified an invalid fifo threshold value */ + case SER_ERR_NULL_PTR: /* User specified a buffer address that was NULL */ + pCom->error = HB_COM_ERR_PARAMVALUE; + break; + case SER_ERR_INVALID_BASE: /* User specified an invalid base address */ + case SER_ERR_INVALID_IRQ: /* User specified an invalid IRQ number */ + pCom->error = HB_COM_ERR_PARAMVALUE; + break; + case SER_ERR_IRQ_NOT_FOUND: /* Could not find an IRQ for the specified COM port */ + case SER_ERR_LOCK_MEM: /* Could not lock memory in DPMI mode */ + case SER_ERR_UNKNOWN: /* An unknown error occured */ + default: + pCom->error = iError < 0 ? HB_COM_ERR_OTHER : 0; + } +} + +int hb_comInputCount( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iCount = -1; + + if( pCom ) + { + iCount = serial_get_rx_buffered( iPort - 1 ); + hb_comSetOsError( pCom, iCount ); + if( iCount < 0 ) + iCount = -1; + } + + return iCount; +} + +int hb_comOutputCount( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iCount = -1; + + if( pCom ) + { + iCount = serial_get_tx_buffered( iPort - 1 ); + hb_comSetOsError( pCom, iCount ); + if( iCount < 0 ) + iCount = -1; + } + + return iCount; +} + +int hb_comFlush( int iPort, int iType ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + if( pCom ) + { + switch( iType ) + { + case HB_COM_IFLUSH: + case HB_COM_IOFLUSH: + iResult = serial_clear_rx_buffer( iPort - 1 ); + if( iType == HB_COM_IFLUSH || iResult != SER_SUCCESS ) + break; + break; + case HB_COM_OFLUSH: + iResult = serial_clear_tx_buffer( iPort - 1 ); + break; + default: + hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE ); + return -1; + } + + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + } + + return iResult; +} + +int hb_comMCR( int iPort, int * piValue, int iClr, int iSet ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + int iValue = 0; + + if( pCom ) + { + iResult = serial_get_mcr( iPort - 1 ); + if( iResult >= 0 ) + { + int iNewVal = ( iResult & ~iClr ) | iSet; + iValue = iResult; + iResult = iValue == iNewVal ? 0 : serial_set_mcr( iPort - 1, iNewVal ); + } + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + } + + if( piValue ) + *piValue = iValue; + + return iResult; +} + +int hb_comMSR( int iPort, int * piValue ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + int iValue = 0; + + if( pCom ) + { + iValue = serial_get_msr( iPort - 1 ); + hb_comSetOsError( pCom, iValue ); + if( iValue < 0 ) + { + iResult = -1; + iValue = 0; + } + } + + if( piValue ) + *piValue = iValue; + + return iResult; +} + +int hb_comLSR( int iPort, int * piValue ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1, iValue = 0; + + if( pCom ) + { + iValue = serial_get_lsr( iPort - 1 ); + hb_comSetOsError( pCom, iValue ); + if( iValue < 0 ) + { + iResult = -1; + iValue = 0; + } + } + + if( piValue ) + *piValue = iValue; + + return iResult; +} + +int hb_comSendBreak( int iPort, int iDurationInMilliSecs ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + + HB_SYMBOL_UNUSED( iDurationInMilliSecs ); + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return -1; +} + +int hb_comFlowControl( int iPort, int *piFlow, int iFlow ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1, iValue = 0; + + if( pCom ) + { + iResult = serial_get_handshaking( iPort - 1 ); + + if( iResult >= 0 ) + { + switch( iResult ) + { + case SER_HANDSHAKING_XONXOFF: + iValue |= HB_COM_FLOW_XOFF | HB_COM_FLOW_XOFF; + break; + case SER_HANDSHAKING_RTSCTS: + iValue |= HB_COM_FLOW_IRTSCTS | HB_COM_FLOW_ORTSCTS; + break; + case SER_HANDSHAKING_DTRDSR: + iValue |= HB_COM_FLOW_IDTRDSR | HB_COM_FLOW_ODTRDSR; + break; + case SER_HANDSHAKING_NONE: + break; + } + if( iFlow >= 0 ) + { + int iFlowVal = 0; + + if( iFlow & ( HB_COM_FLOW_IRTSCTS | HB_COM_FLOW_ORTSCTS ) ) + iFlowVal = SER_HANDSHAKING_RTSCTS; + else if( iFlow & ( HB_COM_FLOW_IDTRDSR | HB_COM_FLOW_ODTRDSR ) ) + iFlowVal = SER_HANDSHAKING_DTRDSR; + else if( iFlow & ( HB_COM_FLOW_XON | HB_COM_FLOW_XOFF ) ) + iFlowVal = SER_HANDSHAKING_XONXOFF; + + if( iFlowVal != iResult ) + iResult = serial_set_handshaking( iPort - 1, iFlowVal ); + } + } + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + } + + if( piFlow ) + *piFlow = iValue; + + return iResult; +} + +int hb_comFlowSet( int iPort, int iFlow ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + + HB_SYMBOL_UNUSED( iFlow ); + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return -1; +} + +int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + HB_SYMBOL_UNUSED( iXONchar ); + HB_SYMBOL_UNUSED( iXOFFchar ); + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return iResult; +} + +int hb_comDiscardChar( int iPort, int iChar ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + + HB_SYMBOL_UNUSED( iChar ); + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return -1; +} + +int hb_comErrorChar( int iPort, int iChar ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + + HB_SYMBOL_UNUSED( iChar ); + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return -1; +} + +int hb_comOutputState( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + if( pCom ) + { + iResult = serial_get_tx_buffered( iPort - 1 ); + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + else if( iResult == 0 ) + iResult = HB_COM_TX_EMPTY; + else + iResult = 0; + } + + return iResult; +} + +int hb_comInputState( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + if( pCom ) + hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT ); + + return iResult; +} + +long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + long lSent = -1; + + if( pCom ) + { + const char * buffer = ( const char * ) data; + HB_MAXUINT timer = timeout <= 0 ? 0 : ( hb_dateMilliSeconds() + timeout ); + + hb_comSetOsError( pCom, SER_SUCCESS ); + lSent = 0; + + hb_vmUnlock(); + + while( len > 0 ) + { + int iSent; + + iSent = serial_write_buffered( iPort - 1, buffer, len ); + if( iSent < 0 ) + { + hb_comSetOsError( pCom, iSent ); + if( lSent == 0 ) + lSent = -1; + break; + } + + lSent += iSent; + buffer += iSent; + len -= iSent; + + if( len > 0 ) + { + if( timer == 0 || timer < hb_dateMilliSeconds() ) + { + if( lSent == 0 ) + { + hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT ); + lSent = -1; + } + break; + } + hb_releaseCPU(); + } + } + + hb_vmLock(); + } + + return lSent; +} + +long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + long lReceived = -1; + + if( pCom ) + { + char * buffer = ( char * ) data; + HB_MAXUINT timer = timeout <= 0 ? 0 : ( hb_dateMilliSeconds() + timeout ); + + hb_comSetOsError( pCom, SER_SUCCESS ); + lReceived = 0; + + hb_vmUnlock(); + + while( len > 0 ) + { + int iRecv = serial_read( iPort - 1, buffer, len ); + + if( iRecv < 0 ) + { + hb_comSetOsError( pCom, iRecv ); + if( lReceived == 0 ) + lReceived = -1; + break; + } + + lReceived += iRecv; + buffer += iRecv; + len -= iRecv; + + if( lReceived > 0 || timer == 0 || timer < hb_dateMilliSeconds() ) + { + if( lReceived == 0 ) + { + hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT ); + lReceived = -1; + } + break; + } + hb_releaseCPU(); + } + + hb_vmLock(); + } + + return lReceived; +} + +static int s_comChkPortParam( int *piBaud, int *piParity, + int *piSize, int *piStop ) +{ + int iResult = 0; + + if( *piBaud == 0 ) + *piBaud = 9600; + + *piParity = HB_TOLOWER( *piParity ); + switch( *piParity ) + { + case 0: + *piParity = 'n'; + case 'n': + case 'e': + case 'o': + case 's': + case 'm': + break; + + default: + iResult = -1; + } + + switch( *piSize ) + { + case 0: + *piSize = 8; + case 8: + case 7: + case 6: + case 5: + break; + default: + iResult = -1; + } + + switch( *piStop ) + { + case 0: + *piStop = 1; + case 1: + case 2: + break; + default: + iResult = -1; + } + + return iResult; +} + +int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + if( pCom ) + { + iResult = s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop ); + if( iResult == 0 ) + { + iResult = serial_set_bps( iPort - 1, iBaud ); + if( iResult == SER_SUCCESS ) + iResult = serial_set_data( iPort - 1, iSize ); + if( iResult == SER_SUCCESS ) + iResult = serial_set_parity( iPort - 1, iParity ); + if( iResult == SER_SUCCESS ) + iResult = serial_set_stop( iPort - 1, iStop ); + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + } + else + hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE ); + } + + return iResult; +} + +int hb_comClose( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN ); + int iResult = -1; + + if( pCom ) + { + hb_vmUnlock(); + iResult = serial_close( iPort - 1 ); + pCom->status &= ~HB_COM_OPEN; + hb_comSetOsError( pCom, iResult ); + if( iResult < 0 ) + iResult = -1; + hb_vmLock(); + } + + return iResult; +} + +int hb_comOpen( int iPort ) +{ + PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED ); + int iResult = -1; + + if( pCom ) + { + if( pCom->status & HB_COM_OPEN ) + { + hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN ); + } + else + { + int iBaud, iParity, iSize, iStop, iFlowControl; + + hb_vmUnlock(); + + iBaud = iParity = iSize = iStop = 0; + iFlowControl = SER_HANDSHAKING_NONE; + s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop ); + iResult = serial_open( iPort - 1, iBaud, iSize, iParity, iStop, + iFlowControl ); + if( iResult == 0 ) + { + pCom->status |= HB_COM_OPEN; + hb_comSetOsError( pCom, 0 ); + } + else + { + serial_close( iPort - 1 ); + hb_comSetOsError( pCom, iResult ); + iResult = -1; + } + + hb_vmLock(); + } + } + + return iResult; +} + +/* end of HB_HAS_DOSSRL */ + #elif defined( HB_HAS_PMCOM ) static void hb_comSetOsError( PHB_COM pCom, int iError ) diff --git a/utils/hbmk2/hbmk2.hbp b/utils/hbmk2/hbmk2.hbp index 31968cde83..04a0cd74ac 100644 --- a/utils/hbmk2/hbmk2.hbp +++ b/utils/hbmk2/hbmk2.hbp @@ -17,7 +17,7 @@ hbmk2.prg -DHBMK_WITH_ALL_EMBEDDED_HEADERS --lhbpmcom{dos} +-lhbdossrl{dos} # not using these together with .rc input, because some compilers (mingw) # do not support multiple .rc inputs diff --git a/utils/hbmk2/hbmk2.prg b/utils/hbmk2/hbmk2.prg index aacdcff1b8..fc41d59bb3 100644 --- a/utils/hbmk2/hbmk2.prg +++ b/utils/hbmk2/hbmk2.prg @@ -3640,7 +3640,7 @@ STATIC FUNCTION __hbmk( aArgs, nArgTarget, nLevel, /* @ */ lPause, /* @ */ lExit OTHERWISE ; tmp := NIL ENDSWITCH - AAdd( hbmk[ _HBMK_aLIBUSERSYS ], "hbpmcom" ) + AAdd( hbmk[ _HBMK_aLIBUSERSYS ], "hbdossrl" ) IF ! Empty( tmp ) #if defined( HB_HAS_WATT ) AAdd( hbmk[ _HBMK_aLIBUSERSYSPRE ], tmp )