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