2 * PROJECT: ReactOS ComPort Library
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: lib/reactos/cportlib/cport.c
5 * PURPOSE: Provides a serial port library for KDCOM, INIT, and FREELDR
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* NOTE: This library follows the precise serial port intialization steps documented
10 * by Microsoft in some of their Server hardware guidance. Because they've clearly
11 * documented their serial algorithms, we use the same ones to stay "compliant".
12 * Do not change this code to "improve" it. It's done this way on purpose, at least on x86.
15 * REPLY: I reworked the COM-port testing code because the original one
16 * (i.e. the Microsoft's documented one) doesn't work on Virtual PC 2007.
20 /* NOTE: This code is used by Headless Support (Ntoskrnl.exe and Osloader.exe) and
21 Kdcom.dll in Windows. It may be that WinDBG depends on some of these quirks.
24 /* NOTE: The original code supports Modem Control. We currently do not */
27 - Make this serial-port specific (NS16550 vs other serial port types)
28 - Get x64 KDCOM, KDBG, FREELDR, and other current code to use this
31 /* INCLUDES *******************************************************************/
33 #include <cportlib/cportlib.h>
34 #include <drivers/serial/ns16550.h>
42 /* GLOBALS ********************************************************************/
45 #define TIMEOUT_COUNT 1024 * 200
50 /* FUNCTIONS ******************************************************************/
54 CpEnableFifo(IN PUCHAR Address
,
57 /* Set FIFO and clear the receive/transmit buffers */
58 WRITE_PORT_UCHAR(Address
+ FIFO_CONTROL_REGISTER
,
59 Enable
? SERIAL_FCR_ENABLE
| SERIAL_FCR_RCVR_RESET
| SERIAL_FCR_TXMT_RESET
60 : SERIAL_FCR_DISABLE
);
65 CpSetBaud(IN PCPPORT Port
,
69 ULONG Mode
= CLOCK_RATE
/ BaudRate
;
72 Lcr
= READ_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
);
73 WRITE_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
, Lcr
| SERIAL_LCR_DLAB
);
75 /* Set the baud rate */
76 WRITE_PORT_UCHAR(Port
->Address
+ DIVISOR_LATCH_LSB
, (UCHAR
)(Mode
& 0xFF));
77 WRITE_PORT_UCHAR(Port
->Address
+ DIVISOR_LATCH_MSB
, (UCHAR
)((Mode
>> 8) & 0xFF));
80 WRITE_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
, Lcr
);
82 /* Save baud rate in port */
83 Port
->BaudRate
= BaudRate
;
88 CpInitialize(IN PCPPORT Port
,
93 if (Port
== NULL
|| Address
== NULL
|| BaudRate
== 0)
94 return STATUS_INVALID_PARAMETER
;
96 if (!CpDoesPortExist(Address
))
97 return STATUS_NOT_FOUND
;
99 /* Initialize port data */
100 Port
->Address
= Address
;
104 /* Disable the interrupts */
105 WRITE_PORT_UCHAR(Address
+ LINE_CONTROL_REGISTER
, 0);
106 WRITE_PORT_UCHAR(Address
+ INTERRUPT_ENABLE_REGISTER
, 0);
108 /* Turn on DTR, RTS and OUT2 */
109 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
,
110 SERIAL_MCR_DTR
| SERIAL_MCR_RTS
| SERIAL_MCR_OUT2
);
112 /* Set the baud rate */
113 CpSetBaud(Port
, BaudRate
);
115 /* Set 8 data bits, 1 stop bit, no parity, no break */
116 WRITE_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
,
117 SERIAL_8_DATA
| SERIAL_1_STOP
| SERIAL_NONE_PARITY
);
120 // TODO: Check whether FIFO exists and turn it on in that case.
121 CpEnableFifo(Address
, TRUE
); // for 16550
123 /* Read junk out of the RBR */
124 (VOID
)READ_PORT_UCHAR(Address
+ RECEIVE_BUFFER_REGISTER
);
126 return STATUS_SUCCESS
;
130 ComPortTest1(IN PUCHAR Address
)
133 * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation"
134 * Out-of-Band Management Port Device Requirements:
135 * The device must act as a 16550 or 16450 UART.
136 * Windows Server 2003 will test this device using the following process:
137 * 1. Save off the current modem status register.
138 * 2. Place the UART into diagnostic mode (The UART is placed into loopback mode
139 * by writing SERIAL_MCR_LOOP to the modem control register).
140 * 3. The modem status register is read and the high bits are checked. This means
141 * SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI and SERIAL_MSR_DCD should
143 * 4. Place the UART in diagnostic mode and turn on OUTPUT (Loopback Mode and
144 * OUTPUT are both turned on by writing (SERIAL_MCR_LOOP | SERIAL_MCR_OUT1)
145 * to the modem control register).
146 * 5. The modem status register is read and the ring indicator is checked.
147 * This means SERIAL_MSR_RI should be set.
148 * 6. Restore original modem status register.
150 * REMARK: Strangely enough, the Virtual PC 2007 virtual machine
151 * doesn't pass this test.
154 BOOLEAN RetVal
= FALSE
;
157 /* Save the Modem Control Register */
158 Mcr
= READ_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
);
160 /* Enable loop (diagnostic) mode (set Bit 4 of the MCR) */
161 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, SERIAL_MCR_LOOP
);
163 /* Clear all modem output bits */
164 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, SERIAL_MCR_LOOP
);
166 /* Read the Modem Status Register */
167 Msr
= READ_PORT_UCHAR(Address
+ MODEM_STATUS_REGISTER
);
170 * The upper nibble of the MSR (modem output bits) must be
171 * equal to the lower nibble of the MCR (modem input bits).
173 if ((Msr
& (SERIAL_MSR_CTS
| SERIAL_MSR_DSR
| SERIAL_MSR_RI
| SERIAL_MSR_DCD
)) == 0x00)
175 /* Set all modem output bits */
176 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
,
177 SERIAL_MCR_OUT1
| SERIAL_MCR_LOOP
); // Windows
179 WRITE_PORT_UCHAR(Address + MODEM_CONTROL_REGISTER,
180 SERIAL_MCR_DTR | SERIAL_MCR_RTS | SERIAL_MCR_OUT1 | SERIAL_MCR_OUT2 | SERIAL_MCR_LOOP);
183 /* Read the Modem Status Register */
184 Msr
= READ_PORT_UCHAR(Address
+ MODEM_STATUS_REGISTER
);
187 * The upper nibble of the MSR (modem output bits) must be
188 * equal to the lower nibble of the MCR (modem input bits).
190 if (Msr
& SERIAL_MSR_RI
) // Windows
191 // if (Msr & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_DCD) == 0xF0) // ReactOS
197 /* Restore the MCR */
198 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, Mcr
);
204 ComPortTest2(IN PUCHAR Address
)
207 * This test checks whether the 16450/16550 scratch register is available.
208 * If not, the serial port is considered as unexisting.
215 WRITE_PORT_UCHAR(Address
+ SCRATCH_REGISTER
, Byte
);
217 if (READ_PORT_UCHAR(Address
+ SCRATCH_REGISTER
) != Byte
)
220 } while (++Byte
!= 0);
227 CpDoesPortExist(IN PUCHAR Address
)
229 return ( ComPortTest1(Address
) || ComPortTest2(Address
) );
234 CpReadLsr(IN PCPPORT Port
,
235 IN UCHAR ExpectedValue
)
239 /* Read the LSR and check if the expected value is present */
240 Lsr
= READ_PORT_UCHAR(Port
->Address
+ LINE_STATUS_REGISTER
);
241 if (!(Lsr
& ExpectedValue
))
243 /* Check the MSR for ring indicator toggle */
244 Msr
= READ_PORT_UCHAR(Port
->Address
+ MODEM_STATUS_REGISTER
);
246 /* If the indicator reaches 3, we've seen this on/off twice */
247 RingIndicator
|= (Msr
& SERIAL_MSR_RI
) ? 1 : 2;
248 if (RingIndicator
== 3) Port
->Flags
|= CPPORT_FLAG_MODEM_CONTROL
;
256 CpGetByte(IN PCPPORT Port
,
261 ULONG LimitCount
= Wait
? TIMEOUT_COUNT
: 1;
263 /* Handle early read-before-init */
264 if (!Port
->Address
) return CP_GET_NODATA
;
266 /* If "wait" mode enabled, spin many times, otherwise attempt just once */
269 /* Read LSR for data ready */
270 Lsr
= CpReadLsr(Port
, SERIAL_LSR_DR
);
271 if ((Lsr
& SERIAL_LSR_DR
) == SERIAL_LSR_DR
)
273 /* If an error happened, clear the byte and fail */
274 if (Lsr
& (SERIAL_LSR_FE
| SERIAL_LSR_PE
| SERIAL_LSR_OE
))
280 /* Otherwise read the byte and return it */
281 *Byte
= READ_PORT_UCHAR(Port
->Address
+ RECEIVE_BUFFER_REGISTER
);
283 /* Handle CD if port is in modem control mode */
284 if (Port
->Flags
& CPPORT_FLAG_MODEM_CONTROL
)
286 /* Not implemented yet */
287 DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n");
291 return CP_GET_SUCCESS
;
295 /* Reset LSR, no data was found */
297 return CP_GET_NODATA
;
302 CpPutByte(IN PCPPORT Port
,
305 /* Check if port is in modem control to handle CD */
306 // while (Port->Flags & CPPORT_FLAG_MODEM_CONTROL) // Commented for the moment.
307 if (Port
->Flags
& CPPORT_FLAG_MODEM_CONTROL
) // To be removed when this becomes implemented.
309 /* Not implemented yet */
310 DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n");
313 /* Wait for LSR to say we can go ahead */
314 while ((CpReadLsr(Port
, SERIAL_LSR_THRE
) & SERIAL_LSR_THRE
) == 0x00);
317 WRITE_PORT_UCHAR(Port
->Address
+ TRANSMIT_HOLDING_REGISTER
, Byte
);