X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=lib%2Fcportlib%2Fcport.c;h=4cad5b58934214f6ac9715233e15fa8642dbb47b;hp=75a4c03ce94c2a0971b227865021dc85501d00d2;hb=8e37700e5c527418228fa2269adc06c7ebcebcb9;hpb=a070e2aaf24b3fb8746916053ac8f30bf26dbd1d diff --git a/lib/cportlib/cport.c b/lib/cportlib/cport.c index 75a4c03ce94..4cad5b58934 100644 --- a/lib/cportlib/cport.c +++ b/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 @@ -20,110 +24,226 @@ /* NOTE: The original code supports Modem Control. We currently do not */ /* FIXMEs: - - Make this serial-port specific (NS16550 vs other serial port types) - - Get x64 KDCOM, KDBG, FREELDR, and other current code to use this + - Make this serial-port specific (NS16550 vs other serial port types) + - Get x64 KDCOM, KDBG, FREELDR, and other current code to use this */ - + /* INCLUDES *******************************************************************/ #include #include #include #include +#include + +#define NDEBUG #include /* GLOBALS ********************************************************************/ +// Wait timeout value +#define TIMEOUT_COUNT 1024 * 200 + UCHAR RingIndicator; + /* FUNCTIONS ******************************************************************/ VOID NTAPI +CpEnableFifo(IN PUCHAR Address, + IN BOOLEAN Enable) +{ + /* 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 */ + 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 Rate) + IN PUCHAR Address, + IN ULONG BaudRate) { - /* Reset port data */ - Port->Address = Address; - Port->Baud = 0; + /* Validity checks */ + if (Port == NULL || Address == NULL || BaudRate == 0) + return STATUS_INVALID_PARAMETER; - /* Set the baud rate */ - CpSetBaud(Port, Rate); + if (!CpDoesPortExist(Address)) + return STATUS_NOT_FOUND; - /* Enable on DTR and RTS */ - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, - SERIAL_MCR_DTR | SERIAL_MCR_RTS); + /* Initialize port data */ + Port->Address = Address; + Port->BaudRate = 0; + Port->Flags = 0; - /* Disable interrupts */ + /* 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; } -VOID -NTAPI -CpEnableFifo(IN PUCHAR Address, - IN BOOLEAN Enable) +static BOOLEAN +ComPortTest1(IN PUCHAR Address) { - /* Set FIFO */ - WRITE_PORT_UCHAR(Address + FIFO_CONTROL_REGISTER, Enable ? SERIAL_FCR_ENABLE : 0); + /* + * 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(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); + + /* 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_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 + { + RetVal = TRUE; + } + } + + /* 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; } BOOLEAN NTAPI CpDoesPortExist(IN PUCHAR Address) { - UCHAR Old; - /* - * 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. - */ - Old = READ_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER); - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, SERIAL_MCR_LOOP); - 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))) - { - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, - (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP)); - if (READ_PORT_UCHAR(Address + MODEM_STATUS_REGISTER) & SERIAL_MSR_RI) - { - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, Old); - return TRUE; - } - } - WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER, Old); - return FALSE; + return ( ComPortTest1(Address) || ComPortTest2(Address) ); } UCHAR NTAPI CpReadLsr(IN PCPPORT Port, - IN UCHAR ExpectedValue) + IN UCHAR ExpectedValue) { - UCHAR Lsr, Msr; + UCHAR Lsr, Msr; - /* Read the LSR and check if the expected value is present */ + /* Read the LSR and check if the expected value is present */ Lsr = READ_PORT_UCHAR(Port->Address + LINE_STATUS_REGISTER); if (!(Lsr & ExpectedValue)) - { - /* Check the MSR for ring indicator toggle */ + { + /* Check the MSR for ring indicator toggle */ Msr = READ_PORT_UCHAR(Port->Address + MODEM_STATUS_REGISTER); - /* If the indicator reaches 3, we've seen this on/off twice */ + /* If the indicator reaches 3, we've seen this on/off twice */ RingIndicator |= (Msr & SERIAL_MSR_RI) ? 1 : 2; if (RingIndicator == 3) Port->Flags |= CPPORT_FLAG_MODEM_CONTROL; } @@ -131,78 +251,52 @@ CpReadLsr(IN PCPPORT Port, return Lsr; } -VOID +USHORT NTAPI -CpSetBaud(IN PCPPORT Port, - IN ULONG Rate) +CpGetByte(IN PCPPORT Port, + OUT PUCHAR Byte, + IN BOOLEAN Wait, + IN BOOLEAN Poll) { - 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); + UCHAR Lsr; + ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1; - /* Set baud rate */ - Mode = 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)); + /* Handle early read-before-init */ + if (!Port->Address) return CP_GET_NODATA; - /* 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) -{ - UCHAR Lsr; - ULONG i; - - /* 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--) - { - /* Read LSR for data ready */ + /* If "wait" mode enabled, spin many times, otherwise attempt just once */ + 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 an error happened, clear the byte and fail */ + if (Lsr & (SERIAL_LSR_FE | SERIAL_LSR_PE | SERIAL_LSR_OE)) + { *Byte = 0; return CP_GET_ERROR; } - /* If only polling was requested by caller, return now */ + /* If only polling was requested by caller, return now */ if (Poll) return CP_GET_SUCCESS; - /* Otherwise read the byte and return it */ + /* Otherwise read the byte and return it */ *Byte = READ_PORT_UCHAR(Port->Address + RECEIVE_BUFFER_REGISTER); - /* Handle CD if port is in modem control mode */ + /* Handle CD if port is in modem control mode */ if (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) - { - /* Not implemented yet */ - DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n"); + { + /* Not implemented yet */ + DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n"); } - /* Byte was read */ + /* Byte was read */ return CP_GET_SUCCESS; } } - /* Reset LSR, no data was found */ + /* Reset LSR, no data was found */ CpReadLsr(Port, 0); return CP_GET_NODATA; } @@ -210,18 +304,21 @@ CpGetByte(IN PCPPORT Port, VOID NTAPI CpPutByte(IN PCPPORT Port, - IN UCHAR Byte) + IN UCHAR Byte) { - /* Check if port is in modem control to handle CD */ - while (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) - { - /* Not implemented yet */ - DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n"); + /* Check if port is in modem control to handle CD */ + // 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)); + /* Wait for LSR to say we can go ahead */ + 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 */