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 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 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 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 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 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 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 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 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 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 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 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