From 222e79232cc30d622820a3049bac7d2d33da608f Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Sat, 7 Mar 2020 00:50:31 +0600 Subject: [PATCH] [CPORTLIB][NTOS:INBV][KDCOM][FREELDR] Add ComPort library for NEC PC-98 series (#2407) There are 2 known serial ports: COM1 - based on Intel 8251A COM2 - National Semiconductor 16550 --- boot/freeldr/freeldr/lib/comm/rs232.c | 4 + boot/freeldr/freeldr/ntldr/headless.c | 57 ++ drivers/base/kdcom/kdcom.c | 4 + drivers/base/kdgdb/kdcom.c | 4 + ntoskrnl/inbv/inbvport.c | 37 ++ ntoskrnl/kd/i386/kdbg.c | 4 + sdk/include/reactos/drivers/pc98/cpu.h | 28 + sdk/include/reactos/drivers/pc98/pit.h | 97 ++++ sdk/include/reactos/drivers/pc98/serial.h | 184 +++++++ sdk/include/reactos/drivers/pc98/sysport.h | 82 +++ sdk/lib/cportlib/CMakeLists.txt | 6 +- sdk/lib/cportlib/cport_pc98.c | 598 +++++++++++++++++++++ 12 files changed, 1104 insertions(+), 1 deletion(-) create mode 100644 sdk/include/reactos/drivers/pc98/cpu.h create mode 100644 sdk/include/reactos/drivers/pc98/pit.h create mode 100644 sdk/include/reactos/drivers/pc98/serial.h create mode 100644 sdk/include/reactos/drivers/pc98/sysport.h create mode 100644 sdk/lib/cportlib/cport_pc98.c diff --git a/boot/freeldr/freeldr/lib/comm/rs232.c b/boot/freeldr/freeldr/lib/comm/rs232.c index 4860720c3cc..10d0b0bedb8 100644 --- a/boot/freeldr/freeldr/lib/comm/rs232.c +++ b/boot/freeldr/freeldr/lib/comm/rs232.c @@ -33,7 +33,11 @@ #define DEFAULT_BAUD_RATE 19200 #if defined(_M_IX86) || defined(_M_AMD64) +#if defined(SARCH_PC98) +static const ULONG BaseArray[] = {0, 0x30, 0x238}; +#else static const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8}; +#endif #elif defined(_M_PPC) static const ULONG BaseArray[] = {0, 0x800003F8}; #elif defined(_M_MIPS) diff --git a/boot/freeldr/freeldr/ntldr/headless.c b/boot/freeldr/freeldr/ntldr/headless.c index f5ee58697f2..35af2e39daf 100644 --- a/boot/freeldr/freeldr/ntldr/headless.c +++ b/boot/freeldr/freeldr/ntldr/headless.c @@ -70,6 +70,42 @@ WinLdrPortInitialize(IN ULONG BaudRate, /* Set default baud rate */ if (BaudRate == 0) BaudRate = 19200; +#if defined(SARCH_PC98) + /* Check if port or address given */ + if (PortNumber) + { + /* Pick correct address for port */ + if (!PortAddress) + { + if (PortNumber == 1) + { + PortAddress = (PUCHAR)0x30; + } + else + { + PortAddress = (PUCHAR)0x238; + PortNumber = 2; + } + } + } + else + { + /* Pick correct port for address */ + PortAddress = (PUCHAR)0x30; + if (CpDoesPortExist(PortAddress)) + { + PortNumber = 1; + } + else + { + PortAddress = (PUCHAR)0x238; + if (!CpDoesPortExist(PortAddress)) + return FALSE; + + PortNumber = 2; + } + } +#else /* Check if port or address given */ if (PortNumber) { @@ -111,6 +147,7 @@ WinLdrPortInitialize(IN ULONG BaudRate, PortNumber = 1; } } +#endif /* Not yet supported */ ASSERT(LoaderRedirectionInformation.IsMMIODevice == FALSE); @@ -173,6 +210,25 @@ WinLdrInitializeHeadlessPort(VOID) PortAddress = LoaderRedirectionInformation.PortAddress; BaudRate = LoaderRedirectionInformation.BaudRate; +#if defined(SARCH_PC98) + /* Pick a port address */ + if (PortNumber) + { + if (!PortAddress) + { + if (PortNumber == 2) + LoaderRedirectionInformation.PortAddress = (PUCHAR)0x238; + else + LoaderRedirectionInformation.PortAddress = (PUCHAR)0x30; + } + } + else + { + /* No number, so no EMS */ + WinLdrTerminalConnected = FALSE; + return; + } +#else /* Pick a port address */ if (PortNumber) { @@ -204,6 +260,7 @@ WinLdrInitializeHeadlessPort(VOID) WinLdrTerminalConnected = FALSE; return; } +#endif /* Call arch code to initialize the port */ PortAddress = LoaderRedirectionInformation.PortAddress; diff --git a/drivers/base/kdcom/kdcom.c b/drivers/base/kdcom/kdcom.c index d7892c1670f..eb62aa4e551 100644 --- a/drivers/base/kdcom/kdcom.c +++ b/drivers/base/kdcom/kdcom.c @@ -23,7 +23,11 @@ #define DEFAULT_BAUD_RATE 19200 #if defined(_M_IX86) || defined(_M_AMD64) +#if defined(SARCH_PC98) +const ULONG BaseArray[] = {0, 0x30, 0x238}; +#else const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8}; +#endif #elif defined(_M_PPC) const ULONG BaseArray[] = {0, 0x800003F8}; #elif defined(_M_MIPS) diff --git a/drivers/base/kdgdb/kdcom.c b/drivers/base/kdgdb/kdcom.c index 9c52ba6d14e..1a798b74403 100644 --- a/drivers/base/kdgdb/kdcom.c +++ b/drivers/base/kdgdb/kdcom.c @@ -21,7 +21,11 @@ #define DEFAULT_BAUD_RATE 19200 #if defined(_M_IX86) || defined(_M_AMD64) +#if defined(SARCH_PC98) +const ULONG BaseArray[] = {0, 0x30, 0x238}; +#else const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8}; +#endif #elif defined(_M_PPC) const ULONG BaseArray[] = {0, 0x800003F8}; #elif defined(_M_MIPS) diff --git a/ntoskrnl/inbv/inbvport.c b/ntoskrnl/inbv/inbvport.c index e95e8256783..526383a4771 100644 --- a/ntoskrnl/inbv/inbvport.c +++ b/ntoskrnl/inbv/inbvport.c @@ -82,6 +82,42 @@ InbvPortInitialize(IN ULONG BaudRate, /* Set default baud rate */ if (BaudRate == 0) BaudRate = 19200; +#if defined(SARCH_PC98) + /* Check if port or address given */ + if (PortNumber) + { + /* Pick correct address for port */ + if (!PortAddress) + { + if (PortNumber == 1) + { + PortAddress = (PUCHAR)0x30; + } + else + { + PortAddress = (PUCHAR)0x238; + PortNumber = 2; + } + } + } + else + { + /* Pick correct port for address */ + PortAddress = (PUCHAR)0x30; + if (CpDoesPortExist(PortAddress)) + { + PortNumber = 1; + } + else + { + PortAddress = (PUCHAR)0x238; + if (!CpDoesPortExist(PortAddress)) + return FALSE; + + PortNumber = 2; + } + } +#else /* Check if port or address given */ if (PortNumber) { @@ -123,6 +159,7 @@ InbvPortInitialize(IN ULONG BaudRate, PortNumber = 1; } } +#endif /* Initialize the port unless it's already up, and then return it */ if (Port[PortNumber - 1].Address) return FALSE; diff --git a/ntoskrnl/kd/i386/kdbg.c b/ntoskrnl/kd/i386/kdbg.c index b87ce56e9ff..9277e019b2a 100644 --- a/ntoskrnl/kd/i386/kdbg.c +++ b/ntoskrnl/kd/i386/kdbg.c @@ -17,7 +17,11 @@ #define DEFAULT_BAUD_RATE 19200 #if defined(_M_IX86) || defined(_M_AMD64) +#if defined(SARCH_PC98) +const ULONG BaseArray[] = {0, 0x30, 0x238}; +#else const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8}; +#endif #elif defined(_M_PPC) const ULONG BaseArray[] = {0, 0x800003F8}; #elif defined(_M_MIPS) diff --git a/sdk/include/reactos/drivers/pc98/cpu.h b/sdk/include/reactos/drivers/pc98/cpu.h new file mode 100644 index 00000000000..d5e10a911c6 --- /dev/null +++ b/sdk/include/reactos/drivers/pc98/cpu.h @@ -0,0 +1,28 @@ +/* + * PROJECT: NEC PC-98 series onboard hardware + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: CPU I/O ports header file + * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +#define CPU_IO_o_RESET 0x0F +#define CPU_IO_o_A20_UNMASK 0xF2 + +#define CPU_IO_o_A20_CONTROL 0xF6 + #define CPU_A20_ENABLE 0x02 + #define CPU_A20_DISABLE 0x03 + +/* + * ARTIC (A Relative Time Indication Counter) - 24-bit binary up counter + */ +#define CPU_IO_o_ARTIC_DELAY 0x5F /* Constant delay (about 600 ns) */ +#define CPU_IO_i_ARTIC_0 0x5C +#define CPU_IO_i_ARTIC_1 0x5D +#define CPU_IO_i_ARTIC_2 0x5E +#define CPU_IO_i_ARTIC_3 0x5F + +#define ARTIC_FREQUENCY 307200 +#define ARTIC_FREQUENCY_0_1 ARTIC_FREQUENCY +#define ARTIC_FREQUENCY_2_3 (ARTIC_FREQUENCY >> 8) diff --git a/sdk/include/reactos/drivers/pc98/pit.h b/sdk/include/reactos/drivers/pc98/pit.h new file mode 100644 index 00000000000..2a3c56c2771 --- /dev/null +++ b/sdk/include/reactos/drivers/pc98/pit.h @@ -0,0 +1,97 @@ +/* + * PROJECT: NEC PC-98 series on-board hardware + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Intel 8253A PIT header file + * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +#define TIMER_CHANNEL0_DATA_PORT 0x71 +#define TIMER_CHANNEL1_DATA_PORT 0x73 +#define TIMER_CHANNEL2_DATA_PORT 0x75 +#define TIMER_CONTROL_PORT 0x77 + +/* Tick rate of PIT depends on system clock frequency */ +#define TIMER_FREQUENCY_1 1996800 /* 8 MHz */ +#define TIMER_FREQUENCY_2 2457600 /* 10 MHz, 5 MHz */ + +typedef enum _TIMER_OPERATING_MODES +{ + /* Interrupt On Terminal Count */ + PitOperatingMode0, + + /* Hardware Re-triggerable One-Shot */ + PitOperatingMode1, + + /* Rate Generator */ + PitOperatingMode2, + + /* Square Wave Generator */ + PitOperatingMode3, + + /* Software Triggered Strobe */ + PitOperatingMode4, + + /* Hardware Triggered Strobe */ + PitOperatingMode5 +} TIMER_OPERATING_MODES; + +typedef enum _TIMER_ACCESS_MODES +{ + PitAccessModeCounterLatch, + PitAccessModeLow, + PitAccessModeHigh, + PitAccessModeLowHigh +} TIMER_ACCESS_MODES; + +typedef enum _TIMER_CHANNELS +{ + /* IRQ 0 */ + PitChannel0, + + /* PC Speaker */ + PitChannel1, + + /* RS-232 chipset */ + PitChannel2, + + /* Execute multiple latch command */ + MultipleLatch +} TIMER_CHANNELS; + +typedef union _TIMER_CONTROL_PORT_REGISTER +{ + struct + { + UCHAR BcdMode:1; + UCHAR OperatingMode:3; + UCHAR AccessMode:2; + UCHAR Channel:2; + }; + UCHAR Bits; +} TIMER_CONTROL_PORT_REGISTER, *PTIMER_CONTROL_PORT_REGISTER; + +FORCEINLINE +ULONG +Read8253Timer(TIMER_CHANNELS TimerChannel) +{ + ULONG Count; + + WRITE_PORT_UCHAR((PUCHAR)TIMER_CONTROL_PORT, (TimerChannel << 6) | PitAccessModeCounterLatch); + Count = READ_PORT_UCHAR((PUCHAR)(TIMER_CHANNEL0_DATA_PORT + TimerChannel * 2)); + Count |= READ_PORT_UCHAR((PUCHAR)(TIMER_CHANNEL0_DATA_PORT + TimerChannel * 2)) << 8; + + return Count; +} + +FORCEINLINE +VOID +Write8253Timer( + TIMER_CONTROL_PORT_REGISTER TimerControl, + USHORT Count) +{ + WRITE_PORT_UCHAR((PUCHAR)TIMER_CONTROL_PORT, TimerControl.Bits); + WRITE_PORT_UCHAR((PUCHAR)(TIMER_CHANNEL0_DATA_PORT + TimerControl.Channel * 2), Count & 0xFF); + WRITE_PORT_UCHAR((PUCHAR)(TIMER_CHANNEL0_DATA_PORT + TimerControl.Channel * 2), (Count >> 8) & 0xFF); +} diff --git a/sdk/include/reactos/drivers/pc98/serial.h b/sdk/include/reactos/drivers/pc98/serial.h new file mode 100644 index 00000000000..fb49c4e7abc --- /dev/null +++ b/sdk/include/reactos/drivers/pc98/serial.h @@ -0,0 +1,184 @@ +/* + * PROJECT: NEC PC-98 series onboard hardware + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: UART header file + * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +/* COM1 (Intel 8251A-based UART) **********************************************/ + +/* + * UART registers and definitions + */ +#define SER1_IO_i_DATA 0x030 +#define SER1_IO_i_STATUS 0x032 + #define SER1_STATUS_TxRDY 0x01 /* Transmitter ready */ + #define SER1_STATUS_RxRDY 0x02 /* Receiver ready */ + #define SER1_STATUS_TxEMPTY 0x04 /* Transmitter empty */ + #define SER1_STATUS_PE 0x08 /* Parity error */ + #define SER1_STATUS_OE 0x10 /* Overrun error */ + #define SER1_STATUS_FE 0x20 /* Framing error */ + #define SER1_STATUS_SYNDET 0x40 /* Sync detect / Break detect */ + #define SER1_STATUS_DSR 0x80 /* Data set ready */ +#define SER1_IO_i_RECEIVER_BUFFER 0x130 +#define SER1_IO_i_LINE_STATUS 0x132 + #define SER1_LSR_TxEMPTY 0x01 /* Transmitter empty */ + #define SER1_LSR_TxRDY 0x02 /* Transmitter ready */ + #define SER1_LSR_RxRDY 0x04 /* Receiver ready */ + #define SER1_LSR_OE 0x10 /* Overrun error */ + #define SER1_LSR_PE 0x20 /* Parity error */ + #define SER1_LSR_BI 0x80 /* Break detect */ +#define SER1_IO_i_MODEM_STATUS 0x134 + #define SER_MSR_CTS_CHANGED 0x01 /* Change in clear to send */ + #define SER_MSR_DSR_CHANGED 0x02 /* Change in data set ready */ + #define SER_MSR_RI_CHANGED 0x04 /* Trailing edge ring indicator */ + #define SER_MSR_DCD_CHANGED 0x08 /* Change in carrier detect */ + #define SER_MSR_CTS 0x10 /* Clear to send */ + #define SER_MSR_DSR 0x20 /* Data set ready */ + #define SER_MSR_RI 0x40 /* Ring indicator */ + #define SER_MSR_DCD 0x80 /* Data carrier detect */ +#define SER1_IO_i_INTERRUPT_ID 0x136 + #define SER_IIR_MS 0x00 /* Modem status change */ + #define SER_IIR_THR 0x02 /* Transmitter holding register empty */ + #define SER_IIR_RDA 0x04 /* Received data acailable */ + #define SER_IIR_RLS 0x06 /* Receiver line status change */ + #define SER_IIR_CTI 0x0C /* Character timeout */ + #define SER_IIR_ID_MASK 0x0F + #define SER_IIR_SELF 0x01 /* No interrupt pending */ + #define SER1_IIR_MUST_BE_ZERO 0x20 + #define SER1_IIR_FIFOS_ENABLED 0x40 /* Toggles for each read */ +#define SER1_IO_i_FIFO_CONTROL 0x138 +#define SER1_IO_i_DIVISOR_LATCH 0x13A + +#define SER1_IO_o_DATA 0x030 +#define SER1_IO_o_MODE_COMMAND 0x032 + /* Parity generate/check */ + #define SER1_MODE_PEN 0x10 /* Parity enable */ + #define SER1_MODE_EP 0x20 /* Even parity generation/check */ + #define SER1_MODE_ESD 0x40 /* External sync detect */ + #define SER1_MODE_SCS 0x80 /* Single character sync */ + /* Character length */ + #define SER1_MODE_LENGTH_5 0x00 + #define SER1_MODE_LENGTH_6 0x04 + #define SER1_MODE_LENGTH_7 0x08 + #define SER1_MODE_LENGTH_8 0x0C + /* Baud rate factor */ + #define SER1_MODE_SYNC 0x00 + #define SER1_MODE_CLOCKx1 0x01 + #define SER1_MODE_CLOCKx16 0x02 + #define SER1_MODE_CLOCKx64 0x03 + /* Number of stop bits */ + #define SER1_MODE_1_STOP 0x40 + #define SER1_MODE_1_5_STOP 0x80 + #define SER1_MODE_2_STOP 0xC0 + /* Command bits */ + #define SER1_COMMMAND_TxEN 0x01 /* Transmit enable */ + #define SER1_COMMMAND_DTR 0x02 /* Data terminal ready */ + #define SER1_COMMMAND_RxEN 0x04 /* Receive enable */ + #define SER1_COMMMAND_SBRK 0x08 /* Send break character */ + #define SER1_COMMMAND_ER 0x10 /* Error reset */ + #define SER1_COMMMAND_RTS 0x20 /* Request to send */ + #define SER1_COMMMAND_IR 0x40 /* Internal reset */ + #define SER1_COMMMAND_EH 0x80 /* Enter hunt mode */ +#define SER1_IO_o_TRANSMITTER_BUFFER 0x130 +#define SER1_IO_o_FIFO_CONTROL 0x138 + #define SER_FCR_DISABLE 0x00 /* Disable FIFO */ + #define SER_FCR_ENABLE 0x01 /* Enable FIFO */ + #define SER_FCR_RCVR_RESET 0x02 /* Clear receive FIFO */ + #define SER_FCR_TXMT_RESET 0x04 /* Clear transmit FIFO */ + /* Receive FIFO interrupt trigger level */ + #define SER_FCR_1_BYTE_HIGH_WATER 0x00 + #define SER_FCR_4_BYTE_HIGH_WATER 0x40 + #define SER_FCR_8_BYTE_HIGH_WATER 0x80 + #define SER_FCR_14_BYTE_HIGH_WATER 0xC0 +#define SER1_IO_o_DIVISOR_LATCH 0x13A + #define SER1_DLR_BAUD_115200 0x01 + #define SER1_DLR_BAUD_57600 0x02 + #define SER1_DLR_BAUD_38400 0x03 + #define SER1_DLR_BAUD_28800 0x04 + #define SER1_DLR_BAUD_19200 0x06 + #define SER1_DLR_BAUD_14400 0x08 + #define SER1_DLR_BAUD_9600 0x0C + #define SER1_DLR_MODE_VFAST 0x80 + #define SER1_DLR_MODE_LEGACY 0x00 + +/* COM2 (National Semiconductor 16550 UART) ***********************************/ + +/* + * UART registers and definitions + */ +#define SER2_IO_i_RECEIVER_BUFFER 0x238 /* If DLAB = 0 */ +#define SER2_IO_i_DIVISOR_LATCH_LSB 0x238 /* If DLAB = 1 */ +#define SER2_IO_i_INTERRUPT_EN 0x239 /* If DLAB = 0 */ +#define SER2_IO_i_DIVISOR_LATCH_MSB 0x239 /* If DLAB = 1 */ +#define SER2_IO_i_INTERRUPT_ID 0x23A + /* Bits 0-3 same as for COM1 */ + #define SER2_IIR_MUST_BE_ZERO 0x30 + #define SER2_IIR_NO_FIFO 0x00 + #define SER2_IIR_HAS_FIFO 0x40 + #define SER2_IIR_FIFOS_ENABLED 0xC0 +#define SER2_IO_i_LINE_CONTROL 0x23B +#define SER2_IO_i_MODEM_CONTROL 0x23C +#define SER2_IO_i_LINE_STATUS 0x23D + #define SER2_LSR_DR 0x01 /* Data ready */ + #define SER2_LSR_OE 0x02 /* Overrun error */ + #define SER2_LSR_PE 0x04 /* Parity error */ + #define SER2_LSR_FE 0x80 /* Framing error */ + #define SER2_LSR_BI 0x80 /* Break interrupt */ + #define SER2_LSR_THR_EMPTY 0x20 /* Transmit holding register empty */ + #define SER2_LSR_TSR_EMPTY 0x40 /* Transmitter FIFO empty */ + #define SER2_LSR_ERROR_IN_FIFO 0x80 /* FIFO error */ +#define SER2_IO_i_MODEM_STATUS 0x23E + /* Bits 0-7 same as for COM1 */ +#define SER2_IO_i_SCRATCH 0x23F + +#define SER2_IO_o_TRANSMITTER_BUFFER 0x238 /* If DLAB = 0 */ +#define SER2_IO_o_DIVISOR_LATCH_LSB 0x238 /* If DLAB = 1 */ + #define SER2_DLR_BAUD_115200 0x0001 + #define SER2_DLR_BAUD_57600 0x0002 + #define SER2_DLR_BAUD_38400 0x0003 + #define SER2_DLR_BAUD_19200 0x0006 + #define SER2_DLR_BAUD_9600 0x000C + #define SER2_DLR_BAUD_4800 0x0018 + #define SER2_DLR_BAUD_2400 0x0030 + #define SER2_DLR_BAUD_1200 0x0060 + #define SER2_DLR_BAUD_600 0x00C0 + #define SER2_DLR_BAUD_300 0x0180 +#define SER2_IO_o_DIVISOR_LATCH_MSB 0x239 /* If DLAB = 1 */ +#define SER2_IO_o_INTERRUPT_EN 0x239 /* If DLAB = 0 */ + #define SER2_IER_DATA_RECEIVED 0x01 /* Received data available */ + #define SER2_IER_THR_EMPTY 0x02 /* Transmitter holding register empty */ + #define SER2_IER_LSR_CHANGE 0x04 /* Receiver line register status change */ + #define SER2_IER_MSR_CHANGE 0x08 /* Modem status register change */ +#define SER2_IO_o_FIFO_CONTROL 0x23A + /* Bits 0-2, 6-7 same as for COM1 */ + #define SER2_FCR_DMA_SELECT 0x04 +#define SER2_IO_o_LINE_CONTROL 0x23B + /* Character length */ + #define SER2_LCR_LENGTH_5 0x00 + #define SER2_LCR_LENGTH_6 0x01 + #define SER2_LCR_LENGTH_7 0x02 + #define SER2_LCR_LENGTH_8 0x03 + /* Number of stop bits */ + #define SER2_LCR_ST1 0x00 /* 1 */ + #define SER2_LCR_ST2 0x04 /* 1.5 - 2 */ + /* Parity generate/check */ + #define SER2_LCR_NO_PARITY 0x00 + #define SER2_LCR_ODD_PARITY 0x08 + #define SER2_LCR_EVEN_PARITY 0x18 + #define SER2_LCR_MARK_PARITY 0x28 + #define SER2_LCR_SPACE_PARITY 0x38 + #define SER2_LCR_BREAK 0x40 + #define SER2_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define SER2_IO_o_MODEM_CONTROL 0x23C + #define SER2_MCR_DTR_STATE 0x01 /* Data terminal ready */ + #define SER2_MCR_RTS_STATE 0x02 /* Request to send */ + #define SER2_MCR_OUT_1 0x04 + #define SER2_MCR_OUT_2 0x08 + #define SER2_MCR_LOOPBACK 0x10 +#define SER2_IO_o_LINE_STATUS 0x23D +#define SER2_IO_o_SCRATCH 0x23F + +#define SER2_CLOCK_RATE 115200 diff --git a/sdk/include/reactos/drivers/pc98/sysport.h b/sdk/include/reactos/drivers/pc98/sysport.h new file mode 100644 index 00000000000..8d439517ed8 --- /dev/null +++ b/sdk/include/reactos/drivers/pc98/sysport.h @@ -0,0 +1,82 @@ +/* + * PROJECT: NEC PC-98 series onboard hardware + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Intel 8255A PPI header file + * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +#define PPI_IO_o_PORT_C 0x35 +#define PPI_IO_o_CONTROL 0x37 + +#define PPI_IO_i_PORT_A 0x31 +#define PPI_IO_i_PORT_B 0x33 +#define PPI_IO_i_PORT_C 0x35 + +typedef union _SYSTEM_CONTROL_PORT_A_REGISTER +{ + struct + { + UCHAR DipSw2_1:1; + UCHAR DipSw2_2:1; + UCHAR DipSw2_3:1; + UCHAR DipSw2_4:1; + UCHAR DipSw2_5:1; + UCHAR DipSw2_6:1; + UCHAR DipSw2_7:1; + UCHAR DipSw2_8:1; + }; + UCHAR Bits; +} SYSTEM_CONTROL_PORT_A_REGISTER, *PSYSTEM_CONTROL_PORT_A_REGISTER; + +typedef union _SYSTEM_CONTROL_PORT_B_REGISTER +{ + struct + { + UCHAR RtcData:1; + + /* NMI */ + UCHAR ExtendedMemoryParityCheck:1; + UCHAR MemoryParityCheck:1; + + UCHAR HighResolution:1; + UCHAR Int3:1; + UCHAR DataCarrierDetect:1; + UCHAR ClearToSend:1; + UCHAR RingIndicator:1; + }; + UCHAR Bits; +} SYSTEM_CONTROL_PORT_B_REGISTER, *PSYSTEM_CONTROL_PORT_B_REGISTER; + +typedef union _SYSTEM_CONTROL_PORT_C_REGISTER +{ + struct + { + UCHAR InterruptEnableRxReady:1; + UCHAR InterruptEnableTxEmpty:1; + UCHAR InterruptEnableTxReady:1; + UCHAR Timer1GateToSpeaker:1; + UCHAR Mcke:1; + UCHAR Shut1:1; + UCHAR PrinterStrobeSignal:1; + UCHAR Shut0:1; + }; + UCHAR Bits; +} SYSTEM_CONTROL_PORT_C_REGISTER, *PSYSTEM_CONTROL_PORT_C_REGISTER; + +typedef union _SYSTEM_CONTROL_PORT_REGISTER +{ + struct + { + UCHAR InterruptEnableRxReady:1; + UCHAR InterruptEnableTxEmpty:1; + UCHAR InterruptEnableTxReady:1; + UCHAR Timer1GateToSpeaker:1; + UCHAR Mcke:1; + UCHAR Shut1:1; + UCHAR PrinterStrobeSignal:1; + UCHAR Shut0:1; + }; + UCHAR Bits; +} SYSTEM_CONTROL_PORT_REGISTER, *PSYSTEM_CONTROL_PORT_REGISTER; diff --git a/sdk/lib/cportlib/CMakeLists.txt b/sdk/lib/cportlib/CMakeLists.txt index c0154d27fc0..1ad3ea717fc 100644 --- a/sdk/lib/cportlib/CMakeLists.txt +++ b/sdk/lib/cportlib/CMakeLists.txt @@ -1,3 +1,7 @@ -add_library(cportlib cport.c) +if(SARCH STREQUAL "pc98") + add_library(cportlib cport_pc98.c) +else() + add_library(cportlib cport.c) +endif() add_dependencies(cportlib xdk) diff --git a/sdk/lib/cportlib/cport_pc98.c b/sdk/lib/cportlib/cport_pc98.c new file mode 100644 index 00000000000..0714fd2bccf --- /dev/null +++ b/sdk/lib/cportlib/cport_pc98.c @@ -0,0 +1,598 @@ +/* + * PROJECT: ReactOS ComPort Library for NEC PC-98 series + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Provides a serial port library for KDCOM, INIT, and FREELDR + * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) + */ + +/* Note: ns16550 code from cportlib.c */ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +#define TIMEOUT_COUNT 1024 * 200 + +static struct +{ + PUCHAR Address; + BOOLEAN HasFifo; + BOOLEAN FifoEnabled; + UCHAR RingIndicator; +} Rs232ComPort[] = +{ + { (PUCHAR)0x030, FALSE, FALSE, 0 }, + { (PUCHAR)0x238, FALSE, FALSE, 0 } +}; + +static BOOLEAN IsNekoProject = FALSE; + +/* FUNCTIONS ******************************************************************/ + +static BOOLEAN +CpIsNekoProject(VOID) +{ + UCHAR Input[3] = "NP2"; + UCHAR Output[3]; + UCHAR i; + + for (i = 0; i < 3; i++) + WRITE_PORT_UCHAR((PUCHAR)0x7EF, Input[i]); + + for (i = 0; i < 3; i++) + Output[i] = READ_PORT_UCHAR((PUCHAR)0x7EF); + + return (memcmp(Input, Output, 3) == 0); +} + +static VOID +CpWait(VOID) +{ + UCHAR i; + + for (i = 0; i < 6; i++) + WRITE_PORT_UCHAR((PUCHAR)CPU_IO_o_ARTIC_DELAY, 0); +} + +VOID +NTAPI +CpEnableFifo( + IN PUCHAR Address, + IN BOOLEAN Enable) +{ + /* Set FIFO and clear the receive/transmit buffers */ + if (Address == Rs232ComPort[0].Address && Rs232ComPort[0].HasFifo) + { + if (Enable) + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL, + SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET); + else + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL, SER_FCR_DISABLE); + Rs232ComPort[0].FifoEnabled = Enable; + } + else if (Address == Rs232ComPort[1].Address && Rs232ComPort[1].HasFifo) + { + if (Enable) + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL, + SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET); + else + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL, SER_FCR_DISABLE); + Rs232ComPort[1].FifoEnabled = Enable; + } + CpWait(); +} + +VOID +NTAPI +CpSetBaud( + IN PCPPORT Port, + IN ULONG BaudRate) +{ + UCHAR Lcr; + USHORT Count; + TIMER_CONTROL_PORT_REGISTER TimerControl; + + if (Port->Address == Rs232ComPort[0].Address) + { + if (Rs232ComPort[0].HasFifo) + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DIVISOR_LATCH, SER1_DLR_MODE_LEGACY); + + TimerControl.BcdMode = FALSE; + TimerControl.OperatingMode = PitOperatingMode3; + TimerControl.AccessMode = PitAccessModeLowHigh; + TimerControl.Channel = PitChannel2; + if (IsNekoProject) + { + /* The horrible text input lag happens by about 6 seconds on my PC */ + Count = 3; + } + else + { + Count = (READ_PORT_UCHAR((PUCHAR)0x42) & 0x20) ? + (TIMER_FREQUENCY_1 / (BaudRate * 16)) : (TIMER_FREQUENCY_2 / (BaudRate * 16)); + } + Write8253Timer(TimerControl, Count); + + /* Save baud rate in port */ + Port->BaudRate = BaudRate; + } + else if (Port->Address == Rs232ComPort[1].Address) + { + /* Set the DLAB on */ + Lcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_CONTROL); + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr | SER2_LCR_DLAB); + + /* Set the baud rate */ + Count = SER2_CLOCK_RATE / BaudRate; + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_LSB, Count & 0xFF); + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_MSB, (Count >> 8) & 0xFF); + + /* Reset DLAB */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr & ~SER2_LCR_DLAB); + + /* Save baud rate in port */ + Port->BaudRate = BaudRate; + } +} + +NTSTATUS +NTAPI +CpInitialize( + IN PCPPORT Port, + IN PUCHAR Address, + IN ULONG BaudRate) +{ + SYSTEM_CONTROL_PORT_C_REGISTER SystemControl; + UCHAR FifoStatus; + + if (Port == NULL || Address == NULL || BaudRate == 0) + return STATUS_INVALID_PARAMETER; + + if (!CpDoesPortExist(Address)) + return STATUS_NOT_FOUND; + + /* Initialize port data */ + Port->Address = Address; + Port->BaudRate = 0; + Port->Flags = 0; + + IsNekoProject = CpIsNekoProject(); + + if (Port->Address == Rs232ComPort[0].Address) + { + /* FIFO test */ + FifoStatus = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED; + CpWait(); + Rs232ComPort[0].HasFifo = ((READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED) != FifoStatus); + + /* Disable the interrupts */ + SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_C); + SystemControl.InterruptEnableRxReady = FALSE; + SystemControl.InterruptEnableTxEmpty = FALSE; + SystemControl.InterruptEnableTxReady = FALSE; + WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits); + + /* Turn off FIFO */ + if (Rs232ComPort[0].HasFifo) + CpEnableFifo(Address, FALSE); + + /* Set the baud rate */ + CpSetBaud(Port, BaudRate); + + /* Software reset */ + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0); + CpWait(); + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0); + CpWait(); + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0); + CpWait(); + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, SER1_COMMMAND_IR); + CpWait(); + + /* Mode instruction - asynchronous mode, 8 data bits, 1 stop bit, no parity, 16x clock divisor */ + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, + SER1_MODE_LENGTH_8 | SER1_MODE_1_STOP | SER1_MODE_CLOCKx16); + CpWait(); + + /* Command instruction - transmit enable, turn on DTR and RTS, receive enable, clear error flag */ + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, + SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR | + SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS); + CpWait(); + + /* Disable the interrupts again */ + WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits); + + /* Turn on FIFO */ + if (Rs232ComPort[0].HasFifo) + CpEnableFifo(Address, TRUE); + + /* Read junk out of the data register */ + if (Rs232ComPort[0].HasFifo) + (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER); + else + (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA); + + return STATUS_SUCCESS; + } + else if (Port->Address == Rs232ComPort[1].Address) + { + /* Disable the interrupts */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, 0); + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_INTERRUPT_EN, 0); + + /* Turn on DTR, RTS and OUT2 */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, + SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE | SER2_MCR_OUT_2); + + /* Set the baud rate */ + CpSetBaud(Port, BaudRate); + + /* Set 8 data bits, 1 stop bit, no parity, no break */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, + SER2_LCR_LENGTH_8 | SER2_LCR_ST1 | SER2_LCR_NO_PARITY); + + /* FIFO test */ + Rs232ComPort[1].HasFifo = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_INTERRUPT_ID) & SER2_IIR_HAS_FIFO; + + /* Turn on FIFO */ + if (Rs232ComPort[1].HasFifo) + CpEnableFifo(Address, TRUE); + + /* Read junk out of the RBR */ + (VOID)READ_PORT_UCHAR((PUCHAR)SER2_IO_i_RECEIVER_BUFFER); + + return STATUS_SUCCESS; + } + + return STATUS_NOT_FOUND; +} + +static BOOLEAN +ComPortTest1(IN PUCHAR Address) +{ + /* + * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation" + * Out-of-Band Management Port Device Requirements: + * The device must act as a 16550 or 16450 UART. + * Windows Server 2003 will test this device using the following process: + * 1. Save off the current modem status register. + * 2. Place the UART into diagnostic mode (The UART is placed into loopback mode + * by writing SERIAL_MCR_LOOP to the modem control register). + * 3. The modem status register is read and the high bits are checked. This means + * SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI and SERIAL_MSR_DCD should + * all be clear. + * 4. Place the UART in diagnostic mode and turn on OUTPUT (Loopback Mode and + * OUTPUT are both turned on by writing (SERIAL_MCR_LOOP | SERIAL_MCR_OUT1) + * to the modem control register). + * 5. The modem status register is read and the ring indicator is checked. + * This means SERIAL_MSR_RI should be set. + * 6. Restore original modem status register. + * + * REMARK: Strangely enough, the Virtual PC 2007 virtual machine + * doesn't pass this test. + */ + + BOOLEAN RetVal = FALSE; + UCHAR Mcr, Msr; + + /* Save the Modem Control Register */ + Mcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_CONTROL); + + /* Enable loop (diagnostic) mode (set Bit 4 of the MCR) */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK); + + /* Clear all modem output bits */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK); + + /* Read the Modem Status Register */ + Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS); + + /* + * The upper nibble of the MSR (modem output bits) must be + * equal to the lower nibble of the MCR (modem input bits). + */ + if ((Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD)) == 0x00) + { + /* Set all modem output bits */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, + SER2_MCR_OUT_1 | SER2_MCR_LOOPBACK); // Windows +/* ReactOS + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, + SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE | + SER2_MCR_OUT_1 | SER2_MCR_OUT_2 | SER2_MCR_LOOPBACK); +*/ + + /* Read the Modem Status Register */ + Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS); + + /* + * The upper nibble of the MSR (modem output bits) must be + * equal to the lower nibble of the MCR (modem input bits). + */ + if (Msr & SER_MSR_RI) // Windows + // if (Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD) == 0xF0) // ReactOS + { + RetVal = TRUE; + } + } + + /* Restore the MCR */ + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, Mcr); + + return RetVal; +} + +static BOOLEAN +ComPortTest2(IN PUCHAR Address) +{ + /* + * This test checks whether the 16450/16550 scratch register is available. + * If not, the serial port is considered as unexisting. + */ + + UCHAR Byte = 0; + + do + { + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_SCRATCH, Byte); + + if (READ_PORT_UCHAR((PUCHAR)SER2_IO_i_SCRATCH) != Byte) + return FALSE; + + } + while (++Byte != 0); + + return TRUE; +} + +BOOLEAN +NTAPI +CpDoesPortExist(IN PUCHAR Address) +{ + UCHAR Data, Status; + + if (Address == Rs232ComPort[0].Address || Address == (PUCHAR)0x41) + { + Data = READ_PORT_UCHAR(Address); + Status = READ_PORT_UCHAR(Address + 2); + if ((Data & Status) == 0xFF || (Data | Status) == 0x00) + return FALSE; + else + return TRUE; + } + else if (Address == Rs232ComPort[1].Address) + { + return (ComPortTest1(Address) || ComPortTest2(Address)); + } + + return FALSE; +} + +UCHAR +NTAPI +CpReadLsr( + IN PCPPORT Port, + IN UCHAR ExpectedValue) +{ + UCHAR Lsr, Msr; + SYSTEM_CONTROL_PORT_B_REGISTER SystemControl; + + if (Port->Address == Rs232ComPort[0].Address) + { + /* Read the LSR and check if the expected value is present */ + if (Rs232ComPort[0].HasFifo) + { + Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_LINE_STATUS); + if (!(Lsr & ExpectedValue)) + { + Msr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_MODEM_STATUS); + + /* If the ring indicator reaches 3, we've seen this on/off twice */ + Rs232ComPort[0].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2; + if (Rs232ComPort[0].RingIndicator == 3) + Port->Flags |= CPPORT_FLAG_MODEM_CONTROL; + } + } + else + { + Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_STATUS); + if (!(Lsr & ExpectedValue)) + { + SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_B); + + /* If the ring indicator reaches 3, we've seen this on/off twice */ + Rs232ComPort[0].RingIndicator |= SystemControl.RingIndicator ? 1 : 2; + if (Rs232ComPort[0].RingIndicator == 3) + Port->Flags |= CPPORT_FLAG_MODEM_CONTROL; + } + } + + return Lsr; + } + else if (Port->Address == Rs232ComPort[1].Address) + { + /* Read the LSR and check if the expected value is present */ + Lsr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_STATUS); + if (!(Lsr & ExpectedValue)) + { + Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS); + + /* If the indicator reaches 3, we've seen this on/off twice */ + Rs232ComPort[1].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2; + if (Rs232ComPort[1].RingIndicator == 3) + Port->Flags |= CPPORT_FLAG_MODEM_CONTROL; + } + + return Lsr; + } + + return 0; +} + +USHORT +NTAPI +CpGetByte( + IN PCPPORT Port, + OUT PUCHAR Byte, + IN BOOLEAN Wait, + IN BOOLEAN Poll) +{ + UCHAR Lsr; + ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1; + UCHAR SuccessFlags, ErrorFlags; + BOOLEAN FifoEnabled; + + /* Handle early read-before-init */ + if (!Port->Address) + return CP_GET_NODATA; + + if (Port->Address == Rs232ComPort[0].Address) + { + SuccessFlags = Rs232ComPort[0].HasFifo ? SER1_LSR_RxRDY : SER1_STATUS_RxRDY; + ErrorFlags = Rs232ComPort[0].HasFifo ? (SER1_LSR_PE | SER1_LSR_OE) : + (SER1_STATUS_FE | SER1_STATUS_PE | SER1_STATUS_OE); + + /* If "wait" mode enabled, spin many times, otherwise attempt just once */ + while (LimitCount--) + { + /* Read LSR for data ready */ + Lsr = CpReadLsr(Port, SuccessFlags); + if (Lsr & SuccessFlags) + { + /* If an error happened, clear the byte and fail */ + if (Lsr & ErrorFlags) + { + /* Save the last FIFO state */ + FifoEnabled = Rs232ComPort[0].FifoEnabled; + + /* Turn off FIFO */ + if (FifoEnabled) + CpEnableFifo(Port->Address, FALSE); + + /* Clear error flag */ + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, + SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR | + SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS); + + /* Turn on FIFO */ + if (FifoEnabled) + CpEnableFifo(Port->Address, TRUE); + + *Byte = 0; + return CP_GET_ERROR; + } + + /* If only polling was requested by caller, return now */ + if (Poll) + return CP_GET_SUCCESS; + + /* Otherwise read the byte and return it */ + if (Rs232ComPort[0].HasFifo) + *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER); + else + *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA); + + /* TODO: Handle CD if port is in modem control mode */ + + /* Byte was read */ + return CP_GET_SUCCESS; + } + else if (IsNekoProject && Rs232ComPort[0].HasFifo) + { + /* + * Neko Project 21/W doesn't set RxRDY without reading any data from 0x136. + * TODO: Check real hardware behavior. + */ + (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID); + } + } + + /* Reset LSR, no data was found */ + CpReadLsr(Port, 0); + } + else if (Port->Address == Rs232ComPort[1].Address) + { + /* If "wait" mode enabled, spin many times, otherwise attempt just once */ + while (LimitCount--) + { + /* Read LSR for data ready */ + Lsr = CpReadLsr(Port, SER2_LSR_DR); + if ((Lsr & SER2_LSR_DR) == SER2_LSR_DR) + { + /* If an error happened, clear the byte and fail */ + if (Lsr & (SER2_LSR_FE | SER2_LSR_PE | SER2_LSR_OE)) + { + *Byte = 0; + return CP_GET_ERROR; + } + + /* If only polling was requested by caller, return now */ + if (Poll) + return CP_GET_SUCCESS; + + /* Otherwise read the byte and return it */ + *Byte = READ_PORT_UCHAR((UCHAR)SER2_IO_i_RECEIVER_BUFFER); + + /* TODO: Handle CD if port is in modem control mode */ + + /* Byte was read */ + return CP_GET_SUCCESS; + } + } + + /* Reset LSR, no data was found */ + CpReadLsr(Port, 0); + } + + return CP_GET_NODATA; +} + +VOID +NTAPI +CpPutByte( + IN PCPPORT Port, + IN UCHAR Byte) +{ + if (Port->Address == Rs232ComPort[0].Address) + { + /* TODO: Check if port is in modem control to handle CD */ + + if (Rs232ComPort[0].HasFifo) + { + while ((CpReadLsr(Port, SER1_LSR_TxRDY) & SER1_LSR_TxRDY) == 0) + NOTHING; + + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_TRANSMITTER_BUFFER, Byte); + } + else + { + while ((CpReadLsr(Port, SER1_STATUS_TxRDY) & SER1_STATUS_TxRDY) == 0) + NOTHING; + + WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DATA, Byte); + } + } + else if (Port->Address == Rs232ComPort[1].Address) + { + /* TODO: Check if port is in modem control to handle CD */ + + while ((CpReadLsr(Port, SER2_LSR_THR_EMPTY) & SER2_LSR_THR_EMPTY) == 0) + NOTHING; + + WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_TRANSMITTER_BUFFER, Byte); + } +}