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