From: Hermès Bélusca-Maïto Date: Wed, 28 Nov 2012 22:12:29 +0000 (+0000) Subject: [CPORTLIB] X-Git-Tag: backups/ros-csrss@60644~118^2~7 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=da43d9219e63efea1db7e1b60040dc323059a616 [CPORTLIB] - Do a synthesis of all the code I've found concerning initializing COM ports (in kdcom / kddll / kdbg in the kernel and in freeldr). The "// Windows" comments concern the original code for testing the existence of COM ports, inside cportlib, whereas "// ReactOS" comments concern the equivalent existing in our kdcom/kddll/kdbg etc... - CORRECT THE VIRTUAL PC COM PORT INITIALIZATION !! The problem was, that the current COM port existence test fails on VPC whereas it works well in all other virtual machines. Therefore : * I keep the existing testing code in a function called ComPortTest1, this works everywhere but on VPC ; * I add a very basic COM port existence test function called ComPortTest2, which basically looks for the scratch register, this works everywhere including on VPC. * The COM port existence test consists in the two tests above. svn path=/trunk/; revision=57776 --- 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,31 +10,45 @@ #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, +CpEnableFifo( IN PUCHAR Address, - IN ULONG Rate + IN BOOLEAN Enable ); VOID NTAPI -CpEnableFifo( +CpSetBaud( + IN PCPPORT Port, + IN ULONG BaudRate +); + +NTSTATUS +NTAPI +CpInitialize( + IN PCPPORT Port, IN PUCHAR Address, - IN BOOLEAN Enable + IN ULONG BaudRate ); BOOLEAN @@ -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) +CpEnableFifo(IN PUCHAR Address, + IN BOOLEAN Enable) { - /* Reset port data */ - Port->Address = Address; - Port->Baud = 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); +} + +VOID +NTAPI +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 */ - CpSetBaud(Port, Rate); + WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_LSB, (UCHAR)(Mode & 0xFF)); + WRITE_PORT_UCHAR(Port->Address + DIVISOR_LATCH_MSB, (UCHAR)((Mode >> 8) & 0xFF)); - /* Enable on DTR and RTS */ - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, - SERIAL_MCR_DTR | SERIAL_MCR_RTS); + /* Reset DLAB */ + WRITE_PORT_UCHAR(Port->Address + LINE_CONTROL_REGISTER, Lcr); - /* Disable interrupts */ - WRITE_PORT_UCHAR(Address + INTERRUPT_ENABLE_REGISTER, 0); + /* Save baud rate in port */ + Port->BaudRate = BaudRate; } -VOID +NTSTATUS NTAPI -CpEnableFifo(IN PUCHAR Address, - IN BOOLEAN Enable) +CpInitialize(IN PCPPORT Port, + IN PUCHAR Address, + IN ULONG BaudRate) { - /* Set FIFO */ - WRITE_PORT_UCHAR(Address + FIFO_CONTROL_REGISTER, Enable ? SERIAL_FCR_ENABLE : 0); + /* 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; } -BOOLEAN -NTAPI -CpDoesPortExist(IN PUCHAR Address) +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; + /* 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); + + /* Clear all modem output bits */ 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))) + /* 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); // Windows +/* ReactOS 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_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 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; +} - return FALSE; +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 */