Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1046 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. port.c
  5. Abstract:
  6. This modules implements com port code to support reading/writing from com ports.
  7. Author:
  8. Bryan M. Willman (bryanwi) 24-Sep-90
  9. Revision History:
  10. --*/
  11. #include "ntos.h"
  12. #include "ntimage.h"
  13. #include <zwapi.h>
  14. #include <ntdddisk.h>
  15. #include <setupblk.h>
  16. #include <fsrtl.h>
  17. #include <ntverp.h>
  18. #include "stdlib.h"
  19. #include "stdio.h"
  20. #include <string.h>
  21. #include <safeboot.h>
  22. #include <inbv.h>
  23. #include <bootvid.h>
  24. //
  25. // Define COM Port registers.
  26. //
  27. #define COM1_PORT 0x03f8
  28. #define COM2_PORT 0x02f8
  29. #define COM_DAT 0x00
  30. #define COM_IEN 0x01 // interrupt enable register
  31. #define COM_FCR 0x02 // FIFO Control Register
  32. #define COM_LCR 0x03 // line control registers
  33. #define COM_MCR 0x04 // modem control reg
  34. #define COM_LSR 0x05 // line status register
  35. #define COM_MSR 0x06 // modem status register
  36. #define COM_DLL 0x00 // divisor latch least sig
  37. #define COM_DLM 0x01 // divisor latch most sig
  38. #define COM_BI 0x10 // Break detect
  39. #define COM_FE 0x08 // Framing error
  40. #define COM_PE 0x04 // Parity error
  41. #define COM_OE 0x02 // Overrun error
  42. #define LC_DLAB 0x80 // divisor latch access bit
  43. #define CLOCK_RATE 0x1C200 // USART clock rate
  44. #define MC_DTRRTS 0x03 // Control bits to assert DTR and RTS
  45. #define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
  46. #define MS_CD 0x80
  47. #define BD_150 150
  48. #define BD_300 300
  49. #define BD_600 600
  50. #define BD_1200 1200
  51. #define BD_2400 2400
  52. #define BD_4800 4800
  53. #define BD_9600 9600
  54. #define BD_14400 14400
  55. #define BD_19200 19200
  56. #define BD_56000 57600
  57. #define BD_115200 115200
  58. #define COM_OUTRDY 0x20
  59. #define COM_DATRDY 0x01
  60. //
  61. // Status Constants for reading data from comport
  62. //
  63. #define CP_GET_SUCCESS 0
  64. #define CP_GET_NODATA 1
  65. #define CP_GET_ERROR 2
  66. //
  67. // This bit controls the loopback testing mode of the device. Basically
  68. // the outputs are connected to the inputs (and vice versa).
  69. //
  70. #define SERIAL_MCR_LOOP 0x10
  71. //
  72. // This bit is used for general purpose output.
  73. //
  74. #define SERIAL_MCR_OUT1 0x04
  75. //
  76. // This bit contains the (complemented) state of the clear to send
  77. // (CTS) line.
  78. //
  79. #define SERIAL_MSR_CTS 0x10
  80. //
  81. // This bit contains the (complemented) state of the data set ready
  82. // (DSR) line.
  83. //
  84. #define SERIAL_MSR_DSR 0x20
  85. //
  86. // This bit contains the (complemented) state of the ring indicator
  87. // (RI) line.
  88. //
  89. #define SERIAL_MSR_RI 0x40
  90. //
  91. // This bit contains the (complemented) state of the data carrier detect
  92. // (DCD) line.
  93. //
  94. #define SERIAL_MSR_DCD 0x80
  95. typedef struct _CPPORT {
  96. PUCHAR Address;
  97. ULONG Baud;
  98. USHORT Flags;
  99. } CPPORT, *PCPPORT;
  100. #define PORT_DEFAULTRATE 0x0001 // baud rate not specified, using default
  101. #define PORT_MODEMCONTROL 0x0002 // using modem controls
  102. //
  103. // Define wait timeout value.
  104. //
  105. #define TIMEOUT_COUNT 1024 * 200
  106. //
  107. // Routines for reading/writing bytes out to the UART.
  108. //
  109. UCHAR
  110. (*READ_UCHAR)(
  111. IN PUCHAR Addr
  112. );
  113. VOID
  114. (*WRITE_UCHAR)(
  115. IN PUCHAR Addr,
  116. IN UCHAR Value
  117. );
  118. //
  119. // Define COM Port function prototypes.
  120. //
  121. VOID
  122. CpInitialize (
  123. PCPPORT Port,
  124. PUCHAR Address,
  125. ULONG Rate
  126. );
  127. VOID
  128. CpEnableFifo(
  129. IN PUCHAR Address,
  130. IN BOOLEAN bEnable
  131. );
  132. BOOLEAN
  133. CpDoesPortExist(
  134. IN PUCHAR Address
  135. );
  136. UCHAR
  137. CpReadLsr (
  138. IN PCPPORT Port,
  139. IN UCHAR Waiting
  140. );
  141. VOID
  142. CpSetBaud (
  143. PCPPORT Port,
  144. ULONG Rate
  145. );
  146. USHORT
  147. CpGetByte (
  148. PCPPORT Port,
  149. PUCHAR Byte,
  150. BOOLEAN WaitForData,
  151. BOOLEAN PollOnly
  152. );
  153. VOID
  154. CpPutByte (
  155. PCPPORT Port,
  156. UCHAR Byte
  157. );
  158. //
  159. // Define debugger port initial state.
  160. //
  161. CPPORT Port[4] = {
  162. {NULL, 0, PORT_DEFAULTRATE},
  163. {NULL, 0, PORT_DEFAULTRATE},
  164. {NULL, 0, PORT_DEFAULTRATE},
  165. {NULL, 0, PORT_DEFAULTRATE}
  166. };
  167. //
  168. // We'll use these to fill in some function pointers,
  169. // which in turn will be used to read/write from the
  170. // UART. We can't simply assign the function pointers
  171. // to point to READ_PORT_UCHAR/READ_REGISTER_UCHAR and
  172. // WRITE_PORT_UCHAR/WRITE_REGISTER_UCHAR, because in
  173. // the case of IA64, some of these functions are macros.
  174. //
  175. // To get around this, build these dummy functions that
  176. // will inturn simply call the correct READ/WRITE functions/macros.
  177. //
  178. UCHAR
  179. MY_READ_PORT_UCHAR( IN PUCHAR Addr )
  180. {
  181. return( READ_PORT_UCHAR(Addr) );
  182. }
  183. UCHAR
  184. MY_READ_REGISTER_UCHAR( IN PUCHAR Addr )
  185. {
  186. return( READ_REGISTER_UCHAR(Addr) );
  187. }
  188. VOID
  189. MY_WRITE_PORT_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  190. {
  191. WRITE_PORT_UCHAR(Addr, Value);
  192. }
  193. VOID
  194. MY_WRITE_REGISTER_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  195. {
  196. WRITE_REGISTER_UCHAR(Addr, Value);
  197. }
  198. BOOLEAN
  199. InbvPortInitialize(
  200. IN ULONG BaudRate,
  201. IN ULONG PortNumber,
  202. IN PUCHAR PortAddress,
  203. OUT PULONG BlFileId,
  204. IN BOOLEAN IsMMIOAddress
  205. )
  206. /*++
  207. Routine Description:
  208. This functions initializes the com port.
  209. Arguments:
  210. BaudRate - Supplies an optional baud rate.
  211. PortNumber - supplies an optinal port number.
  212. BlFileId - A place to store a fake file Id, if successful.
  213. IsMMIOAddress - Indicates whether or not the given PortAddress
  214. parameter is in MMIO address space.
  215. Returned Value:
  216. TRUE - If a debug port is found, and BlFileId will point to a location within Port[].
  217. --*/
  218. {
  219. UCHAR DebugMessage[80];
  220. //
  221. // If the baud rate is not specified, then default the baud rate to 19.2.
  222. //
  223. if (BaudRate == 0) {
  224. BaudRate = BD_19200;
  225. }
  226. //
  227. // If a port number is not specified, then attempt to use port 2 then
  228. // port 1. Otherwise, use the specified port.
  229. //
  230. if (PortNumber == 0) {
  231. if (CpDoesPortExist((PUCHAR)COM2_PORT)) {
  232. PortNumber = 2;
  233. PortAddress = (PUCHAR)COM2_PORT;
  234. } else if (CpDoesPortExist((PUCHAR)COM1_PORT)) {
  235. PortNumber = 1;
  236. PortAddress = (PUCHAR)COM1_PORT;
  237. } else {
  238. return FALSE;
  239. }
  240. } else {
  241. if( PortAddress == NULL ) {
  242. //
  243. // The port address wasn't specified. Guess what it
  244. // is based on the COM port number.
  245. //
  246. switch (PortNumber) {
  247. case 1:
  248. PortAddress = (PUCHAR)0x3f8;
  249. break;
  250. case 2:
  251. PortAddress = (PUCHAR)0x2f8;
  252. break;
  253. case 3:
  254. PortAddress = (PUCHAR)0x3e8;
  255. break;
  256. default:
  257. PortNumber = 4;
  258. PortAddress = (PUCHAR)0x2e8;
  259. }
  260. }
  261. }
  262. //
  263. // Check if the port is already in use.
  264. //
  265. if (Port[PortNumber-1].Address != NULL) {
  266. return FALSE;
  267. }
  268. //
  269. // we need to handle the case where we're dealing with
  270. // MMIO space (as opposed to System I/O space).
  271. //
  272. if( IsMMIOAddress ) {
  273. PHYSICAL_ADDRESS PhysAddr;
  274. PVOID MyPtr;
  275. PhysAddr.LowPart = PtrToUlong(PortAddress);
  276. PhysAddr.HighPart = 0;
  277. MyPtr = MmMapIoSpace(PhysAddr,(1+COM_MSR),FALSE);
  278. PortAddress = MyPtr;
  279. READ_UCHAR = MY_READ_REGISTER_UCHAR;
  280. WRITE_UCHAR = MY_WRITE_REGISTER_UCHAR;
  281. } else {
  282. // System IO space.
  283. READ_UCHAR = MY_READ_PORT_UCHAR;
  284. WRITE_UCHAR = MY_WRITE_PORT_UCHAR;
  285. }
  286. //
  287. // Initialize the specified port.
  288. //
  289. CpInitialize(&(Port[PortNumber-1]),
  290. PortAddress,
  291. BaudRate);
  292. *BlFileId = (PortNumber-1);
  293. return TRUE;
  294. }
  295. BOOLEAN
  296. InbvPortTerminate(
  297. IN ULONG BlFileId
  298. )
  299. /*++
  300. Routine Description:
  301. This functions closes the com port.
  302. Arguments:
  303. BlFileId - File Id to be stored.
  304. Returned Value:
  305. TRUE - port was closed successfully.
  306. --*/
  307. {
  308. //
  309. // Check if the port is already in use.
  310. //
  311. if (Port[BlFileId].Address != NULL) {
  312. //
  313. // Do any cleanup necessary here. Note that we don't require any
  314. // cleanup today so this is a NOP.
  315. //
  316. NOTHING;
  317. }
  318. Port[BlFileId].Address = NULL;
  319. return(TRUE);
  320. }
  321. VOID
  322. InbvPortPutByte (
  323. IN ULONG BlFileId,
  324. IN UCHAR Output
  325. )
  326. /*++
  327. Routine Description:
  328. Write a byte to the port.
  329. Arguments:
  330. BlFileId - The port to write to.
  331. Output - Supplies the output data byte.
  332. Return Value:
  333. None.
  334. --*/
  335. {
  336. CpPutByte(&Port[BlFileId], Output);
  337. if (Output == '\n') {
  338. CpPutByte(&(Port[BlFileId]), '\r');
  339. }
  340. }
  341. VOID
  342. InbvPortPutString (
  343. IN ULONG BlFileId,
  344. IN PUCHAR Output
  345. )
  346. /*++
  347. Routine Description:
  348. Write a string to the port.
  349. Arguments:
  350. BlFileId - The port to write to.
  351. Output - Supplies the output data string.
  352. Return Value:
  353. None.
  354. --*/
  355. {
  356. if (BlFileId == 0) {
  357. return;
  358. }
  359. while (*Output != '\0') {
  360. InbvPortPutByte(BlFileId, *Output);
  361. Output++;
  362. }
  363. }
  364. BOOLEAN
  365. InbvPortGetByte (
  366. IN ULONG BlFileId,
  367. OUT PUCHAR Input
  368. )
  369. /*++
  370. Routine Description:
  371. Fetch a byte from the port and return it.
  372. Arguments:
  373. BlFileId - The port to read from.
  374. Input - Returns the data byte.
  375. Return Value:
  376. TRUE if successful, else FALSE.
  377. --*/
  378. {
  379. return (CpGetByte(&(Port[BlFileId]), Input, TRUE, FALSE) == CP_GET_SUCCESS);
  380. }
  381. BOOLEAN
  382. InbvPortPollOnly (
  383. IN ULONG BlFileId
  384. )
  385. /*++
  386. Routine Description:
  387. Check if a byte is available
  388. Arguments:
  389. BlFileId - The port to poll.
  390. Return Value:
  391. TRUE if there is data waiting, else FALSE.
  392. --*/
  393. {
  394. CHAR Input;
  395. return (CpGetByte(&(Port[BlFileId]), &Input, FALSE, TRUE) == CP_GET_SUCCESS);
  396. }
  397. VOID
  398. CpInitialize (
  399. PCPPORT Port,
  400. PUCHAR Address,
  401. ULONG Rate
  402. )
  403. /*++
  404. Routine Description:
  405. Fill in the com port port object, set the initial baud rate,
  406. turn on the hardware.
  407. Arguments:
  408. Port - address of port object
  409. Address - port address of the com port
  410. (CP_COM1_PORT, CP_COM2_PORT)
  411. Rate - baud rate (CP_BD_150 ... CP_BD_19200)
  412. --*/
  413. {
  414. PUCHAR hwport;
  415. UCHAR mcr, ier;
  416. Port->Address = Address;
  417. Port->Baud = 0;
  418. CpSetBaud(Port, Rate);
  419. //
  420. // Assert DTR, RTS.
  421. //
  422. hwport = Port->Address;
  423. hwport += COM_MCR;
  424. mcr = MC_DTRRTS;
  425. WRITE_UCHAR(hwport, mcr);
  426. hwport = Port->Address;
  427. hwport += COM_IEN;
  428. ier = 0;
  429. WRITE_UCHAR(hwport, ier);
  430. return;
  431. }
  432. VOID
  433. InbvPortEnableFifo(
  434. IN ULONG DeviceId,
  435. IN BOOLEAN bEnable
  436. )
  437. /*++
  438. Routine Description:
  439. This routine will attempt to enable the FIFO in the 16550 UART.
  440. Note that the behaviour is undefined for the 16450, but practically,
  441. this should have no effect.
  442. Arguments:
  443. DeviceId - Value returned by InbvPortInitialize()
  444. bEnable - if TRUE, FIFO is enabled
  445. if FALSE, FIFO is disabled
  446. Return Value:
  447. None
  448. --*/
  449. {
  450. CpEnableFifo(
  451. Port[DeviceId].Address,
  452. bEnable
  453. );
  454. }
  455. VOID
  456. CpEnableFifo(
  457. IN PUCHAR Address,
  458. IN BOOLEAN bEnable
  459. )
  460. /*++
  461. Routine Description:
  462. This routine will attempt to enable the FIFO in the
  463. UART at the address specified. If this is a 16550,
  464. this works. The behaviour on a 16450 is not defined,
  465. but practically, there is no effect.
  466. Arguments:
  467. Address - address of hw port.
  468. bEnable - if TRUE, FIFO is enabled
  469. if FALSE, FIFO is disabled
  470. Return Value:
  471. None
  472. --*/
  473. {
  474. //
  475. // Enable the FIFO in the UART. The behaviour is undefined on the
  476. // 16450, but practically, it should just ignore the command.
  477. //
  478. PUCHAR hwport = Address;
  479. hwport += COM_FCR;
  480. WRITE_UCHAR(hwport, bEnable); // set the FIFO state
  481. }
  482. BOOLEAN
  483. CpDoesPortExist(
  484. IN PUCHAR Address
  485. )
  486. /*++
  487. Routine Description:
  488. This routine will attempt to place the port into its
  489. diagnostic mode. If it does it will twiddle a bit in
  490. the modem control register. If the port exists this
  491. twiddling should show up in the modem status register.
  492. NOTE: This routine must be called before the device is
  493. enabled for interrupts, this includes setting the
  494. output2 bit in the modem control register.
  495. This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c.
  496. Arguments:
  497. Address - address of hw port.
  498. Return Value:
  499. TRUE - Port exists.
  500. FALSE - Port doesn't exist.
  501. --*/
  502. {
  503. UCHAR OldModemStatus;
  504. UCHAR ModemStatus;
  505. BOOLEAN ReturnValue = TRUE;
  506. //
  507. // Save the old value of the modem control register.
  508. //
  509. OldModemStatus = READ_UCHAR(Address + COM_MCR);
  510. //
  511. // Set the port into diagnostic mode.
  512. //
  513. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  514. //
  515. // Bang on it again to make sure that all the lower bits
  516. // are clear.
  517. //
  518. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  519. //
  520. // Read the modem status register. The high for bits should
  521. // be clear.
  522. //
  523. ModemStatus = READ_UCHAR(Address + COM_MSR);
  524. if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
  525. SERIAL_MSR_RI | SERIAL_MSR_DCD)) {
  526. ReturnValue = FALSE;
  527. goto AllDone;
  528. }
  529. //
  530. // So far so good. Now turn on OUT1 in the modem control register
  531. // and this should turn on ring indicator in the modem status register.
  532. //
  533. WRITE_UCHAR(Address + COM_MCR, (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP));
  534. ModemStatus = READ_UCHAR(Address + COM_MSR);
  535. if (!(ModemStatus & SERIAL_MSR_RI)) {
  536. ReturnValue = FALSE;
  537. goto AllDone;
  538. }
  539. //
  540. // Put the modem control back into a clean state.
  541. //
  542. AllDone:
  543. WRITE_UCHAR(Address + COM_MCR, OldModemStatus);
  544. return ReturnValue;
  545. }
  546. UCHAR
  547. CpReadLsr (
  548. PCPPORT Port,
  549. UCHAR waiting
  550. )
  551. /*++
  552. Routine Description:
  553. Read LSR byte from specified port. If HAL owns port & display
  554. it will also cause a debug status to be kept up to date.
  555. Handles entering & exiting modem control mode for debugger.
  556. Arguments:
  557. Port - Address of CPPORT
  558. Returns:
  559. Byte read from port
  560. --*/
  561. {
  562. static UCHAR ringflag = 0;
  563. UCHAR lsr, msr;
  564. lsr = READ_UCHAR(Port->Address + COM_LSR);
  565. if ((lsr & waiting) == 0) {
  566. msr = READ_UCHAR (Port->Address + COM_MSR);
  567. ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2;
  568. if (ringflag == 3) {
  569. //
  570. // The ring indicate line has toggled, use modem control from
  571. // now on.
  572. //
  573. Port->Flags |= PORT_MODEMCONTROL;
  574. }
  575. }
  576. return lsr;
  577. }
  578. VOID
  579. CpSetBaud (
  580. PCPPORT Port,
  581. ULONG Rate
  582. )
  583. /*++
  584. Routine Description:
  585. Set the baud rate for the port and record it in the port object.
  586. Arguments:
  587. Port - address of port object
  588. Rate - baud rate (CP_BD_150 ... CP_BD_56000)
  589. --*/
  590. {
  591. ULONG divisorlatch;
  592. PUCHAR hwport;
  593. UCHAR lcr;
  594. //
  595. // compute the divsor
  596. //
  597. divisorlatch = CLOCK_RATE / Rate;
  598. //
  599. // set the divisor latch access bit (DLAB) in the line control reg
  600. //
  601. hwport = Port->Address;
  602. hwport += COM_LCR; // hwport = LCR register
  603. lcr = READ_UCHAR(hwport);
  604. lcr |= LC_DLAB;
  605. WRITE_UCHAR(hwport, lcr);
  606. //
  607. // set the divisor latch value.
  608. //
  609. hwport = Port->Address;
  610. hwport += COM_DLM; // divisor latch msb
  611. WRITE_UCHAR(hwport, (UCHAR)((divisorlatch >> 8) & 0xff));
  612. hwport--; // divisor latch lsb
  613. WRITE_UCHAR(hwport, (UCHAR)(divisorlatch & 0xff));
  614. //
  615. // Set LCR to 3. (3 is a magic number in the original assembler)
  616. //
  617. hwport = Port->Address;
  618. hwport += COM_LCR;
  619. WRITE_UCHAR(hwport, 3);
  620. //
  621. // Remember the baud rate
  622. //
  623. Port->Baud = Rate;
  624. return;
  625. }
  626. USHORT
  627. CpGetByte (
  628. PCPPORT Port,
  629. PUCHAR Byte,
  630. BOOLEAN WaitForByte,
  631. BOOLEAN PollOnly
  632. )
  633. /*++
  634. Routine Description:
  635. Fetch a byte and return it.
  636. Arguments:
  637. Port - address of port object that describes hw port
  638. Byte - address of variable to hold the result
  639. WaitForByte - flag indicates wait for byte or not.
  640. PollOnly - flag indicates whether to return immediately, not reading the byte, or not.
  641. Return Value:
  642. CP_GET_SUCCESS if data returned, or if data is ready and PollOnly is TRUE.
  643. CP_GET_NODATA if no data available, but no error.
  644. CP_GET_ERROR if error (overrun, parity, etc.)
  645. --*/
  646. {
  647. UCHAR lsr;
  648. UCHAR value;
  649. ULONG limitcount;
  650. //
  651. // Check to make sure the CPPORT we were passed has been initialized.
  652. // (The only time it won't be initialized is when the kernel debugger
  653. // is disabled, in which case we just return.)
  654. //
  655. if (Port->Address == NULL) {
  656. return CP_GET_NODATA;
  657. }
  658. limitcount = WaitForByte ? TIMEOUT_COUNT : 1;
  659. while (limitcount != 0) {
  660. limitcount--;
  661. lsr = CpReadLsr(Port, COM_DATRDY);
  662. if ((lsr & COM_DATRDY) == COM_DATRDY) {
  663. //
  664. // Check for errors
  665. //
  666. //
  667. // If we get an overrun error, and there is data ready, we should
  668. // return the data we have, so we ignore overrun errors. Reading
  669. // the LSR clears this bit, so the first read already cleared the
  670. // overrun error.
  671. //
  672. if (lsr & (COM_FE | COM_PE)) {
  673. *Byte = 0;
  674. return CP_GET_ERROR;
  675. }
  676. if (PollOnly) {
  677. return CP_GET_SUCCESS;
  678. }
  679. //
  680. // fetch the byte
  681. //
  682. *Byte = READ_UCHAR(Port->Address + COM_DAT);
  683. if (Port->Flags & PORT_MODEMCONTROL) {
  684. //
  685. // Using modem control. If no CD, then skip this byte.
  686. //
  687. if ((READ_UCHAR(Port->Address + COM_MSR) & MS_CD) == 0) {
  688. continue;
  689. }
  690. }
  691. return CP_GET_SUCCESS;
  692. }
  693. }
  694. CpReadLsr(Port, 0);
  695. return CP_GET_NODATA;
  696. }
  697. VOID
  698. CpPutByte (
  699. PCPPORT Port,
  700. UCHAR Byte
  701. )
  702. /*++
  703. Routine Description:
  704. Write a byte out to the specified com port.
  705. Arguments:
  706. Port - Address of CPPORT object
  707. Byte - data to emit
  708. --*/
  709. {
  710. UCHAR msr, lsr;
  711. //
  712. // If modem control, make sure DSR, CTS and CD are all set before
  713. // sending any data.
  714. //
  715. while ((Port->Flags & PORT_MODEMCONTROL) &&
  716. (msr = READ_UCHAR(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) {
  717. //
  718. // If no CD, and there's a charactor ready, eat it
  719. //
  720. lsr = CpReadLsr(Port, 0);
  721. if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) {
  722. READ_UCHAR(Port->Address + COM_DAT);
  723. }
  724. }
  725. //
  726. // Wait for port to not be busy
  727. //
  728. while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ;
  729. //
  730. // Send the byte
  731. //
  732. WRITE_UCHAR(Port->Address + COM_DAT, Byte);
  733. return;
  734. }