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.

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