[CPORTLIB]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 28 Nov 2012 22:12:29 +0000 (22:12 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 28 Nov 2012 22:12:29 +0000 (22:12 +0000)
- 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

reactos/include/reactos/libs/cportlib/cportlib.h
reactos/lib/cportlib/cport.c

index 31b7615..b7681f7 100644 (file)
 
 #include <ntdef.h>
 
+//
+// 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
index 97d281f..7279b69 100644 (file)
  * 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
 #include <drivers/serial/ns16550.h>
 #include <intrin.h>
 #include <ioaccess.h>
+#include <ntstatus.h>
+
+#define NDEBUG
 #include <debug.h>
 
 /* 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 */