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.

1558 lines
34 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 "bldr.h"
  12. #include "string.h"
  13. #include "stdlib.h"
  14. #include "stdio.h"
  15. #include "ntverp.h"
  16. #include "acpitabl.h"
  17. #ifdef _IA64_
  18. #include "bldria64.h"
  19. #endif
  20. //
  21. // Headless Port information.
  22. //
  23. ULONG BlTerminalDeviceId = 0;
  24. BOOLEAN BlTerminalConnected = FALSE;
  25. ULONG BlTerminalDelay = 0;
  26. HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
  27. //
  28. // Define COM Port registers.
  29. //
  30. #define COM_DAT 0x00
  31. #define COM_IEN 0x01 // interrupt enable register
  32. #define COM_FCR 0x02 // FIFO Control Register
  33. #define COM_LCR 0x03 // line control registers
  34. #define COM_MCR 0x04 // modem control reg
  35. #define COM_LSR 0x05 // line status register
  36. #define COM_MSR 0x06 // modem status register
  37. #define COM_DLL 0x00 // divisor latch least sig
  38. #define COM_DLM 0x01 // divisor latch most sig
  39. #define COM_BI 0x10
  40. #define COM_FE 0x08
  41. #define COM_PE 0x04
  42. #define COM_OE 0x02
  43. #define LC_DLAB 0x80 // divisor latch access bit
  44. #define CLOCK_RATE 0x1C200 // USART clock rate
  45. #define MC_DTRRTS 0x03 // Control bits to assert DTR and RTS
  46. #define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
  47. #define MS_CD 0x80
  48. #define COM_OUTRDY 0x20
  49. #define COM_DATRDY 0x01
  50. //
  51. // This bit controls the loopback testing mode of the device. Basically
  52. // the outputs are connected to the inputs (and vice versa).
  53. //
  54. #define SERIAL_MCR_LOOP 0x10
  55. //
  56. // This bit is used for general purpose output.
  57. //
  58. #define SERIAL_MCR_OUT1 0x04
  59. //
  60. // This bit contains the (complemented) state of the clear to send
  61. // (CTS) line.
  62. //
  63. #define SERIAL_MSR_CTS 0x10
  64. //
  65. // This bit contains the (complemented) state of the data set ready
  66. // (DSR) line.
  67. //
  68. #define SERIAL_MSR_DSR 0x20
  69. //
  70. // This bit contains the (complemented) state of the ring indicator
  71. // (RI) line.
  72. //
  73. #define SERIAL_MSR_RI 0x40
  74. //
  75. // This bit contains the (complemented) state of the data carrier detect
  76. // (DCD) line.
  77. //
  78. #define SERIAL_MSR_DCD 0x80
  79. typedef struct _CPPORT {
  80. PUCHAR Address;
  81. ULONG Baud;
  82. USHORT Flags;
  83. } CPPORT, *PCPPORT;
  84. #define PORT_DEFAULTRATE 0x0001 // baud rate not specified, using default
  85. #define PORT_MODEMCONTROL 0x0002 // using modem controls
  86. //
  87. // Define wait timeout value.
  88. //
  89. #define TIMEOUT_COUNT 1024 * 200
  90. extern
  91. VOID
  92. FwStallExecution(
  93. IN ULONG Microseconds
  94. );
  95. //
  96. // Routines for reading/writing bytes out to the UART.
  97. //
  98. UCHAR
  99. (*READ_UCHAR)(
  100. IN PUCHAR Addr
  101. );
  102. VOID
  103. (*WRITE_UCHAR)(
  104. IN PUCHAR Addr,
  105. IN UCHAR Value
  106. );
  107. //
  108. // Define COM Port function prototypes.
  109. //
  110. VOID
  111. CpInitialize (
  112. PCPPORT Port,
  113. PUCHAR Address,
  114. ULONG Rate
  115. );
  116. VOID
  117. CpEnableFifo(
  118. IN PUCHAR Address,
  119. IN BOOLEAN bEnable
  120. );
  121. LOGICAL
  122. CpDoesPortExist(
  123. IN PUCHAR Address
  124. );
  125. UCHAR
  126. CpReadLsr (
  127. IN PCPPORT Port,
  128. IN UCHAR Waiting
  129. );
  130. VOID
  131. CpSetBaud (
  132. PCPPORT Port,
  133. ULONG Rate
  134. );
  135. USHORT
  136. CpGetByte (
  137. PCPPORT Port,
  138. PUCHAR Byte,
  139. BOOLEAN WaitForData,
  140. BOOLEAN PollOnly
  141. );
  142. VOID
  143. CpPutByte (
  144. PCPPORT Port,
  145. UCHAR Byte
  146. );
  147. //
  148. // Define debugger port initial state.
  149. //
  150. CPPORT Port[4] = {
  151. {NULL, 0, PORT_DEFAULTRATE},
  152. {NULL, 0, PORT_DEFAULTRATE},
  153. {NULL, 0, PORT_DEFAULTRATE},
  154. {NULL, 0, PORT_DEFAULTRATE}
  155. };
  156. //
  157. // This is how we find table information from
  158. // the ACPI table index.
  159. //
  160. extern PDESCRIPTION_HEADER
  161. BlFindACPITable(
  162. IN PCHAR TableName,
  163. IN ULONG TableLength
  164. );
  165. //
  166. // We'll use these to fill in some function pointers,
  167. // which in turn will be used to read/write from the
  168. // UART. We can't simply assign the function pointers
  169. // to point to READ_PORT_UCHAR/READ_REGISTER_UCHAR and
  170. // WRITE_PORT_UCHAR/WRITE_REGISTER_UCHAR, because in
  171. // the case of IA64, some of these functions are macros.
  172. //
  173. // To get around this, build these dummy functions that
  174. // will inturn simply call the correct READ/WRITE functions/macros.
  175. //
  176. UCHAR
  177. MY_READ_PORT_UCHAR( IN PUCHAR Addr )
  178. {
  179. return( READ_PORT_UCHAR(Addr) );
  180. }
  181. UCHAR
  182. MY_READ_REGISTER_UCHAR( IN PUCHAR Addr )
  183. {
  184. return( READ_REGISTER_UCHAR(Addr) );
  185. }
  186. VOID
  187. MY_WRITE_PORT_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  188. {
  189. WRITE_PORT_UCHAR(Addr, Value);
  190. }
  191. VOID
  192. MY_WRITE_REGISTER_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  193. {
  194. WRITE_REGISTER_UCHAR(Addr, Value);
  195. }
  196. LOGICAL
  197. BlRetrieveBIOSRedirectionInformation(
  198. VOID
  199. )
  200. /*++
  201. Routine Description:
  202. This functions retrieves the COM port information from the ACPI
  203. table.
  204. Arguments:
  205. We'll be filling in the LoaderRedirectionInformation structure.
  206. Returned Value:
  207. TRUE - If a debug port is found.
  208. --*/
  209. {
  210. PDEBUG_PORT_TABLE pPortTable = NULL;
  211. PUCHAR CurrentAddress = NULL;
  212. UCHAR Checksum;
  213. ULONG i;
  214. ULONG CheckLength;
  215. pPortTable = (PDEBUG_PORT_TABLE)BlFindACPITable( "SPCR",
  216. sizeof(DEBUG_PORT_TABLE) );
  217. if( pPortTable ) {
  218. //
  219. // generate a checksum for later validation.
  220. //
  221. CurrentAddress = (PUCHAR)pPortTable;
  222. CheckLength = pPortTable->Header.Length;
  223. Checksum = 0;
  224. for( i = 0; i < CheckLength; i++ ) {
  225. Checksum += CurrentAddress[i];
  226. }
  227. if(
  228. // checksum is okay?
  229. (Checksum == 0) &&
  230. // device address defined?
  231. ((UCHAR UNALIGNED *)pPortTable->BaseAddress.Address.LowPart != (UCHAR *)NULL) &&
  232. // he better be in system or memory I/O
  233. // note: 0 - systemI/O
  234. // 1 - memory mapped I/O
  235. ((pPortTable->BaseAddress.AddressSpaceID == 0) ||
  236. (pPortTable->BaseAddress.AddressSpaceID == 1))
  237. ) {
  238. if( pPortTable->BaseAddress.AddressSpaceID == 0 ) {
  239. LoaderRedirectionInformation.IsMMIODevice = TRUE;
  240. } else {
  241. LoaderRedirectionInformation.IsMMIODevice = FALSE;
  242. }
  243. //
  244. // We got the table. Now dig out the information we want.
  245. // See definitiion of DEBUG_PORT_TABLE (acpitabl.h)
  246. //
  247. LoaderRedirectionInformation.UsedBiosSettings = TRUE;
  248. LoaderRedirectionInformation.PortNumber = 3;
  249. LoaderRedirectionInformation.PortAddress = (UCHAR UNALIGNED *)(pPortTable->BaseAddress.Address.LowPart);
  250. if( pPortTable->BaudRate == 7 ) {
  251. LoaderRedirectionInformation.BaudRate = BD_115200;
  252. } else if( pPortTable->BaudRate == 6 ) {
  253. LoaderRedirectionInformation.BaudRate = BD_57600;
  254. } else if( pPortTable->BaudRate == 4 ) {
  255. LoaderRedirectionInformation.BaudRate = BD_19200;
  256. } else {
  257. LoaderRedirectionInformation.BaudRate = BD_9600;
  258. }
  259. LoaderRedirectionInformation.Parity = pPortTable->Parity;
  260. LoaderRedirectionInformation.StopBits = pPortTable->StopBits;
  261. LoaderRedirectionInformation.TerminalType = pPortTable->TerminalType;
  262. //
  263. // If this is a new DEBUG_PORT_TABLE, then it's got the PCI device
  264. // information.
  265. //
  266. if( pPortTable->Header.Length >= sizeof(DEBUG_PORT_TABLE) ) {
  267. LoaderRedirectionInformation.PciDeviceId = (USHORT UNALIGNED)pPortTable->PciDeviceId;
  268. LoaderRedirectionInformation.PciVendorId = (USHORT UNALIGNED)pPortTable->PciVendorId;
  269. LoaderRedirectionInformation.PciBusNumber = (UCHAR)pPortTable->PciBusNumber;
  270. LoaderRedirectionInformation.PciSlotNumber = (UCHAR)pPortTable->PciSlotNumber;
  271. LoaderRedirectionInformation.PciFunctionNumber = (UCHAR)pPortTable->PciFunctionNumber;
  272. LoaderRedirectionInformation.PciFlags = (ULONG UNALIGNED)pPortTable->PciFlags;
  273. } else {
  274. //
  275. // There's no PCI device information in this table.
  276. //
  277. LoaderRedirectionInformation.PciDeviceId = (USHORT)0xFFFF;
  278. LoaderRedirectionInformation.PciVendorId = (USHORT)0xFFFF;
  279. LoaderRedirectionInformation.PciBusNumber = 0;
  280. LoaderRedirectionInformation.PciSlotNumber = 0;
  281. LoaderRedirectionInformation.PciFunctionNumber = 0;
  282. LoaderRedirectionInformation.PciFlags = 0;
  283. }
  284. return TRUE;
  285. }
  286. }
  287. return FALSE;
  288. }
  289. LOGICAL
  290. BlPortInitialize(
  291. IN ULONG BaudRate,
  292. IN ULONG PortNumber,
  293. IN PUCHAR PortAddress OPTIONAL,
  294. IN BOOLEAN ReInitialize,
  295. OUT PULONG BlFileId
  296. )
  297. /*++
  298. Routine Description:
  299. This functions initializes the com port.
  300. Arguments:
  301. BaudRate - Supplies an optional baud rate.
  302. PortNumber - supplies an optinal port number.
  303. ReInitialize - Set to TRUE if we already have this port open, but for some
  304. reason need to completely reset the port. Otw it should be FALSE.
  305. BlFileId - A place to store a fake file Id, if successful.
  306. Returned Value:
  307. TRUE - If a debug port is found, and BlFileId will point to a location within Port[].
  308. --*/
  309. {
  310. //
  311. // Make guesses on any inputs that we didn't get.
  312. //
  313. if( BaudRate == 0 ) {
  314. BaudRate = BD_19200;
  315. }
  316. if( PortNumber == 0 ) {
  317. //
  318. // Try COM2, then COM1
  319. //
  320. if (CpDoesPortExist((PUCHAR)COM2_PORT)) {
  321. PortNumber = 2;
  322. PortAddress = (PUCHAR)COM2_PORT;
  323. } else if (CpDoesPortExist((PUCHAR)COM1_PORT)) {
  324. PortNumber = 1;
  325. PortAddress = (PUCHAR)COM1_PORT;
  326. } else {
  327. return FALSE;
  328. }
  329. }
  330. //
  331. // If the user didn't send us a port address, then
  332. // guess based on the COM port number.
  333. //
  334. if( PortAddress == 0 ) {
  335. switch (PortNumber) {
  336. case 1:
  337. PortAddress = (PUCHAR)COM1_PORT;
  338. break;
  339. case 2:
  340. PortAddress = (PUCHAR)COM2_PORT;
  341. break;
  342. case 3:
  343. PortAddress = (PUCHAR)COM3_PORT;
  344. break;
  345. default:
  346. PortNumber = 4;
  347. PortAddress = (PUCHAR)COM4_PORT;
  348. }
  349. }
  350. //
  351. // we need to handle the case where we're dealing with
  352. // MMIO space (as opposed to System I/O space).
  353. //
  354. if( LoaderRedirectionInformation.IsMMIODevice ) {
  355. PHYSICAL_ADDRESS PhysAddr;
  356. PVOID MyPtr;
  357. PhysAddr.LowPart = PtrToUlong(PortAddress);
  358. PhysAddr.HighPart = 0;
  359. MyPtr = MmMapIoSpace(PhysAddr,(1+COM_MSR),TRUE);
  360. PortAddress = MyPtr;
  361. READ_UCHAR = MY_READ_REGISTER_UCHAR;
  362. WRITE_UCHAR = MY_WRITE_REGISTER_UCHAR;
  363. } else {
  364. // System IO space.
  365. READ_UCHAR = MY_READ_PORT_UCHAR;
  366. WRITE_UCHAR = MY_WRITE_PORT_UCHAR;
  367. }
  368. //
  369. // See if the port even exists...
  370. //
  371. if (!CpDoesPortExist(PortAddress)) {
  372. if( LoaderRedirectionInformation.IsMMIODevice == FALSE ) {
  373. //
  374. // Don't fail on this if this is a headless MMIO device.
  375. // Hack required for HP.
  376. //
  377. return FALSE;
  378. }
  379. }
  380. //
  381. // Check if the port is already in use, and this is a first init.
  382. //
  383. if (!ReInitialize && (Port[PortNumber-1].Address != NULL)) {
  384. return FALSE;
  385. }
  386. //
  387. // Check if someone tries to reinit a port that is not open.
  388. //
  389. if (ReInitialize && (Port[PortNumber-1].Address == NULL)) {
  390. return FALSE;
  391. }
  392. //
  393. // Initialize the specified port.
  394. //
  395. CpInitialize(&(Port[PortNumber-1]),
  396. PortAddress,
  397. BaudRate);
  398. *BlFileId = (PortNumber-1);
  399. return TRUE;
  400. }
  401. VOID
  402. BlLoadGUID(
  403. VOID
  404. )
  405. /*++
  406. Routine Description:
  407. Attempt to find the System GUID. If we find it, load it into
  408. the LoaderRedirectionInformation structure.
  409. Arguments:
  410. None.
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. #include <smbios.h>
  416. #include <wmidata.h>
  417. PUCHAR CurrentAddress = NULL;
  418. PUCHAR EndingAddress = NULL;
  419. UCHAR Checksum;
  420. ULONG i;
  421. ULONG CheckLength;
  422. BOOLEAN FoundIt = FALSE;
  423. PSYSID_UUID_ENTRY UuidEntry = NULL;
  424. CurrentAddress = (PUCHAR)SYSID_EPS_SEARCH_START;
  425. EndingAddress = CurrentAddress + SYSID_EPS_SEARCH_SIZE;
  426. while( CurrentAddress < EndingAddress ) {
  427. UuidEntry = (PSYSID_UUID_ENTRY)CurrentAddress;
  428. if( memcmp(UuidEntry->Type, SYSID_TYPE_UUID, 0x6) == 0 ) {
  429. //
  430. // See if the checksum matches too.
  431. //
  432. CheckLength = UuidEntry->Length;
  433. Checksum = 0;
  434. for( i = 0; i < CheckLength; i++ ) {
  435. Checksum += CurrentAddress[i];
  436. }
  437. if( Checksum == 0 ) {
  438. FoundIt = TRUE;
  439. RtlCopyMemory( &LoaderRedirectionInformation.SystemGUID,
  440. UuidEntry->UUID,
  441. sizeof(GUID) );
  442. break;
  443. }
  444. }
  445. CurrentAddress++;
  446. }
  447. if( !FoundIt ) {
  448. RtlZeroMemory( &LoaderRedirectionInformation.SystemGUID,
  449. sizeof(SYSID_UUID) );
  450. }
  451. return;
  452. }
  453. VOID
  454. BlEnableFifo(
  455. IN ULONG DeviceId,
  456. IN BOOLEAN bEnable
  457. )
  458. /*++
  459. Routine Description:
  460. This routine will attempt to enable the FIFO in the 16550 UART.
  461. Note that the behaviour is undefined for the 16450, but practically,
  462. this should have no effect.
  463. Arguments:
  464. DeviceId - Value returned by BlPortInitialize()
  465. bEnable - if TRUE, FIFO is enabled
  466. if FALSE, FIFO is disabled
  467. Return Value:
  468. None
  469. --*/
  470. {
  471. CpEnableFifo(
  472. Port[DeviceId].Address,
  473. bEnable
  474. );
  475. }
  476. VOID
  477. BlInitializeHeadlessPort(
  478. VOID
  479. )
  480. /*++
  481. Routine Description:
  482. Does x86-specific initialization of a dumb terminal connected to a serial port. Currently,
  483. it assumes baud rate and com port are pre-initialized, but this can be changed in the future
  484. by reading the values from boot.ini or someplace.
  485. Arguments:
  486. None.
  487. Return Value:
  488. None.
  489. --*/
  490. {
  491. ULONG i;
  492. PUCHAR TmpBuffer;
  493. if( (LoaderRedirectionInformation.PortNumber == 0) ||
  494. !(LoaderRedirectionInformation.PortAddress) ) {
  495. //
  496. // This means that no one has filled in the LoaderRedirectionInformation
  497. // structure, which means that we aren't redirecting right now.
  498. // See if the BIOS was redirecting. If so, pick up those settings
  499. // and use them.
  500. //
  501. BlRetrieveBIOSRedirectionInformation();
  502. }
  503. if( LoaderRedirectionInformation.PortNumber ) {
  504. //
  505. // We really need to make sure there's an address associated with
  506. // this port and not just a port number.
  507. //
  508. if( LoaderRedirectionInformation.PortAddress == NULL ) {
  509. switch( LoaderRedirectionInformation.PortNumber ) {
  510. case 4:
  511. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM4_PORT;
  512. break;
  513. case 3:
  514. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM3_PORT;
  515. break;
  516. case 2:
  517. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM2_PORT;
  518. break;
  519. case 1:
  520. default:
  521. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM1_PORT;
  522. break;
  523. }
  524. }
  525. //
  526. // Either we just created a LoaderRedirectionInformation, or it was
  527. // built before we ever got into this function. Either way, we should
  528. // go try and initialize the port he wants to talk through.
  529. //
  530. BlTerminalConnected = (BOOLEAN)BlPortInitialize(LoaderRedirectionInformation.BaudRate,
  531. LoaderRedirectionInformation.PortNumber,
  532. LoaderRedirectionInformation.PortAddress,
  533. BlTerminalConnected,
  534. &BlTerminalDeviceId);
  535. if (BlIsTerminalConnected()) {
  536. //
  537. // Enable the FIFO on the UART so we reduce the chance of a character
  538. // getting dropped.
  539. //
  540. BlEnableFifo(
  541. BlTerminalDeviceId,
  542. TRUE
  543. );
  544. //
  545. // Go get the machine's GUID.
  546. //
  547. BlLoadGUID();
  548. //
  549. // Figure time to delay based on baudrate. Note: we do this calculation
  550. // to be at 60% of the baud rate because it appears that FwStallExecution
  551. // is extremely inaccurate, and that if we dont go slow enough a lot of
  552. // screen attributes being sent in a row causes a real vt100 to drop
  553. // characters that follows as it repaints/clears/whatever the screen.
  554. //
  555. if( LoaderRedirectionInformation.BaudRate == 0 ) {
  556. LoaderRedirectionInformation.BaudRate = BD_9600;
  557. }
  558. BlTerminalDelay = LoaderRedirectionInformation.BaudRate;
  559. BlTerminalDelay = BlTerminalDelay / 10; // 10 bits per character (8-1-1) is the max.
  560. BlTerminalDelay = ((1000000 / BlTerminalDelay) * 10) / 6; // 60% speed.
  561. //
  562. // Make sure there are no stale attributes on the terminal
  563. // sitting at the other end of our headless port.
  564. //
  565. // <CSI>m (turn attributes off)
  566. TmpBuffer = "\033[m";
  567. for( i = 0; i < strlen(TmpBuffer); i++ ) {
  568. BlPortPutByte( BlTerminalDeviceId, TmpBuffer[i]);
  569. FwStallExecution(BlTerminalDelay);
  570. }
  571. } else {
  572. //
  573. // Make sure we don't have any redirection information
  574. // hanging around if we didn't pass BlIsTerminalConnected()
  575. //
  576. RtlZeroMemory( &LoaderRedirectionInformation, sizeof(HEADLESS_LOADER_BLOCK) );
  577. }
  578. } else {
  579. BlTerminalConnected = FALSE;
  580. }
  581. }
  582. LOGICAL
  583. BlTerminalAttached(
  584. IN ULONG DeviceId
  585. )
  586. /*++
  587. Routine Description:
  588. This routine will attempt to discover if a terminal is attached.
  589. Arguments:
  590. DeviceId - Value returned by BlPortInitialize()
  591. Return Value:
  592. TRUE - Port seems to have something attached.
  593. FALSE - Port doesn't seem to have anything attached.
  594. --*/
  595. {
  596. UCHAR ModemStatus;
  597. BOOLEAN ReturnValue;
  598. //
  599. // Check for a carrier.
  600. //
  601. ModemStatus = READ_UCHAR(Port[DeviceId].Address + COM_MSR);
  602. ReturnValue = ((ModemStatus & MS_DSRCTSCD) == MS_DSRCTSCD);
  603. return ReturnValue;
  604. }
  605. VOID
  606. BlSetHeadlessRestartBlock(
  607. IN PTFTP_RESTART_BLOCK RestartBlock
  608. )
  609. /*++
  610. Routine Description:
  611. This routine will fill in the areas of the restart block that are appropriate
  612. for the headless server effort.
  613. Arguments:
  614. RestartBlock - The magic structure for holding restart information from oschoice
  615. to setupldr.
  616. Return Value:
  617. None.
  618. --*/
  619. {
  620. if( LoaderRedirectionInformation.PortNumber ) {
  621. RestartBlock->HeadlessUsedBiosSettings = (ULONG)LoaderRedirectionInformation.UsedBiosSettings;
  622. RestartBlock->HeadlessPortNumber = (ULONG)LoaderRedirectionInformation.PortNumber;
  623. RestartBlock->HeadlessPortAddress = (PUCHAR)LoaderRedirectionInformation.PortAddress;
  624. RestartBlock->HeadlessBaudRate = (ULONG)LoaderRedirectionInformation.BaudRate;
  625. RestartBlock->HeadlessParity = (ULONG)LoaderRedirectionInformation.Parity;
  626. RestartBlock->HeadlessStopBits = (ULONG)LoaderRedirectionInformation.StopBits;
  627. RestartBlock->HeadlessTerminalType = (ULONG)LoaderRedirectionInformation.TerminalType;
  628. RestartBlock->HeadlessPciDeviceId = LoaderRedirectionInformation.PciDeviceId;
  629. RestartBlock->HeadlessPciVendorId = LoaderRedirectionInformation.PciVendorId;
  630. RestartBlock->HeadlessPciBusNumber = LoaderRedirectionInformation.PciBusNumber;
  631. RestartBlock->HeadlessPciSlotNumber = LoaderRedirectionInformation.PciSlotNumber;
  632. RestartBlock->HeadlessPciFunctionNumber = LoaderRedirectionInformation.PciFunctionNumber;
  633. RestartBlock->HeadlessPciFlags = LoaderRedirectionInformation.PciFlags;
  634. }
  635. }
  636. VOID
  637. BlGetHeadlessRestartBlock(
  638. IN PTFTP_RESTART_BLOCK RestartBlock,
  639. IN BOOLEAN RestartBlockValid
  640. )
  641. /*++
  642. Routine Description:
  643. This routine will get all the information from a restart block
  644. for the headless server effort.
  645. Arguments:
  646. RestartBlock - The magic structure for holding restart information from oschoice
  647. to setupldr.
  648. RestartBlockValid - Is this block valid (full of good info)?
  649. Return Value:
  650. None.
  651. --*/
  652. {
  653. LoaderRedirectionInformation.UsedBiosSettings = (BOOLEAN)RestartBlock->HeadlessUsedBiosSettings;
  654. LoaderRedirectionInformation.DataBits = 0;
  655. LoaderRedirectionInformation.StopBits = (UCHAR)RestartBlock->HeadlessStopBits;
  656. LoaderRedirectionInformation.Parity = (BOOLEAN)RestartBlock->HeadlessParity;
  657. LoaderRedirectionInformation.BaudRate = (ULONG)RestartBlock->HeadlessBaudRate;;
  658. LoaderRedirectionInformation.PortNumber = (ULONG)RestartBlock->HeadlessPortNumber;
  659. LoaderRedirectionInformation.PortAddress = (PUCHAR)RestartBlock->HeadlessPortAddress;
  660. LoaderRedirectionInformation.TerminalType = (UCHAR)RestartBlock->HeadlessTerminalType;
  661. LoaderRedirectionInformation.PciDeviceId = (USHORT)RestartBlock->HeadlessPciDeviceId;
  662. LoaderRedirectionInformation.PciVendorId = (USHORT)RestartBlock->HeadlessPciVendorId;
  663. LoaderRedirectionInformation.PciBusNumber = (UCHAR)RestartBlock->HeadlessPciBusNumber;
  664. LoaderRedirectionInformation.PciSlotNumber = (UCHAR)RestartBlock->HeadlessPciSlotNumber;
  665. LoaderRedirectionInformation.PciFunctionNumber = (UCHAR)RestartBlock->HeadlessPciFunctionNumber;
  666. LoaderRedirectionInformation.PciFlags = (ULONG)RestartBlock->HeadlessPciFlags;
  667. }
  668. ULONG
  669. BlPortGetByte (
  670. IN ULONG BlFileId,
  671. OUT PUCHAR Input
  672. )
  673. /*++
  674. Routine Description:
  675. Fetch a byte from the port and return it.
  676. Arguments:
  677. BlFileId - The port to read from.
  678. Input - Returns the data byte.
  679. Return Value:
  680. CP_GET_SUCCESS is returned if a byte is successfully read from the
  681. kernel debugger line.
  682. CP_GET_ERROR is returned if error encountered during reading.
  683. CP_GET_NODATA is returned if timeout.
  684. --*/
  685. {
  686. return CpGetByte(&Port[BlFileId], Input, TRUE, FALSE);
  687. }
  688. VOID
  689. BlPortPutByte (
  690. IN ULONG BlFileId,
  691. IN UCHAR Output
  692. )
  693. /*++
  694. Routine Description:
  695. Write a byte to the port.
  696. Arguments:
  697. BlFileId - The port to write to.
  698. Output - Supplies the output data byte.
  699. Return Value:
  700. None.
  701. --*/
  702. {
  703. CpPutByte(&Port[BlFileId], Output);
  704. }
  705. ULONG
  706. BlPortPollByte (
  707. IN ULONG BlFileId,
  708. OUT PUCHAR Input
  709. )
  710. /*++
  711. Routine Description:
  712. Fetch a byte from the port and return it if one is available.
  713. Arguments:
  714. BlFileId - The port to poll.
  715. Input - Returns the data byte.
  716. Return Value:
  717. CP_GET_SUCCESS is returned if a byte is successfully read.
  718. CP_GET_ERROR is returned if error encountered during reading.
  719. CP_GET_NODATA is returned if timeout.
  720. --*/
  721. {
  722. return CpGetByte(&Port[BlFileId], Input, FALSE, FALSE);
  723. }
  724. ULONG
  725. BlPortPollOnly (
  726. IN ULONG BlFileId
  727. )
  728. /*++
  729. Routine Description:
  730. Check if a byte is available
  731. Arguments:
  732. BlFileId - The port to poll.
  733. Return Value:
  734. CP_GET_SUCCESS is returned if a byte is ready.
  735. CP_GET_ERROR is returned if error encountered.
  736. CP_GET_NODATA is returned if timeout.
  737. --*/
  738. {
  739. CHAR Input;
  740. return CpGetByte(&Port[BlFileId], &Input, FALSE, TRUE);
  741. }
  742. VOID
  743. CpInitialize (
  744. PCPPORT Port,
  745. PUCHAR Address,
  746. ULONG Rate
  747. )
  748. /*++
  749. Routine Description:
  750. Fill in the com port port object, set the initial baud rate,
  751. turn on the hardware.
  752. Arguments:
  753. Port - address of port object
  754. Address - port address of the com port
  755. (CP_COM1_PORT, CP_COM2_PORT)
  756. Rate - baud rate (CP_BD_150 ... CP_BD_19200)
  757. --*/
  758. {
  759. PUCHAR hwport;
  760. UCHAR mcr, ier;
  761. Port->Address = Address;
  762. Port->Baud = 0;
  763. CpSetBaud(Port, Rate);
  764. //
  765. // Assert DTR, RTS.
  766. //
  767. hwport = Port->Address;
  768. hwport += COM_MCR;
  769. mcr = MC_DTRRTS;
  770. WRITE_UCHAR(hwport, mcr);
  771. hwport = Port->Address;
  772. hwport += COM_IEN;
  773. ier = 0;
  774. WRITE_UCHAR(hwport, ier);
  775. return;
  776. }
  777. VOID
  778. CpEnableFifo(
  779. IN PUCHAR Address,
  780. IN BOOLEAN bEnable
  781. )
  782. /*++
  783. Routine Description:
  784. This routine will attempt to enable the FIFO in the
  785. UART at the address specified. If this is a 16550,
  786. this works. The behaviour on a 16450 is not defined,
  787. but practically, there is no effect.
  788. Arguments:
  789. Address - address of hw port.
  790. bEnable - if TRUE, FIFO is enabled
  791. if FALSE, FIFO is disabled
  792. Return Value:
  793. None
  794. --*/
  795. {
  796. //
  797. // Enable the FIFO in the UART. The behaviour is undefined on the
  798. // 16450, but practically, it should just ignore the command.
  799. //
  800. PUCHAR hwport = Address;
  801. hwport += COM_FCR;
  802. WRITE_UCHAR(hwport, bEnable);
  803. }
  804. LOGICAL
  805. CpDoesPortExist(
  806. IN PUCHAR Address
  807. )
  808. /*++
  809. Routine Description:
  810. This routine will attempt to place the port into its
  811. diagnostic mode. If it does it will twiddle a bit in
  812. the modem control register. If the port exists this
  813. twiddling should show up in the modem status register.
  814. NOTE: This routine must be called before the device is
  815. enabled for interrupts, this includes setting the
  816. output2 bit in the modem control register.
  817. This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c.
  818. Arguments:
  819. Address - address of hw port.
  820. Return Value:
  821. TRUE - Port exists.
  822. FALSE - Port doesn't exist.
  823. --*/
  824. {
  825. UCHAR OldModemStatus;
  826. UCHAR ModemStatus;
  827. BOOLEAN ReturnValue = TRUE;
  828. //
  829. // Save the old value of the modem control register.
  830. //
  831. OldModemStatus = READ_UCHAR(Address + COM_MCR);
  832. //
  833. // Set the port into diagnostic mode.
  834. //
  835. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  836. //
  837. // Bang on it again to make sure that all the lower bits
  838. // are clear.
  839. //
  840. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  841. //
  842. // Read the modem status register. The high for bits should
  843. // be clear.
  844. //
  845. ModemStatus = READ_UCHAR(Address + COM_MSR);
  846. if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
  847. SERIAL_MSR_RI | SERIAL_MSR_DCD)) {
  848. ReturnValue = FALSE;
  849. goto EndFirstTest;
  850. }
  851. //
  852. // So far so good. Now turn on OUT1 in the modem control register
  853. // and this should turn on ring indicator in the modem status register.
  854. //
  855. WRITE_UCHAR(Address + COM_MCR, (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP));
  856. ModemStatus = READ_UCHAR(Address + COM_MSR);
  857. if (!(ModemStatus & SERIAL_MSR_RI)) {
  858. ReturnValue = FALSE;
  859. goto EndFirstTest;
  860. }
  861. EndFirstTest:
  862. if( ReturnValue == FALSE ) {
  863. UCHAR OldIEValue = 0, OldLCValue = 0;
  864. USHORT Value1 = 0, Value2 = 0;
  865. UCHAR PreviousLineControl = 0;
  866. //
  867. // We failed the loopback test. Test another way.
  868. //
  869. // Remember the original Interrupt Enable setting and
  870. // Line Control setting.
  871. OldIEValue = READ_UCHAR( Address + COM_IEN );
  872. OldLCValue = READ_UCHAR( Address + COM_LCR );
  873. // Make sure we aren't accessing the divisor latch.
  874. WRITE_UCHAR( Address + COM_LCR, OldLCValue | LC_DLAB );
  875. WRITE_UCHAR( Address + COM_IEN, 0xF );
  876. Value1 = READ_UCHAR( Address + COM_IEN );
  877. Value1 = Value1 << 8;
  878. Value1 |= READ_UCHAR( Address + COM_DAT );
  879. // Now read the divisor latch.
  880. PreviousLineControl = READ_UCHAR( Address + COM_LCR );
  881. WRITE_UCHAR( Address + COM_LCR, (UCHAR)(PreviousLineControl | LC_DLAB) );
  882. Value2 = READ_UCHAR( Address + COM_DLL );
  883. Value2 = Value2 + (READ_UCHAR(Address + COM_DLM) << 8 );
  884. WRITE_UCHAR( Address + COM_LCR, PreviousLineControl );
  885. // Restore original Line Control register and
  886. // Interrupt Enable setting.
  887. WRITE_UCHAR( Address + COM_LCR, OldLCValue );
  888. WRITE_UCHAR( Address + COM_IEN, OldIEValue );
  889. if( Value1 == Value2 ) {
  890. //
  891. // We passed this test. Reset ReturnValue
  892. // appropriately.
  893. //
  894. ReturnValue = TRUE;
  895. }
  896. }
  897. //
  898. // Put the modem control back into a clean state.
  899. //
  900. WRITE_UCHAR(Address + COM_MCR, OldModemStatus);
  901. return ReturnValue;
  902. }
  903. UCHAR
  904. CpReadLsr (
  905. PCPPORT Port,
  906. UCHAR waiting
  907. )
  908. /*++
  909. Routine Description:
  910. Read LSR byte from specified port. If HAL owns port & display
  911. it will also cause a debug status to be kept up to date.
  912. Handles entering & exiting modem control mode for debugger.
  913. Arguments:
  914. Port - Address of CPPORT
  915. Returns:
  916. Byte read from port
  917. --*/
  918. {
  919. static UCHAR ringflag = 0;
  920. UCHAR lsr, msr;
  921. lsr = READ_UCHAR(Port->Address + COM_LSR);
  922. if ((lsr & waiting) == 0) {
  923. msr = READ_UCHAR (Port->Address + COM_MSR);
  924. ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2;
  925. if (ringflag == 3) {
  926. //
  927. // The ring indicate line has toggled, use modem control from
  928. // now on.
  929. //
  930. Port->Flags |= PORT_MODEMCONTROL;
  931. }
  932. }
  933. return lsr;
  934. }
  935. VOID
  936. CpSetBaud (
  937. PCPPORT Port,
  938. ULONG Rate
  939. )
  940. /*++
  941. Routine Description:
  942. Set the baud rate for the port and record it in the port object.
  943. Arguments:
  944. Port - address of port object
  945. Rate - baud rate (CP_BD_150 ... CP_BD_56000)
  946. --*/
  947. {
  948. ULONG divisorlatch;
  949. PUCHAR hwport;
  950. UCHAR lcr;
  951. //
  952. // compute the divsor
  953. //
  954. divisorlatch = CLOCK_RATE / Rate;
  955. //
  956. // set the divisor latch access bit (DLAB) in the line control reg
  957. //
  958. hwport = Port->Address;
  959. hwport += COM_LCR; // hwport = LCR register
  960. lcr = READ_UCHAR(hwport);
  961. lcr |= LC_DLAB;
  962. WRITE_UCHAR(hwport, lcr);
  963. //
  964. // set the divisor latch value.
  965. //
  966. hwport = Port->Address;
  967. hwport += COM_DLM; // divisor latch msb
  968. WRITE_UCHAR(hwport, (UCHAR)((divisorlatch >> 8) & 0xff));
  969. hwport--; // divisor latch lsb
  970. WRITE_UCHAR(hwport, (UCHAR)(divisorlatch & 0xff));
  971. //
  972. // Set LCR to 3. (3 is a magic number in the original assembler)
  973. //
  974. hwport = Port->Address;
  975. hwport += COM_LCR;
  976. WRITE_UCHAR(hwport, 3);
  977. //
  978. // Remember the baud rate
  979. //
  980. Port->Baud = Rate;
  981. return;
  982. }
  983. USHORT
  984. CpGetByte (
  985. PCPPORT Port,
  986. PUCHAR Byte,
  987. BOOLEAN WaitForByte,
  988. BOOLEAN PollOnly
  989. )
  990. /*++
  991. Routine Description:
  992. Fetch a byte and return it.
  993. Arguments:
  994. Port - address of port object that describes hw port
  995. Byte - address of variable to hold the result
  996. WaitForByte - flag indicates wait for byte or not.
  997. PollOnly - flag indicates whether to return immediately, not reading the byte, or not.
  998. Return Value:
  999. CP_GET_SUCCESS if data returned, or if data is ready and PollOnly is TRUE.
  1000. CP_GET_NODATA if no data available, but no error.
  1001. CP_GET_ERROR if error (overrun, parity, etc.)
  1002. --*/
  1003. {
  1004. UCHAR lsr;
  1005. UCHAR value;
  1006. ULONG limitcount;
  1007. //
  1008. // Check to make sure the CPPORT we were passed has been initialized.
  1009. // (The only time it won't be initialized is when the kernel debugger
  1010. // is disabled, in which case we just return.)
  1011. //
  1012. if (Port->Address == NULL) {
  1013. return CP_GET_NODATA;
  1014. }
  1015. limitcount = WaitForByte ? TIMEOUT_COUNT : 1;
  1016. while (limitcount != 0) {
  1017. limitcount--;
  1018. lsr = CpReadLsr(Port, COM_DATRDY);
  1019. if ((lsr & COM_DATRDY) == COM_DATRDY) {
  1020. //
  1021. // Check for errors
  1022. //
  1023. //
  1024. // If we get an overrun error, and there is data ready, we should
  1025. // return the data we have, so we ignore overrun errors. Reading
  1026. // the LSR clears this bit, so the first read already cleared the
  1027. // overrun error.
  1028. //
  1029. if (lsr & (COM_FE | COM_PE)) {
  1030. *Byte = 0;
  1031. return CP_GET_ERROR;
  1032. }
  1033. if (PollOnly) {
  1034. return CP_GET_SUCCESS;
  1035. }
  1036. //
  1037. // fetch the byte
  1038. //
  1039. *Byte = READ_UCHAR(Port->Address + COM_DAT);
  1040. if (Port->Flags & PORT_MODEMCONTROL) {
  1041. //
  1042. // Using modem control. If no CD, then skip this byte.
  1043. //
  1044. if ((READ_UCHAR(Port->Address + COM_MSR) & MS_CD) == 0) {
  1045. continue;
  1046. }
  1047. }
  1048. return CP_GET_SUCCESS;
  1049. }
  1050. }
  1051. CpReadLsr(Port, 0);
  1052. return CP_GET_NODATA;
  1053. }
  1054. VOID
  1055. CpPutByte (
  1056. PCPPORT Port,
  1057. UCHAR Byte
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. Write a byte out to the specified com port.
  1062. Arguments:
  1063. Port - Address of CPPORT object
  1064. Byte - data to emit
  1065. --*/
  1066. {
  1067. UCHAR msr, lsr;
  1068. //
  1069. // If modem control, make sure DSR, CTS and CD are all set before
  1070. // sending any data.
  1071. //
  1072. while ((Port->Flags & PORT_MODEMCONTROL) &&
  1073. (msr = READ_UCHAR(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) {
  1074. //
  1075. // If no CD, and there's a charactor ready, eat it
  1076. //
  1077. lsr = CpReadLsr(Port, 0);
  1078. if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) {
  1079. READ_UCHAR(Port->Address + COM_DAT);
  1080. }
  1081. }
  1082. //
  1083. // Wait for port to not be busy
  1084. //
  1085. while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ;
  1086. //
  1087. // Send the byte
  1088. //
  1089. WRITE_UCHAR(Port->Address + COM_DAT, Byte);
  1090. return;
  1091. }