xref: /reactos/sdk/lib/cportlib/cport_pc98.c (revision c1a8dce2)
1 /*
2  * PROJECT:         ReactOS ComPort Library for NEC PC-98 series
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Provides a serial port library for KDCOM, INIT, and FREELDR
5  * COPYRIGHT:       Copyright 2020 Dmitry Borisov (di.sean@protonmail.com)
6  */
7 
8 /* Note: ns16550 code from cportlib.c */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <intrin.h>
13 #include <ioaccess.h>
14 #include <ntstatus.h>
15 #include <cportlib/cportlib.h>
16 #include <drivers/pc98/serial.h>
17 #include <drivers/pc98/sysport.h>
18 #include <drivers/pc98/pit.h>
19 #include <drivers/pc98/cpu.h>
20 
21 /* GLOBALS ********************************************************************/
22 
23 #define TIMEOUT_COUNT 1024 * 200
24 
25 static struct
26 {
27     PUCHAR Address;
28     BOOLEAN HasFifo;
29     BOOLEAN FifoEnabled;
30     UCHAR RingIndicator;
31 } Rs232ComPort[] =
32 {
33     { (PUCHAR)0x030, FALSE, FALSE, 0 },
34     { (PUCHAR)0x238, FALSE, FALSE, 0 }
35 };
36 
37 static BOOLEAN IsNekoProject = FALSE;
38 
39 /* FUNCTIONS ******************************************************************/
40 
41 static BOOLEAN
CpIsNekoProject(VOID)42 CpIsNekoProject(VOID)
43 {
44     UCHAR Input[4] = "NP2";
45     UCHAR Output[4] = {0};
46     UCHAR i;
47 
48     for (i = 0; i < 3; i++)
49         WRITE_PORT_UCHAR((PUCHAR)0x7EF, Input[i]);
50 
51     for (i = 0; i < 3; i++)
52         Output[i] = READ_PORT_UCHAR((PUCHAR)0x7EF);
53 
54     return (*(PULONG)Input == *(PULONG)Output);
55 }
56 
57 static VOID
CpWait(VOID)58 CpWait(VOID)
59 {
60     UCHAR i;
61 
62     for (i = 0; i < 6; i++)
63         WRITE_PORT_UCHAR((PUCHAR)CPU_IO_o_ARTIC_DELAY, 0);
64 }
65 
66 VOID
67 NTAPI
CpEnableFifo(IN PUCHAR Address,IN BOOLEAN Enable)68 CpEnableFifo(
69     IN PUCHAR Address,
70     IN BOOLEAN Enable)
71 {
72     /* Set FIFO and clear the receive/transmit buffers */
73     if (Address == Rs232ComPort[0].Address && Rs232ComPort[0].HasFifo)
74     {
75         if (Enable)
76             WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL,
77                              SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET);
78         else
79             WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_FIFO_CONTROL, SER_FCR_DISABLE);
80         Rs232ComPort[0].FifoEnabled = Enable;
81     }
82     else if (Address == Rs232ComPort[1].Address && Rs232ComPort[1].HasFifo)
83     {
84         if (Enable)
85             WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL,
86                              SER_FCR_ENABLE | SER_FCR_RCVR_RESET | SER_FCR_TXMT_RESET);
87         else
88             WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_FIFO_CONTROL, SER_FCR_DISABLE);
89         Rs232ComPort[1].FifoEnabled = Enable;
90     }
91     CpWait();
92 }
93 
94 VOID
95 NTAPI
CpSetBaud(IN PCPPORT Port,IN ULONG BaudRate)96 CpSetBaud(
97     IN PCPPORT Port,
98     IN ULONG BaudRate)
99 {
100     UCHAR Lcr;
101     USHORT Count;
102     TIMER_CONTROL_PORT_REGISTER TimerControl;
103 
104     if (Port->Address == Rs232ComPort[0].Address)
105     {
106         if (Rs232ComPort[0].HasFifo)
107             WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DIVISOR_LATCH, SER1_DLR_MODE_LEGACY);
108 
109         TimerControl.BcdMode = FALSE;
110         TimerControl.OperatingMode = PitOperatingMode3;
111         TimerControl.AccessMode = PitAccessModeLowHigh;
112         TimerControl.Channel = PitChannel2;
113         if (IsNekoProject)
114         {
115             /* The horrible text input lag happens by about 6 seconds on my PC */
116             Count = 3;
117         }
118         else
119         {
120             Count = (READ_PORT_UCHAR((PUCHAR)0x42) & 0x20) ?
121                     (TIMER_FREQUENCY_1 / (BaudRate * 16)) : (TIMER_FREQUENCY_2 / (BaudRate * 16));
122         }
123         Write8253Timer(TimerControl, Count);
124 
125         /* Save baud rate in port */
126         Port->BaudRate = BaudRate;
127     }
128     else if (Port->Address == Rs232ComPort[1].Address)
129     {
130         /* Set the DLAB on */
131         Lcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_CONTROL);
132         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr | SER2_LCR_DLAB);
133 
134         /* Set the baud rate */
135         Count = SER2_CLOCK_RATE / BaudRate;
136         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_LSB, Count & 0xFF);
137         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_DIVISOR_LATCH_MSB, (Count >> 8) & 0xFF);
138 
139         /* Reset DLAB */
140         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, Lcr & ~SER2_LCR_DLAB);
141 
142         /* Save baud rate in port */
143         Port->BaudRate = BaudRate;
144     }
145 }
146 
147 NTSTATUS
148 NTAPI
CpInitialize(IN PCPPORT Port,IN PUCHAR Address,IN ULONG BaudRate)149 CpInitialize(
150     IN PCPPORT Port,
151     IN PUCHAR Address,
152     IN ULONG BaudRate)
153 {
154     SYSTEM_CONTROL_PORT_C_REGISTER SystemControl;
155     UCHAR FifoStatus;
156 
157     if (Port == NULL || Address == NULL || BaudRate == 0)
158         return STATUS_INVALID_PARAMETER;
159 
160     if (!CpDoesPortExist(Address))
161         return STATUS_NOT_FOUND;
162 
163     /* Initialize port data */
164     Port->Address = Address;
165     Port->BaudRate = 0;
166     Port->Flags = 0;
167 
168     IsNekoProject = CpIsNekoProject();
169 
170     if (Port->Address == Rs232ComPort[0].Address)
171     {
172         /* FIFO test */
173         FifoStatus = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED;
174         CpWait();
175         Rs232ComPort[0].HasFifo = ((READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID) & SER1_IIR_FIFOS_ENABLED) != FifoStatus);
176 
177         /* Disable the interrupts */
178         SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_C);
179         SystemControl.InterruptEnableRxReady = FALSE;
180         SystemControl.InterruptEnableTxEmpty = FALSE;
181         SystemControl.InterruptEnableTxReady = FALSE;
182         WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits);
183 
184         /* Turn off FIFO */
185         if (Rs232ComPort[0].HasFifo)
186             CpEnableFifo(Address, FALSE);
187 
188         /* Set the baud rate */
189         CpSetBaud(Port, BaudRate);
190 
191         /* Software reset */
192         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
193         CpWait();
194         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
195         CpWait();
196         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, 0);
197         CpWait();
198         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND, SER1_COMMMAND_IR);
199         CpWait();
200 
201         /* Mode instruction - asynchronous mode, 8 data bits, 1 stop bit, no parity, 16x clock divisor */
202         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
203                          SER1_MODE_LENGTH_8 | SER1_MODE_1_STOP | SER1_MODE_CLOCKx16);
204         CpWait();
205 
206         /* Command instruction - transmit enable, turn on DTR and RTS, receive enable, clear error flag */
207         WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
208                          SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR |
209                          SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS);
210         CpWait();
211 
212         /* Disable the interrupts again */
213         WRITE_PORT_UCHAR((PUCHAR)PPI_IO_o_PORT_C, SystemControl.Bits);
214 
215         /* Turn on FIFO */
216         if (Rs232ComPort[0].HasFifo)
217             CpEnableFifo(Address, TRUE);
218 
219         /* Read junk out of the data register */
220         if (Rs232ComPort[0].HasFifo)
221             (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER);
222         else
223             (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA);
224 
225         return STATUS_SUCCESS;
226     }
227     else if (Port->Address == Rs232ComPort[1].Address)
228     {
229         /* Disable the interrupts */
230         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL, 0);
231         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_INTERRUPT_EN, 0);
232 
233         /* Turn on DTR, RTS and OUT2 */
234         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
235                          SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE | SER2_MCR_OUT_2);
236 
237         /* Set the baud rate */
238         CpSetBaud(Port, BaudRate);
239 
240         /* Set 8 data bits, 1 stop bit, no parity, no break */
241         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_LINE_CONTROL,
242                          SER2_LCR_LENGTH_8 | SER2_LCR_ST1 | SER2_LCR_NO_PARITY);
243 
244         /* FIFO test */
245         Rs232ComPort[1].HasFifo = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_INTERRUPT_ID) & SER2_IIR_HAS_FIFO;
246 
247         /* Turn on FIFO */
248         if (Rs232ComPort[1].HasFifo)
249             CpEnableFifo(Address, TRUE);
250 
251         /* Read junk out of the RBR */
252         (VOID)READ_PORT_UCHAR((PUCHAR)SER2_IO_i_RECEIVER_BUFFER);
253 
254         return STATUS_SUCCESS;
255     }
256 
257     return STATUS_NOT_FOUND;
258 }
259 
260 static BOOLEAN
ComPortTest1(IN PUCHAR Address)261 ComPortTest1(IN PUCHAR Address)
262 {
263     /*
264      * See "Building Hardware and Firmware to Complement Microsoft Windows Headless Operation"
265      * Out-of-Band Management Port Device Requirements:
266      * The device must act as a 16550 or 16450 UART.
267      * Windows Server 2003 will test this device using the following process:
268      *     1. Save off the current modem status register.
269      *     2. Place the UART into diagnostic mode (The UART is placed into loopback mode
270      *        by writing SERIAL_MCR_LOOP to the modem control register).
271      *     3. The modem status register is read and the high bits are checked. This means
272      *        SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI and SERIAL_MSR_DCD should
273      *        all be clear.
274      *     4. Place the UART in diagnostic mode and turn on OUTPUT (Loopback Mode and
275      *         OUTPUT are both turned on by writing (SERIAL_MCR_LOOP | SERIAL_MCR_OUT1)
276      *         to the modem control register).
277      *     5. The modem status register is read and the ring indicator is checked.
278      *        This means SERIAL_MSR_RI should be set.
279      *     6. Restore original modem status register.
280      *
281      * REMARK: Strangely enough, the Virtual PC 2007 virtual machine
282      *         doesn't pass this test.
283      */
284 
285     BOOLEAN RetVal = FALSE;
286     UCHAR Mcr, Msr;
287 
288     /* Save the Modem Control Register */
289     Mcr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_CONTROL);
290 
291     /* Enable loop (diagnostic) mode (set Bit 4 of the MCR) */
292     WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK);
293 
294     /* Clear all modem output bits */
295     WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, SER2_MCR_LOOPBACK);
296 
297     /* Read the Modem Status Register */
298     Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
299 
300     /*
301      * The upper nibble of the MSR (modem output bits) must be
302      * equal to the lower nibble of the MCR (modem input bits).
303      */
304     if ((Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD)) == 0x00)
305     {
306         /* Set all modem output bits */
307         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
308                          SER2_MCR_OUT_1 | SER2_MCR_LOOPBACK); // Windows
309 /* ReactOS
310         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL,
311                          SER2_MCR_DTR_STATE | SER2_MCR_RTS_STATE |
312                          SER2_MCR_OUT_1 | SER2_MCR_OUT_2 | SER2_MCR_LOOPBACK);
313 */
314 
315         /* Read the Modem Status Register */
316         Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
317 
318         /*
319          * The upper nibble of the MSR (modem output bits) must be
320          * equal to the lower nibble of the MCR (modem input bits).
321          */
322         if (Msr & SER_MSR_RI) // Windows
323         // if (Msr & (SER_MSR_CTS | SER_MSR_DSR | SER_MSR_RI | SER_MSR_DCD) == 0xF0) // ReactOS
324         {
325             RetVal = TRUE;
326         }
327     }
328 
329     /* Restore the MCR */
330     WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_MODEM_CONTROL, Mcr);
331 
332     return RetVal;
333 }
334 
335 static BOOLEAN
ComPortTest2(IN PUCHAR Address)336 ComPortTest2(IN PUCHAR Address)
337 {
338     /*
339      * This test checks whether the 16450/16550 scratch register is available.
340      * If not, the serial port is considered as unexisting.
341      */
342 
343     UCHAR Byte = 0;
344 
345     do
346     {
347         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_SCRATCH, Byte);
348 
349         if (READ_PORT_UCHAR((PUCHAR)SER2_IO_i_SCRATCH) != Byte)
350             return FALSE;
351 
352     }
353     while (++Byte != 0);
354 
355     return TRUE;
356 }
357 
358 BOOLEAN
359 NTAPI
CpDoesPortExist(IN PUCHAR Address)360 CpDoesPortExist(IN PUCHAR Address)
361 {
362     UCHAR Data, Status;
363 
364     if (Address == Rs232ComPort[0].Address || Address == (PUCHAR)0x41)
365     {
366         Data = READ_PORT_UCHAR(Address);
367         Status = READ_PORT_UCHAR(Address + 2);
368         if ((Data & Status) == 0xFF || (Data | Status) == 0x00)
369             return FALSE;
370         else
371             return TRUE;
372     }
373     else if (Address == Rs232ComPort[1].Address)
374     {
375         return (ComPortTest1(Address) || ComPortTest2(Address));
376     }
377 
378     return FALSE;
379 }
380 
381 UCHAR
382 NTAPI
CpReadLsr(IN PCPPORT Port,IN UCHAR ExpectedValue)383 CpReadLsr(
384     IN PCPPORT Port,
385     IN UCHAR ExpectedValue)
386 {
387     UCHAR Lsr, Msr;
388     SYSTEM_CONTROL_PORT_B_REGISTER SystemControl;
389 
390     if (Port->Address == Rs232ComPort[0].Address)
391     {
392         /* Read the LSR and check if the expected value is present */
393         if (Rs232ComPort[0].HasFifo)
394         {
395             Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_LINE_STATUS);
396             if (!(Lsr & ExpectedValue))
397             {
398                 Msr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_MODEM_STATUS);
399 
400                 /* If the ring indicator reaches 3, we've seen this on/off twice */
401                 Rs232ComPort[0].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2;
402                 if (Rs232ComPort[0].RingIndicator == 3)
403                     Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
404             }
405         }
406         else
407         {
408             Lsr = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_STATUS);
409             if (!(Lsr & ExpectedValue))
410             {
411                 SystemControl.Bits = READ_PORT_UCHAR((PUCHAR)PPI_IO_i_PORT_B);
412 
413                 /* If the ring indicator reaches 3, we've seen this on/off twice */
414                 Rs232ComPort[0].RingIndicator |= SystemControl.RingIndicator ? 1 : 2;
415                 if (Rs232ComPort[0].RingIndicator == 3)
416                     Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
417             }
418         }
419 
420         return Lsr;
421     }
422     else if (Port->Address == Rs232ComPort[1].Address)
423     {
424         /* Read the LSR and check if the expected value is present */
425         Lsr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_LINE_STATUS);
426         if (!(Lsr & ExpectedValue))
427         {
428             Msr = READ_PORT_UCHAR((PUCHAR)SER2_IO_i_MODEM_STATUS);
429 
430             /* If the indicator reaches 3, we've seen this on/off twice */
431             Rs232ComPort[1].RingIndicator |= (Msr & SER_MSR_RI) ? 1 : 2;
432             if (Rs232ComPort[1].RingIndicator == 3)
433                 Port->Flags |= CPPORT_FLAG_MODEM_CONTROL;
434         }
435 
436         return Lsr;
437     }
438 
439     return 0;
440 }
441 
442 USHORT
443 NTAPI
CpGetByte(IN PCPPORT Port,OUT PUCHAR Byte,IN BOOLEAN Wait,IN BOOLEAN Poll)444 CpGetByte(
445     IN PCPPORT Port,
446     OUT PUCHAR Byte,
447     IN BOOLEAN Wait,
448     IN BOOLEAN Poll)
449 {
450     UCHAR Lsr;
451     ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1;
452     UCHAR SuccessFlags, ErrorFlags;
453     BOOLEAN FifoEnabled;
454 
455     /* Handle early read-before-init */
456     if (!Port->Address)
457         return CP_GET_NODATA;
458 
459     if (Port->Address == Rs232ComPort[0].Address)
460     {
461         SuccessFlags = Rs232ComPort[0].HasFifo ? SER1_LSR_RxRDY : SER1_STATUS_RxRDY;
462         ErrorFlags = Rs232ComPort[0].HasFifo ? (SER1_LSR_PE | SER1_LSR_OE) :
463                                                (SER1_STATUS_FE | SER1_STATUS_PE | SER1_STATUS_OE);
464 
465         /* If "wait" mode enabled, spin many times, otherwise attempt just once */
466         while (LimitCount--)
467         {
468             /* Read LSR for data ready */
469             Lsr = CpReadLsr(Port, SuccessFlags);
470             if (Lsr & SuccessFlags)
471             {
472                 /* If an error happened, clear the byte and fail */
473                 if (Lsr & ErrorFlags)
474                 {
475                     /* Save the last FIFO state */
476                     FifoEnabled = Rs232ComPort[0].FifoEnabled;
477 
478                     /* Turn off FIFO */
479                     if (FifoEnabled)
480                         CpEnableFifo(Port->Address, FALSE);
481 
482                     /* Clear error flag */
483                     WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_MODE_COMMAND,
484                                      SER1_COMMMAND_TxEN | SER1_COMMMAND_DTR |
485                                      SER1_COMMMAND_RxEN | SER1_COMMMAND_ER | SER1_COMMMAND_RTS);
486 
487                     /* Turn on FIFO */
488                     if (FifoEnabled)
489                         CpEnableFifo(Port->Address, TRUE);
490 
491                     *Byte = 0;
492                     return CP_GET_ERROR;
493                 }
494 
495                 /* If only polling was requested by caller, return now */
496                 if (Poll)
497                     return CP_GET_SUCCESS;
498 
499                 /* Otherwise read the byte and return it */
500                 if (Rs232ComPort[0].HasFifo)
501                     *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_RECEIVER_BUFFER);
502                 else
503                     *Byte = READ_PORT_UCHAR((PUCHAR)SER1_IO_i_DATA);
504 
505                 /* TODO: Handle CD if port is in modem control mode */
506 
507                 /* Byte was read */
508                 return CP_GET_SUCCESS;
509             }
510             else if (IsNekoProject && Rs232ComPort[0].HasFifo)
511             {
512                 /*
513                  * Neko Project 21/W doesn't set RxRDY without reading any data from 0x136.
514                  * TODO: Check real hardware behavior.
515                  */
516                 (VOID)READ_PORT_UCHAR((PUCHAR)SER1_IO_i_INTERRUPT_ID);
517             }
518         }
519 
520         /* Reset LSR, no data was found */
521         CpReadLsr(Port, 0);
522     }
523     else if (Port->Address == Rs232ComPort[1].Address)
524     {
525         /* If "wait" mode enabled, spin many times, otherwise attempt just once */
526         while (LimitCount--)
527         {
528             /* Read LSR for data ready */
529             Lsr = CpReadLsr(Port, SER2_LSR_DR);
530             if ((Lsr & SER2_LSR_DR) == SER2_LSR_DR)
531             {
532                 /* If an error happened, clear the byte and fail */
533                 if (Lsr & (SER2_LSR_FE | SER2_LSR_PE | SER2_LSR_OE))
534                 {
535                     *Byte = 0;
536                     return CP_GET_ERROR;
537                 }
538 
539                 /* If only polling was requested by caller, return now */
540                 if (Poll)
541                     return CP_GET_SUCCESS;
542 
543                 /* Otherwise read the byte and return it */
544                 *Byte = READ_PORT_UCHAR((UCHAR)SER2_IO_i_RECEIVER_BUFFER);
545 
546                 /* TODO: Handle CD if port is in modem control mode */
547 
548                 /* Byte was read */
549                 return CP_GET_SUCCESS;
550             }
551         }
552 
553         /* Reset LSR, no data was found */
554         CpReadLsr(Port, 0);
555     }
556 
557     return CP_GET_NODATA;
558 }
559 
560 VOID
561 NTAPI
CpPutByte(IN PCPPORT Port,IN UCHAR Byte)562 CpPutByte(
563     IN PCPPORT Port,
564     IN UCHAR Byte)
565 {
566     if (Port->Address == Rs232ComPort[0].Address)
567     {
568         /* TODO: Check if port is in modem control to handle CD */
569 
570         if (Rs232ComPort[0].HasFifo)
571         {
572             while ((CpReadLsr(Port, SER1_LSR_TxRDY) & SER1_LSR_TxRDY) == 0)
573                 NOTHING;
574 
575             WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_TRANSMITTER_BUFFER, Byte);
576         }
577         else
578         {
579             while ((CpReadLsr(Port, SER1_STATUS_TxRDY) & SER1_STATUS_TxRDY) == 0)
580                 NOTHING;
581 
582             WRITE_PORT_UCHAR((PUCHAR)SER1_IO_o_DATA, Byte);
583         }
584     }
585     else if (Port->Address == Rs232ComPort[1].Address)
586     {
587         /* TODO: Check if port is in modem control to handle CD */
588 
589         while ((CpReadLsr(Port, SER2_LSR_THR_EMPTY) & SER2_LSR_THR_EMPTY) == 0)
590             NOTHING;
591 
592         WRITE_PORT_UCHAR((PUCHAR)SER2_IO_o_TRANSMITTER_BUFFER, Byte);
593     }
594 }
595