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.

768 lines
16 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. ixkdcom.c
  5. Abstract:
  6. This module contains a very simply package to do com I/O on machines
  7. with standard AT com-ports. It is C code derived from the debugger's
  8. com code. Likely does not work on a PS/2. (Only rewrote the thing
  9. into C so we wouldn't have to deal with random debugger macros.)
  10. Procedures to init a com object, set and query baud rate, output
  11. character, input character.
  12. Author:
  13. Bryan M. Willman (bryanwi) 24-Sep-1990
  14. Revision History:
  15. John Vert (jvert) 12-Jun-1991
  16. Added ability to check for com-port's existence and hook onto the
  17. highest com-port available.
  18. John Vert (jvert) 19-Jul-1991
  19. Moved into HAL
  20. --*/
  21. #include <nthal.h>
  22. #include <hal.h>
  23. #include "kdcomp.h"
  24. #include "kdcom.h"
  25. #include "inbv.h"
  26. #define TIMEOUT_COUNT 1024 * 200
  27. UCHAR CpReadLsr (PCPPORT, UCHAR);
  28. BOOLEAN KdCompDbgPortsPresent = TRUE;
  29. UCHAR KdCompDbgErrorCount = 0;
  30. #define DBG_ACCEPTABLE_ERRORS 25
  31. static UCHAR LastLsr, LastMsr;
  32. extern pKWriteUchar KdWriteUchar;
  33. extern pKReadUchar KdReadUchar;
  34. VOID
  35. CpInitialize (
  36. PCPPORT Port,
  37. PUCHAR Address,
  38. ULONG Rate
  39. )
  40. /*++
  41. Routine Description:
  42. Fill in the com port port object, set the initial baud rate,
  43. turn on the hardware.
  44. Arguments:
  45. Port - address of port object
  46. Address - port address of the com port
  47. (CP_COM1_PORT, CP_COM2_PORT)
  48. Rate - baud rate (CP_BD_150 ... CP_BD_19200)
  49. --*/
  50. {
  51. PUCHAR hwport;
  52. UCHAR mcr, ier;
  53. Port->Address = Address;
  54. Port->Baud = 0;
  55. CpSetBaud(Port, Rate);
  56. //
  57. // Assert DTR, RTS.
  58. //
  59. hwport = Port->Address;
  60. hwport += COM_MCR;
  61. mcr = MC_DTRRTS;
  62. KdWriteUchar(hwport, mcr);
  63. hwport = Port->Address;
  64. hwport += COM_IEN;
  65. ier = 0;
  66. KdWriteUchar(hwport, ier);
  67. }
  68. VOID
  69. CpSetBaud (
  70. PCPPORT Port,
  71. ULONG Rate
  72. )
  73. /*++
  74. Routine Description:
  75. Set the baud rate for the port and record it in the port object.
  76. Arguments:
  77. Port - address of port object
  78. Rate - baud rate (CP_BD_150 ... CP_BD_56000)
  79. --*/
  80. {
  81. ULONG divisorlatch;
  82. PUCHAR hwport;
  83. UCHAR lcr;
  84. //
  85. // compute the divsor
  86. //
  87. divisorlatch = CLOCK_RATE / Rate;
  88. //
  89. // set the divisor latch access bit (DLAB) in the line control reg
  90. //
  91. hwport = Port->Address;
  92. hwport += COM_LCR; // hwport = LCR register
  93. lcr = KdReadUchar(hwport);
  94. lcr |= LC_DLAB;
  95. KdWriteUchar(hwport, lcr);
  96. //
  97. // set the divisor latch value.
  98. //
  99. hwport = Port->Address;
  100. hwport += COM_DLM; // divisor latch msb
  101. KdWriteUchar(hwport, (UCHAR)((divisorlatch >> 8) & 0xff));
  102. hwport--; // divisor latch lsb
  103. KdWriteUchar(hwport, (UCHAR)(divisorlatch & 0xff));
  104. //
  105. // Set LCR to 3. (3 is a magic number in the original assembler)
  106. //
  107. hwport = Port->Address;
  108. hwport += COM_LCR;
  109. KdWriteUchar(hwport, 3);
  110. //
  111. // Remember the baud rate
  112. //
  113. Port->Baud = Rate;
  114. }
  115. USHORT
  116. CpQueryBaud (
  117. PCPPORT Port
  118. )
  119. /*++
  120. Routine Description:
  121. Return the last value baud rate was set to.
  122. Arguments:
  123. Port - address of cpport object which describes the hw port of interest.
  124. Return Value:
  125. Baud rate. 0 = none has been set.
  126. --*/
  127. {
  128. return (USHORT) Port->Baud;
  129. }
  130. VOID
  131. CpSendModemString (
  132. PCPPORT Port,
  133. IN PUCHAR String
  134. )
  135. /*++
  136. Routine Description:
  137. Sends a command string to the modem.
  138. This is down in order to aid the modem in determining th
  139. baud rate the local connect is at.
  140. Arguments:
  141. Port - Address of CPPORT
  142. String - String to send to modem
  143. --*/
  144. {
  145. static ULONG Delay;
  146. TIME_FIELDS CurrentTime;
  147. UCHAR i;
  148. ULONG l;
  149. if (Port->Flags & PORT_SENDINGSTRING)
  150. return ;
  151. Port->Flags |= PORT_SENDINGSTRING;
  152. if (!Delay) {
  153. // see how long 1 second is
  154. HalQueryRealTimeClock (&CurrentTime);
  155. l = CurrentTime.Second;
  156. while (l == (ULONG) CurrentTime.Second) {
  157. CpReadLsr(Port, 0);
  158. HalQueryRealTimeClock (&CurrentTime);
  159. Delay++;
  160. }
  161. Delay = Delay / 3;
  162. }
  163. l = Delay;
  164. while (*String) {
  165. HalQueryRealTimeClock (&CurrentTime);
  166. i = CpReadLsr (Port, 0);
  167. if (i & COM_OUTRDY) {
  168. if ((--l) == 0) {
  169. KdWriteUchar(Port->Address+COM_DAT, *String);
  170. String++;
  171. l = Delay;
  172. }
  173. }
  174. if (i & COM_DATRDY)
  175. KdReadUchar(Port->Address + COM_DAT);
  176. }
  177. Port->Flags &= ~PORT_SENDINGSTRING;
  178. }
  179. UCHAR
  180. CpReadLsr (
  181. PCPPORT Port,
  182. UCHAR waiting
  183. )
  184. /*++
  185. Routine Description:
  186. Read LSR byte from specified port. If HAL owns port & display
  187. it will also cause a debug status to be kept up to date.
  188. Handles entering & exiting modem control mode for debugger.
  189. Arguments:
  190. Port - Address of CPPORT
  191. Returns:
  192. Byte read from port
  193. --*/
  194. {
  195. static UCHAR ringflag = 0;
  196. static UCHAR diagout[3];
  197. static ULONG diagmsg[3] = { 'TRP ', 'LVO ', 'MRF ' };
  198. static UCHAR ModemString[] = "\n\rAT\n\r";
  199. TIME_FIELDS CurrentTime;
  200. UCHAR lsr, msr, i;
  201. ULONG diagstr[12];
  202. lsr = KdReadUchar(Port->Address + COM_LSR);
  203. //
  204. // Check to see if the port still exists.
  205. //
  206. if (lsr == SERIAL_LSR_NOT_PRESENT) {
  207. KdCompDbgErrorCount++;
  208. if (KdCompDbgErrorCount >= DBG_ACCEPTABLE_ERRORS) {
  209. KdCompDbgPortsPresent = FALSE;
  210. KdCompDbgErrorCount = 0;
  211. }
  212. return SERIAL_LSR_NOT_PRESENT;
  213. }
  214. if (lsr & COM_PE)
  215. diagout[0] = 8; // Parity error
  216. if (lsr & COM_OE)
  217. diagout[1] = 8; // Overflow error
  218. if (lsr & COM_FE)
  219. diagout[2] = 8; // Framing error
  220. if (lsr & waiting) {
  221. LastLsr = ~COM_DATRDY | (lsr & COM_DATRDY);
  222. return lsr;
  223. }
  224. msr = KdReadUchar (Port->Address + COM_MSR);
  225. if (Port->Flags & PORT_MODEMCONTROL) {
  226. if (msr & SERIAL_MSR_DCD) {
  227. //
  228. // In modem control mode with carrier detect
  229. // Reset carrier lost time
  230. //
  231. Port->Flags |= PORT_NOCDLTIME | PORT_MDM_CD;
  232. } else {
  233. //
  234. // In modem control mode, but no carrier detect. After
  235. // 60 seconds drop out of modem control mode
  236. //
  237. if (Port->Flags & PORT_NOCDLTIME) {
  238. HalQueryRealTimeClock (&Port->CarrierLostTime);
  239. Port->Flags &= ~PORT_NOCDLTIME;
  240. ringflag = 0;
  241. }
  242. HalQueryRealTimeClock (&CurrentTime);
  243. if (CurrentTime.Minute != Port->CarrierLostTime.Minute &&
  244. CurrentTime.Second >= Port->CarrierLostTime.Second) {
  245. //
  246. // It's been at least 60 seconds - drop out of
  247. // modem control mode until next RI
  248. //
  249. Port->Flags &= ~PORT_MODEMCONTROL;
  250. CpSendModemString (Port, ModemString);
  251. }
  252. if (Port->Flags & PORT_MDM_CD) {
  253. //
  254. // We had a connection - if it's the connection has been
  255. // down for a few seconds, then send a string to the modem
  256. //
  257. if (CurrentTime.Second < Port->CarrierLostTime.Second)
  258. CurrentTime.Second += 60;
  259. if (CurrentTime.Second > Port->CarrierLostTime.Second + 10) {
  260. Port->Flags &= ~PORT_MDM_CD;
  261. CpSendModemString (Port, ModemString);
  262. }
  263. }
  264. }
  265. }
  266. if (!(Port->Flags & PORT_SAVED)) {
  267. return lsr;
  268. }
  269. KdCheckPowerButton();
  270. if (lsr == LastLsr && msr == LastMsr) {
  271. return lsr;
  272. }
  273. ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2;
  274. if (ringflag == 3) {
  275. //
  276. // The ring indicate line has toggled
  277. // Use modem control from now on
  278. //
  279. ringflag = 0;
  280. Port->Flags |= PORT_MODEMCONTROL | PORT_NOCDLTIME;
  281. Port->Flags &= ~PORT_MDM_CD;
  282. if (Port->Flags & PORT_DEFAULTRATE && Port->Baud != BD_9600) {
  283. //
  284. // Baud rate was never specified switch
  285. // to 9600 baud as default (for modem usage).
  286. //
  287. InbvDisplayString (MSG_DEBUG_9600);
  288. CpSetBaud (Port, BD_9600);
  289. //Port->Flags |= PORT_DISBAUD;
  290. }
  291. }
  292. for (i=0; i < 3; i++) {
  293. if (diagout[i]) {
  294. diagout[i]--;
  295. diagstr[10-i] = diagmsg[i];
  296. } else {
  297. diagstr[10-i] = ' ';
  298. }
  299. }
  300. diagstr[7] = (LastLsr & COM_DATRDY) ? 'VCR ' : ' ';
  301. diagstr[6] = (lsr & COM_OUTRDY) ? ' ' : 'DNS ';
  302. diagstr[5] = (msr & 0x10) ? 'STC ' : ' ';
  303. diagstr[4] = (msr & 0x20) ? 'RSD ' : ' ';
  304. diagstr[3] = (msr & 0x40) ? ' IR ' : ' ';
  305. diagstr[2] = (msr & 0x80) ? ' DC ' : ' ';
  306. diagstr[1] = (Port->Flags & PORT_MODEMCONTROL) ? 'MDM ' : ' ';
  307. diagstr[0] = ' ';
  308. #if 0
  309. if (Port->Flags & PORT_DISBAUD) {
  310. switch (Port->Baud) {
  311. case BD_9600: diagstr[0] = ' 69 '; break;
  312. case BD_14400: diagstr[0] = 'K41 '; break;
  313. case BD_19200: diagstr[0] = 'K91 '; break;
  314. case BD_56000: diagstr[0] = 'K65 '; break;
  315. }
  316. }
  317. #endif
  318. //HalpDisplayDebugStatus ((PUCHAR) diagstr, 11*4);
  319. LastLsr = lsr;
  320. LastMsr = msr;
  321. return lsr;
  322. }
  323. VOID
  324. CpPutByte (
  325. PCPPORT Port,
  326. UCHAR Byte
  327. )
  328. /*++
  329. Routine Description:
  330. Write a byte out to the specified com port.
  331. Arguments:
  332. Port - Address of CPPORT object
  333. Byte - data to emit
  334. --*/
  335. {
  336. UCHAR msr, lsr;
  337. if (KdCompDbgPortsPresent == FALSE) {
  338. return;
  339. }
  340. //
  341. // If modem control, make sure DSR, CTS and CD are all set before
  342. // sending any data.
  343. //
  344. while ((Port->Flags & PORT_MODEMCONTROL) &&
  345. (msr = KdReadUchar(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) {
  346. //
  347. // If no CD, and there's a charactor ready, eat it
  348. //
  349. lsr = CpReadLsr (Port, 0);
  350. if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) {
  351. KdReadUchar(Port->Address + COM_DAT);
  352. }
  353. }
  354. //
  355. // Wait for port to not be busy
  356. //
  357. while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ;
  358. //
  359. // Send the byte
  360. //
  361. KdWriteUchar(Port->Address + COM_DAT, Byte);
  362. }
  363. USHORT
  364. CpGetByte (
  365. PCPPORT Port,
  366. PUCHAR Byte,
  367. BOOLEAN WaitForByte
  368. )
  369. /*++
  370. Routine Description:
  371. Fetch a byte and return it.
  372. Arguments:
  373. Port - address of port object that describes hw port
  374. Byte - address of variable to hold the result
  375. WaitForByte - flag indicates wait for byte or not.
  376. Return Value:
  377. CP_GET_SUCCESS if data returned.
  378. CP_GET_NODATA if no data available, but no error.
  379. CP_GET_ERROR if error (overrun, parity, etc.)
  380. --*/
  381. {
  382. UCHAR lsr;
  383. UCHAR value;
  384. ULONG limitcount;
  385. //
  386. // Make sure DTR and CTS are set
  387. //
  388. // (What does CTS have to do with reading from a full duplex line???)
  389. //
  390. // Check to make sure the CPPORT we were passed has been initialized.
  391. // (The only time it won't be initialized is when the kernel debugger
  392. // is disabled, in which case we just return.)
  393. //
  394. if (Port->Address == NULL) {
  395. KdCheckPowerButton();
  396. return(CP_GET_NODATA);
  397. }
  398. if (KdCompDbgPortsPresent == FALSE) {
  399. if (CpReadLsr(Port, COM_DATRDY) == SERIAL_LSR_NOT_PRESENT) {
  400. return(CP_GET_NODATA);
  401. } else {
  402. CpSetBaud(Port, Port->Baud);
  403. KdCompDbgPortsPresent = TRUE;
  404. }
  405. }
  406. limitcount = WaitForByte ? TIMEOUT_COUNT : 1;
  407. while (limitcount != 0) {
  408. limitcount--;
  409. lsr = CpReadLsr(Port, COM_DATRDY);
  410. if (lsr == SERIAL_LSR_NOT_PRESENT) {
  411. return(CP_GET_NODATA);
  412. }
  413. if ((lsr & COM_DATRDY) == COM_DATRDY) {
  414. //
  415. // Check for errors
  416. //
  417. if (lsr & (COM_FE | COM_PE | COM_OE)) {
  418. *Byte = 0;
  419. return(CP_GET_ERROR);
  420. }
  421. //
  422. // fetch the byte
  423. //
  424. value = KdReadUchar(Port->Address + COM_DAT);
  425. if (Port->Flags & PORT_MODEMCONTROL) {
  426. //
  427. // Using modem control. If no CD, then skip this byte.
  428. //
  429. if ((KdReadUchar(Port->Address + COM_MSR) & MS_CD) == 0) {
  430. continue;
  431. }
  432. }
  433. *Byte = value & (UCHAR)0xff;
  434. return CP_GET_SUCCESS;
  435. }
  436. }
  437. LastLsr = 0;
  438. CpReadLsr (Port, 0);
  439. return CP_GET_NODATA;
  440. }
  441. BOOLEAN
  442. CpDoesPortExist(
  443. IN PUCHAR Address
  444. )
  445. /*++
  446. Routine Description:
  447. This routine will attempt to place the port into its
  448. diagnostic mode. If it does it will twiddle a bit in
  449. the modem control register. If the port exists this
  450. twiddling should show up in the modem status register.
  451. NOTE: This routine must be called before the device is
  452. enabled for interrupts, this includes setting the
  453. output2 bit in the modem control register.
  454. This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c.
  455. Arguments:
  456. Address - address of hw port.
  457. Return Value:
  458. TRUE - Port exists. Party on.
  459. FALSE - Port doesn't exist. Don't use it.
  460. --*/
  461. {
  462. UCHAR OldModemStatus;
  463. UCHAR ModemStatus;
  464. BOOLEAN ReturnValue = TRUE;
  465. //
  466. // Save the old value of the modem control register.
  467. //
  468. OldModemStatus = KdReadUchar(Address+COM_MCR);
  469. //
  470. // Set the port into diagnostic mode.
  471. //
  472. KdWriteUchar(
  473. Address+COM_MCR,
  474. SERIAL_MCR_LOOP
  475. );
  476. //
  477. // Bang on it again to make sure that all the lower bits
  478. // are clear.
  479. //
  480. KdWriteUchar(
  481. Address+COM_MCR,
  482. SERIAL_MCR_LOOP
  483. );
  484. //
  485. // Read the modem status register. The high for bits should
  486. // be clear.
  487. //
  488. ModemStatus = KdReadUchar(Address+COM_MSR);
  489. if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
  490. SERIAL_MSR_RI | SERIAL_MSR_DCD)) {
  491. ReturnValue = FALSE;
  492. goto AllDone;
  493. }
  494. //
  495. // So far so good. Now turn on OUT1 in the modem control register
  496. // and this should turn on ring indicator in the modem status register.
  497. //
  498. KdWriteUchar(
  499. Address+COM_MCR,
  500. (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP)
  501. );
  502. ModemStatus = KdReadUchar(Address+COM_MSR);
  503. if (!(ModemStatus & SERIAL_MSR_RI)) {
  504. ReturnValue = FALSE;
  505. goto AllDone;
  506. }
  507. AllDone: ;
  508. //
  509. // Put the modem control back into a clean state.
  510. //
  511. KdWriteUchar(
  512. Address+COM_MCR,
  513. OldModemStatus
  514. );
  515. return ReturnValue;
  516. }
  517. VOID
  518. CpWritePortUchar(
  519. IN PUCHAR Address,
  520. IN UCHAR Value
  521. )
  522. {
  523. WRITE_PORT_UCHAR(Address, Value);
  524. } // CpWritePortUchar()
  525. UCHAR
  526. CpReadPortUchar(
  527. IN PUCHAR Address
  528. )
  529. {
  530. return READ_PORT_UCHAR(Address);
  531. } // CpReadPortUchar()
  532. VOID
  533. CpWriteRegisterUchar(
  534. IN PUCHAR Address,
  535. IN UCHAR Value
  536. )
  537. {
  538. WRITE_REGISTER_UCHAR(Address, Value);
  539. } // CpWriteRegisterValue()
  540. UCHAR
  541. CpReadRegisterUchar(
  542. IN PUCHAR Address
  543. )
  544. {
  545. return READ_REGISTER_UCHAR(Address);
  546. } // CpReadRegisterUchar()