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.
16 /* NOTE: This code is used by Headless Support (Ntoskrnl.exe and Osloader.exe) and
17 Kdcom.dll in Windows. It may be that WinDBG depends on some of these quirks.
20 /* NOTE: The original code supports Modem Control. We currently do not */
23 - Make this serial-port specific (NS16550 vs other serial port types)
24 - Get x64 KDCOM, KDBG, FREELDR, and other current code to use this
27 /* INCLUDES *******************************************************************/
29 #include <cportlib/cportlib.h>
30 #include <drivers/serial/ns16550.h>
35 /* GLOBALS ********************************************************************/
39 /* FUNCTIONS ******************************************************************/
43 CpInitialize(IN PCPPORT Port
,
48 Port
->Address
= Address
;
51 /* Set the baud rate */
52 CpSetBaud(Port
, Rate
);
54 /* Enable on DTR and RTS */
55 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
,
56 SERIAL_MCR_DTR
| SERIAL_MCR_RTS
);
58 /* Disable interrupts */
59 WRITE_PORT_UCHAR(Address
+ INTERRUPT_ENABLE_REGISTER
, 0);
64 CpEnableFifo(IN PUCHAR Address
,
68 WRITE_PORT_UCHAR(Address
+ FIFO_CONTROL_REGISTER
, Enable
? SERIAL_FCR_ENABLE
: 0);
73 CpDoesPortExist(IN PUCHAR Address
)
77 * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation"
78 * Out-of-Band Management Port Device Requirements:
79 * The device must act as a 16550 or 16450 UART.
80 * Windows Server 2003 will test this device using the following process.
81 * 1. Save off the current modem status register.
82 * 2. Place the UART into diagnostic mode (The UART is placed into loopback mode
83 * by writing SERIAL_MCR_LOOP to the modem control register).
84 * 3. The modem status register is read and the high bits are checked. This means
85 * SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI and SERIAL_MSR_DCD should
87 * 4. Place the UART in diagnostic mode and turn on OUTPUT (Loopback Mode and
88 * OUTPUT are both turned on by writing (SERIAL_MCR_LOOP | SERIAL_MCR_OUT1)
89 * to the modem control register).
90 * 5. The modem status register is read and the ring indicator is checked.
91 * This means SERIAL_MSR_RI should be set.
92 * 6. Restore original modem status register.
94 Old
= READ_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
);
95 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, SERIAL_MCR_LOOP
);
96 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, SERIAL_MCR_LOOP
);
97 if (!(READ_PORT_UCHAR(Address
+ MODEM_STATUS_REGISTER
) &
98 (SERIAL_MSR_CTS
| SERIAL_MSR_DSR
| SERIAL_MSR_RI
| SERIAL_MSR_DCD
)))
100 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
,
101 (SERIAL_MCR_OUT1
| SERIAL_MCR_LOOP
));
102 if (READ_PORT_UCHAR(Address
+ MODEM_STATUS_REGISTER
) & SERIAL_MSR_RI
)
104 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, Old
);
108 WRITE_PORT_UCHAR(Address
+ MODEM_CONTROL_REGISTER
, Old
);
114 CpReadLsr(IN PCPPORT Port
,
115 IN UCHAR ExpectedValue
)
119 /* Read the LSR and check if the expected value is present */
120 Lsr
= READ_PORT_UCHAR(Port
->Address
+ LINE_STATUS_REGISTER
);
121 if (!(Lsr
& ExpectedValue
))
123 /* Check the MSR for ring indicator toggle */
124 Msr
= READ_PORT_UCHAR(Port
->Address
+ MODEM_STATUS_REGISTER
);
126 /* If the indicator reaches 3, we've seen this on/off twice */
127 RingIndicator
|= (Msr
& SERIAL_MSR_RI
) ? 1 : 2;
128 if (RingIndicator
== 3) Port
->Flags
|= CPPORT_FLAG_MODEM_CONTROL
;
136 CpSetBaud(IN PCPPORT Port
,
143 Lcr
= READ_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
);
144 WRITE_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
, Lcr
| SERIAL_LCR_DLAB
);
147 Mode
= 115200 / Rate
;
148 WRITE_PORT_UCHAR(Port
->Address
+ DIVISOR_LATCH_MSB
, (UCHAR
)((Mode
>> 8) & 0xff));
149 WRITE_PORT_UCHAR(Port
->Address
+ DIVISOR_LATCH_LSB
, (UCHAR
)(Mode
& 0xff));
151 /* Reset DLAB and set 8 data bits, 1 stop bit, no parity, no break */
152 WRITE_PORT_UCHAR(Port
->Address
+ LINE_CONTROL_REGISTER
,
153 SERIAL_8_DATA
| SERIAL_1_STOP
| SERIAL_NONE_PARITY
);
155 /* Save baud rate in port */
161 CpGetByte(IN PCPPORT Port
,
169 /* Handle early read-before-init */
170 if (!Port
->Address
) return CP_GET_NODATA
;
172 /* If "wait" mode enabled, spin many times, otherwise attempt just once */
173 i
= Wait
? 204800 : 1;
176 /* Read LSR for data ready */
177 Lsr
= CpReadLsr(Port
, SERIAL_LSR_DR
);
178 if ((Lsr
& SERIAL_LSR_DR
) == SERIAL_LSR_DR
)
180 /* If an error happened, clear the byte and fail */
181 if (Lsr
& (SERIAL_LSR_FE
| SERIAL_LSR_PE
))
187 /* If only polling was requested by caller, return now */
188 if (Poll
) return CP_GET_SUCCESS
;
190 /* Otherwise read the byte and return it */
191 *Byte
= READ_PORT_UCHAR(Port
->Address
+ RECEIVE_BUFFER_REGISTER
);
193 /* Handle CD if port is in modem control mode */
194 if (Port
->Flags
& CPPORT_FLAG_MODEM_CONTROL
)
196 /* Not implemented yet */
197 DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n");
201 return CP_GET_SUCCESS
;
205 /* Reset LSR, no data was found */
207 return CP_GET_NODATA
;
212 CpPutByte(IN PCPPORT Port
,
215 /* Check if port is in modem control to handle CD */
216 while (Port
->Flags
& CPPORT_FLAG_MODEM_CONTROL
)
218 /* Not implemented yet */
219 DPRINT1("CP: CPPORT_FLAG_MODEM_CONTROL unexpected\n");
222 /* Wait for LSR to say we can go ahead */
223 while (!(CpReadLsr(Port
, SERIAL_LSR_THRE
) & SERIAL_LSR_THRE
));
226 WRITE_PORT_UCHAR(Port
->Address
+ RECEIVE_BUFFER_REGISTER
, Byte
);