Patch by Anton Yarotsky:
authorSir Richard <sir_richard@svn.reactos.org>
Fri, 10 Sep 2010 05:22:48 +0000 (05:22 +0000)
committerSir Richard <sir_richard@svn.reactos.org>
Fri, 10 Sep 2010 05:22:48 +0000 (05:22 +0000)
[CPORTLIB]: Implement Windows' Cp (ComPort) library. See Notes in C file. Not yet used. Based on MS Whitepaper: Building Hardware and Firmware to Complement Microsoft Windows Headless Operation.

svn path=/trunk/; revision=48727

reactos/include/reactos/drivers/serial/ns16550.h [new file with mode: 0644]
reactos/include/reactos/libs/cportlib/cportlib.h [new file with mode: 0644]
reactos/lib/cportlib/cport.c [new file with mode: 0644]
reactos/lib/cportlib/cportlib.rbuild [new file with mode: 0644]
reactos/lib/lib.rbuild

diff --git a/reactos/include/reactos/drivers/serial/ns16550.h b/reactos/include/reactos/drivers/serial/ns16550.h
new file mode 100644 (file)
index 0000000..43f58c5
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * PROJECT:         ReactOS ComPort Library
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
+ * FILE:            include/reactos/drivers/serial/ns16550.h
+ * PURPOSE:         Header for National Semiconductor 16550 UART
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ */
+
+/* INCLUDES *******************************************************************/
+
+#pragma once
+
+/* Note: These definitions are the internal definitions used by Microsoft serial
+   driver (see src/kernel/serial.h in Windows sources). Linux uses its own, as
+   do most other OS.
+*/
+
+#if !defined(SERIAL_REGISTER_STRIDE)
+#define SERIAL_REGISTER_STRIDE 1
+#endif
+
+#define RECEIVE_BUFFER_REGISTER    ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define TRANSMIT_HOLDING_REGISTER  ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_ENABLE_REGISTER  ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_IDENT_REGISTER   ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define FIFO_CONTROL_REGISTER      ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define LINE_CONTROL_REGISTER      ((ULONG)((0x03)*SERIAL_REGISTER_STRIDE))
+#define MODEM_CONTROL_REGISTER     ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
+#define LINE_STATUS_REGISTER       ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE))
+#define MODEM_STATUS_REGISTER      ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_LSB          ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_MSB          ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define SERIAL_REGISTER_SPAN       ((ULONG)(7*SERIAL_REGISTER_STRIDE))
+#define SERIAL_STATUS_LENGTH       ((ULONG)(1*SERIAL_REGISTER_STRIDE))
+
+#define SERIAL_DATA_LENGTH_5 0x00
+#define SERIAL_DATA_LENGTH_6 0x01
+#define SERIAL_DATA_LENGTH_7 0x02
+#define SERIAL_DATA_LENGTH_8 0x03
+
+#define SERIAL_IER_RDA   0x01
+#define SERIAL_IER_THR   0x02
+#define SERIAL_IER_RLS   0x04
+#define SERIAL_IER_MS    0x08
+
+#define SERIAL_IIR_RLS      0x06
+#define SERIAL_IIR_RDA      0x04
+#define SERIAL_IIR_CTI      0x0c
+#define SERIAL_IIR_THR      0x02
+#define SERIAL_IIR_MS       0x00
+#define SERIAL_IIR_FIFOS_ENABLED 0xc0
+#define SERIAL_IIR_NO_INTERRUPT_PENDING 0x01
+#define SERIAL_IIR_MUST_BE_ZERO 0x30
+
+#define SERIAL_FCR_ENABLE     ((UCHAR)0x01)
+#define SERIAL_FCR_RCVR_RESET ((UCHAR)0x02)
+#define SERIAL_FCR_TXMT_RESET ((UCHAR)0x04)
+
+#define SERIAL_1_BYTE_HIGH_WATER   ((UCHAR)0x00)
+#define SERIAL_4_BYTE_HIGH_WATER   ((UCHAR)0x40)
+#define SERIAL_8_BYTE_HIGH_WATER   ((UCHAR)0x80)
+#define SERIAL_14_BYTE_HIGH_WATER  ((UCHAR)0xc0)
+
+#define SERIAL_LCR_DLAB     0x80
+#define SERIAL_LCR_BREAK    0x40
+
+#define SERIAL_5_DATA       ((UCHAR)0x00)
+#define SERIAL_6_DATA       ((UCHAR)0x01)
+#define SERIAL_7_DATA       ((UCHAR)0x02)
+#define SERIAL_8_DATA       ((UCHAR)0x03)
+#define SERIAL_DATA_MASK    ((UCHAR)0x03)
+
+#define SERIAL_1_STOP       ((UCHAR)0x00)
+#define SERIAL_1_5_STOP     ((UCHAR)0x04) // Only valid for 5 data bits
+#define SERIAL_2_STOP       ((UCHAR)0x04) // Not valid for 5 data bits
+#define SERIAL_STOP_MASK    ((UCHAR)0x04)
+
+#define SERIAL_NONE_PARITY  ((UCHAR)0x00)
+#define SERIAL_ODD_PARITY   ((UCHAR)0x08)
+#define SERIAL_EVEN_PARITY  ((UCHAR)0x18)
+#define SERIAL_MARK_PARITY  ((UCHAR)0x28)
+#define SERIAL_SPACE_PARITY ((UCHAR)0x38)
+#define SERIAL_PARITY_MASK  ((UCHAR)0x38)
+
+#define SERIAL_MCR_DTR            0x01
+#define SERIAL_MCR_RTS            0x02
+#define SERIAL_MCR_OUT1           0x04
+#define SERIAL_MCR_OUT2           0x08
+#define SERIAL_MCR_LOOP           0x10
+#define SERIAL_MCR_TL16C550CAFE   0x20
+
+#define SERIAL_LSR_DR       0x01
+#define SERIAL_LSR_OE       0x02
+#define SERIAL_LSR_PE       0x04
+#define SERIAL_LSR_FE       0x08
+#define SERIAL_LSR_BI       0x10
+#define SERIAL_LSR_THRE     0x20
+#define SERIAL_LSR_TEMT     0x40
+#define SERIAL_LSR_FIFOERR  0x80
+
+#define SERIAL_MSR_DCTS     0x01
+#define SERIAL_MSR_DDSR     0x02
+#define SERIAL_MSR_TERI     0x04
+#define SERIAL_MSR_DDCD     0x08
+#define SERIAL_MSR_CTS      0x10
+#define SERIAL_MSR_DSR      0x20
+#define SERIAL_MSR_RI       0x40
+#define SERIAL_MSR_DCD      0x80
diff --git a/reactos/include/reactos/libs/cportlib/cportlib.h b/reactos/include/reactos/libs/cportlib/cportlib.h
new file mode 100644 (file)
index 0000000..be64007
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * PROJECT:         ReactOS ComPort Library
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
+ * FILE:            include/reactos/lib/cportlib/cportlib.h
+ * PURPOSE:         Header for the ComPort Library
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <ntdef.h>
+
+#define CP_GET_SUCCESS  0
+#define CP_GET_NODATA   1
+#define CP_GET_ERROR    2
+
+#define CPPORT_FLAG_MODEM_CONTROL      0x02
+typedef struct _CPPORT
+{
+       PUCHAR Address;
+       ULONG Baud;
+       USHORT Flags;
+} CPPORT, *PCPPORT;
+       
+VOID
+NTAPI
+CpInitialize(
+       IN PCPPORT Port,
+       IN PUCHAR Address,
+       IN ULONG Rate
+       );
+
+VOID
+NTAPI
+CpEnableFifo(
+       IN PUCHAR Address,
+       IN BOOLEAN Enable
+       );
+
+BOOLEAN
+NTAPI
+CpDoesPortExist(
+       IN PUCHAR Address
+       );
+       
+UCHAR
+NTAPI
+CpReadLsr(
+       IN PCPPORT Port,
+       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
+       );
+       
+VOID
+NTAPI
+CpPutByte(
+       IN PCPPORT Port,
+       IN UCHAR Byte
+       );
diff --git a/reactos/lib/cportlib/cport.c b/reactos/lib/cportlib/cport.c
new file mode 100644 (file)
index 0000000..75a4c03
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * PROJECT:         ReactOS ComPort Library
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
+ * FILE:            lib/reactos/cportlib/cport.c
+ * PURPOSE:         Provides a serial port library for KDCOM, INIT, and FREELDR
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ */
+
+/* NOTE: This library follows the precise serial port intialization steps documented
+ * by Microsoft in some of their Server hardware guidance. Because they've clearly
+ * 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
+ */
+
+/* NOTE: This code is used by Headless Support (Ntoskrnl.exe and Osloader.exe) and
+   Kdcom.dll in Windows. It may be that WinDBG depends on some of these quirks.
+*/
+
+/* 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
+*/
+       
+/* INCLUDES *******************************************************************/
+
+#include <cportlib/cportlib.h>
+#include <drivers/serial/ns16550.h>
+#include <intrin.h>
+#include <ioaccess.h>
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+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);
+}
+
+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;
+}
+
+UCHAR
+NTAPI
+CpReadLsr(IN PCPPORT Port,
+         IN UCHAR ExpectedValue)
+{
+       UCHAR Lsr, Msr;
+
+       /* 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 */
+        Msr = READ_PORT_UCHAR(Port->Address + MODEM_STATUS_REGISTER);
+
+               /* 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;
+    }
+
+    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 = 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)
+{
+       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 */
+        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))
+                       {
+                *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(Port->Address + RECEIVE_BUFFER_REGISTER);
+
+                       /* 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");
+            }
+
+                       /* 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)
+{
+       /* 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");
+    }
+
+       /* Wait for LSR to say we can go ahead */
+    while (!(CpReadLsr(Port, SERIAL_LSR_THRE) & SERIAL_LSR_THRE));
+
+    /* Send the byte */
+    WRITE_PORT_UCHAR(Port->Address + RECEIVE_BUFFER_REGISTER, Byte);
+}
diff --git a/reactos/lib/cportlib/cportlib.rbuild b/reactos/lib/cportlib/cportlib.rbuild
new file mode 100644 (file)
index 0000000..7a4dcd7
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../tools/rbuild/project.dtd">
+<group>
+       <module name="cportlib" type="staticlibrary">
+               <include base="cportlib">.</include>
+               <file>cport.c</file>
+       </module>
+</group>
index fe9cf99..544ae07 100644 (file)
@@ -13,6 +13,9 @@
        <directory name="cmlib">
                <xi:include href="cmlib/cmlib.rbuild" />
        </directory>
+       <directory name="cportlib">
+               <xi:include href="cportlib/cportlib.rbuild" />
+       </directory>
        <directory name="debugsup">
                <xi:include href="debugsup/debugsup.rbuild" />
        </directory>