diff --git a/reactos/include/reactos/libs/cportlib/cportlib.h b/reactos/include/reactos/libs/cportlib/cportlib.h index 31b76158bc6..b7681f7d337 100644 --- a/reactos/include/reactos/libs/cportlib/cportlib.h +++ b/reactos/include/reactos/libs/cportlib/cportlib.h @@ -10,26 +10,25 @@ #include +// +// Return error codes. +// #define CP_GET_SUCCESS 0 #define CP_GET_NODATA 1 #define CP_GET_ERROR 2 +// +// COM port flags. +// #define CPPORT_FLAG_MODEM_CONTROL 0x02 + typedef struct _CPPORT { PUCHAR Address; - ULONG Baud; + ULONG BaudRate; USHORT Flags; } CPPORT, *PCPPORT; -VOID -NTAPI -CpInitialize( - IN PCPPORT Port, - IN PUCHAR Address, - IN ULONG Rate -); - VOID NTAPI CpEnableFifo( @@ -37,6 +36,21 @@ CpEnableFifo( IN BOOLEAN Enable ); +VOID +NTAPI +CpSetBaud( + IN PCPPORT Port, + IN ULONG BaudRate +); + +NTSTATUS +NTAPI +CpInitialize( + IN PCPPORT Port, + IN PUCHAR Address, + IN ULONG BaudRate +); + BOOLEAN NTAPI CpDoesPortExist( @@ -50,20 +64,13 @@ CpReadLsr( IN UCHAR ExpectedValue ); -VOID -NTAPI -CpSetBaud( - IN PCPPORT Port, - IN ULONG Rate -); - USHORT NTAPI CpGetByte( - IN PCPPORT Port, - IN PUCHAR Byte, - IN BOOLEAN Wait, - IN BOOLEAN Poll + IN PCPPORT Port, + OUT PUCHAR Byte, + IN BOOLEAN Wait, + IN BOOLEAN Poll ); VOID diff --git a/reactos/lib/cportlib/cport.c b/reactos/lib/cportlib/cport.c index 97d281f71ce..7279b69893b 100644 --- a/reactos/lib/cportlib/cport.c +++ b/reactos/lib/cportlib/cport.c @@ -11,6 +11,10 @@ * documented their serial algorithms, we use the same ones to stay "compliant". * Do not change this code to "improve" it. It's done this way on purpose, at least on x86. * -- sir_richard + * + * REPLY: I reworked the COM-port testing code because the original one + * (i.e. the Microsoft's documented one) doesn't work on Virtual PC 2007. + * -- hbelusca */ /* NOTE: This code is used by Headless Support (Ntoskrnl.exe and Osloader.exe) and @@ -30,47 +34,100 @@ #include #include #include +#include + +#define NDEBUG #include /* GLOBALS ********************************************************************/ +// Wait timeout value +#define TIMEOUT_COUNT 1024 * 200 + UCHAR RingIndicator; + /* FUNCTIONS ******************************************************************/ -VOID -NTAPI -CpInitialize(IN PCPPORT Port, - IN PUCHAR Address, - IN ULONG Rate) -{ - /* Reset port data */ - Port->Address = Address; - Port->Baud = 0; - - /* Set the baud rate */ - CpSetBaud(Port, Rate); - - /* Enable on DTR and RTS */ - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, - SERIAL_MCR_DTR | SERIAL_MCR_RTS); - - /* Disable interrupts */ - WRITE_PORT_UCHAR(Address + INTERRUPT_ENABLE_REGISTER, 0); -} - VOID NTAPI CpEnableFifo(IN PUCHAR Address, IN BOOLEAN Enable) { - /* Set FIFO */ - WRITE_PORT_UCHAR(Address + FIFO_CONTROL_REGISTER, Enable ? SERIAL_FCR_ENABLE : 0); + /* Set FIFO and clear the receive/transmit buffers */ + WRITE_PORT_UCHAR(Address + FIFO_CONTROL_REGISTER, + Enable ? SERIAL_FCR_ENABLE | SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET + : SERIAL_FCR_DISABLE); } -BOOLEAN +VOID NTAPI -CpDoesPortExist(IN PUCHAR Address) +CpSetBaud(IN PCPPORT Port, + IN ULONG BaudRate) +{ + UCHAR Lcr; + ULONG Mode = CLOCK_RATE / BaudRate; + + /* Set the DLAB on */ + Lcr = READ_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER); + WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, Lcr | SERIAL_LCR_DLAB); + + /* Set the baud rate */ + WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_LSB, (UCHAR)(Mode & 0xFF)); + WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_MSB, (UCHAR)((Mode >> 8) & 0xFF)); + + /* Reset DLAB */ + WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, Lcr); + + /* Save baud rate in port */ + Port->BaudRate = BaudRate; +} + +NTSTATUS +NTAPI +CpInitialize(IN PCPPORT Port, + IN PUCHAR Address, + IN ULONG BaudRate) +{ + /* Validity checks */ + 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; + + /* Disable the interrupts */ + WRITE_PORT_UCHAR(Address + LINE_CONTROL_REGISTER, 0); + WRITE_PORT_UCHAR(Address + INTERRUPT_ENABLE_REGISTER, 0); + + /* Turn on DTR, RTS and OUT2 */ + WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, + SERIAL_MCR_DTR | SERIAL_MCR_RTS | SERIAL_MCR_OUT2); + + /* Set the baud rate */ + CpSetBaud(Port, BaudRate); + + /* Set 8 data bits, 1 stop bit, no parity, no break */ + WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, + SERIAL_8_DATA | SERIAL_1_STOP | SERIAL_NONE_PARITY); + + /* Turn on FIFO */ + // TODO: Check whether FIFO exists and turn it on in that case. + CpEnableFifo(Address, TRUE); // for 16550 + + /* Read junk out of the RBR */ + (VOID)READ_PORT_UCHAR(Address + RECEIVE_BUFFER_REGISTER); + + return STATUS_SUCCESS; +} + +static BOOLEAN +ComPortTest1(IN PUCHAR Address) { /* * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation" @@ -89,28 +146,87 @@ CpDoesPortExist(IN PUCHAR Address) * 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. */ - UCHAR Old = READ_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER); + BOOLEAN RetVal = FALSE; + UCHAR Mcr, Msr; - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, SERIAL_MCR_LOOP); + /* Save the Modem Control Register */ + Mcr = READ_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER); + + /* Enable loop (diagnostic) mode (set Bit 4 of the MCR) */ WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, SERIAL_MCR_LOOP); - if (!(READ_PORT_UCHAR(Address + MODEM_STATUS_REGISTER) & - (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_DCD))) + /* Clear all modem output bits */ + WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, SERIAL_MCR_LOOP); + + /* Read the Modem Status Register */ + Msr = READ_PORT_UCHAR(Address + MODEM_STATUS_REGISTER); + + /* + * The upper nibble of the MSR (modem output bits) must be + * equal to the lower nibble of the MCR (modem input bits). + */ + if ((Msr & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_DCD)) == 0x00) { + /* Set all modem output bits */ WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, - (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP)); - if (READ_PORT_UCHAR(Address + MODEM_STATUS_REGISTER) & SERIAL_MSR_RI) + SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP); // Windows +/* ReactOS + WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, + SERIAL_MCR_DTR | SERIAL_MCR_RTS | SERIAL_MCR_OUT1 | SERIAL_MCR_OUT2 | SERIAL_MCR_LOOP); +*/ + + /* Read the Modem Status Register */ + Msr = READ_PORT_UCHAR(Address + MODEM_STATUS_REGISTER); + + /* + * The upper nibble of the MSR (modem output bits) must be + * equal to the lower nibble of the MCR (modem input bits). + */ + if (Msr & SERIAL_MSR_RI) // Windows + // if (Msr & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_DCD) == 0xF0) // ReactOS { - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, Old); - return TRUE; + RetVal = TRUE; } } - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, Old); + /* Restore the MCR */ + WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, Mcr); - return FALSE; + 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(Address + SCRATCH_REGISTER, Byte); + + if (READ_PORT_UCHAR(Address + SCRATCH_REGISTER) != Byte) + return FALSE; + + } while (++Byte != 0); + + return TRUE; +} + +BOOLEAN +NTAPI +CpDoesPortExist(IN PUCHAR Address) +{ + return ( ComPortTest1(Address) || ComPortTest2(Address) ); } UCHAR @@ -135,54 +251,28 @@ CpReadLsr(IN PCPPORT Port, return Lsr; } -VOID -NTAPI -CpSetBaud(IN PCPPORT Port, - IN ULONG Rate) -{ - UCHAR Lcr; - USHORT Mode; - - /* Add DLAB */ - Lcr = READ_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER); - WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, Lcr | SERIAL_LCR_DLAB); - - /* Set baud rate */ - Mode = (USHORT)(115200 / Rate); - WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_MSB, (UCHAR)((Mode >> 8) & 0xff)); - WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_LSB, (UCHAR)(Mode & 0xff)); - - /* Reset DLAB and set 8 data bits, 1 stop bit, no parity, no break */ - WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, - SERIAL_8_DATA | SERIAL_1_STOP | SERIAL_NONE_PARITY); - - /* Save baud rate in port */ - Port->Baud = Rate; -} - USHORT NTAPI -CpGetByte(IN PCPPORT Port, - IN PUCHAR Byte, - IN BOOLEAN Wait, - IN BOOLEAN Poll) +CpGetByte(IN PCPPORT Port, + OUT PUCHAR Byte, + IN BOOLEAN Wait, + IN BOOLEAN Poll) { UCHAR Lsr; - ULONG i; + ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1; /* Handle early read-before-init */ if (!Port->Address) return CP_GET_NODATA; /* If "wait" mode enabled, spin many times, otherwise attempt just once */ - i = Wait ? 204800 : 1; - while (i--) + while (LimitCount--) { /* Read LSR for data ready */ Lsr = CpReadLsr(Port, SERIAL_LSR_DR); if ((Lsr & SERIAL_LSR_DR) == SERIAL_LSR_DR) { /* If an error happened, clear the byte and fail */ - if (Lsr & (SERIAL_LSR_FE | SERIAL_LSR_PE)) + if (Lsr & (SERIAL_LSR_FE | SERIAL_LSR_PE | SERIAL_LSR_OE)) { *Byte = 0; return CP_GET_ERROR; @@ -217,17 +307,18 @@ CpPutByte(IN PCPPORT Port, IN UCHAR Byte) { /* Check if port is in modem control to handle CD */ - while (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) + // while (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) // Commented for the moment. + if (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) // To be removed when this becomes implemented. { /* Not implemented yet */ DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n"); } /* Wait for LSR to say we can go ahead */ - while (!(CpReadLsr(Port, SERIAL_LSR_THRE) & SERIAL_LSR_THRE)); + while ((CpReadLsr(Port, SERIAL_LSR_THRE) & SERIAL_LSR_THRE) == 0x00); /* Send the byte */ - WRITE_PORT_UCHAR(Port->Address + RECEIVE_BUFFER_REGISTER, Byte); + WRITE_PORT_UCHAR(Port->Address + TRANSMIT_HOLDING_REGISTER, Byte); } /* EOF */