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.

993 lines
26 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. comport.c
  5. Abstract:
  6. This module contains C code to determine comport and LPT configuration in
  7. syste.
  8. Author:
  9. Shie-Lin Tzong (shielint) Dec-23-1991
  10. Revision History:
  11. --*/
  12. #include "hwdetect.h"
  13. #include "comlpt.h"
  14. #include "bios.h"
  15. #include "string.h"
  16. #define LOWEST_IRQ 3
  17. #define MASTER_IRQ_MASK_BITS 0xf8
  18. #define SLAVE_IRQ_MASK_BITS 0xfe
  19. //
  20. // ComPortAddress[] is a global array to remember which comports have
  21. // been detected and their I/O port addresses.
  22. //
  23. USHORT ComPortAddress[MAX_COM_PORTS] = {0, 0, 0, 0};
  24. VOID
  25. SerialInterruptRequest (
  26. PUCHAR PortAddress
  27. )
  28. /*++
  29. Routine Description:
  30. This routine generates an interrupt on the interrupt line for
  31. com port.
  32. Arguments:
  33. PortAddress - the port address of the desired com port.
  34. Return Value:
  35. None.
  36. --*/
  37. {
  38. USHORT i;
  39. UCHAR Temp;
  40. WRITE_PORT_UCHAR(
  41. PortAddress + MODEM_CONTROL_REGISTER,
  42. 8
  43. );
  44. WRITE_PORT_UCHAR(
  45. PortAddress + INTERRUPT_ENABLE_REGISTER,
  46. 0
  47. );
  48. WRITE_PORT_UCHAR(
  49. PortAddress + INTERRUPT_ENABLE_REGISTER,
  50. 0xf
  51. );
  52. //
  53. // Add some delay
  54. //
  55. for (i = 0; i < 5 ; i++ ) {
  56. Temp = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
  57. Temp = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
  58. }
  59. }
  60. VOID
  61. SerialInterruptDismiss (
  62. PUCHAR PortAddress
  63. )
  64. /*++
  65. Routine Description:
  66. This routine dismisses an interrupt on the interrupt line for
  67. com port.
  68. Arguments:
  69. PortAddress - the port address of the desired com port.
  70. Return Value:
  71. None.
  72. --*/
  73. {
  74. USHORT i;
  75. UCHAR Temp;
  76. Temp = READ_PORT_UCHAR(
  77. PortAddress + INTERRUPT_IDENT_REGISTER
  78. );
  79. WRITE_PORT_UCHAR(
  80. PortAddress + INTERRUPT_ENABLE_REGISTER,
  81. 0
  82. );
  83. //
  84. // Add some delay
  85. //
  86. for (i = 0; i < 5 ; i++ ) {
  87. Temp = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
  88. Temp = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
  89. }
  90. }
  91. BOOLEAN
  92. DoesPortExist(
  93. IN PUCHAR Address
  94. )
  95. /*++
  96. Routine Description:
  97. This routine examines several of what might be the serial device
  98. registers. It ensures that the bits that should be zero are zero.
  99. It will then attempt to set the device to 19200 baud. If the
  100. will then attempt to read that baud. If it is still 19200 then
  101. we can feel pretty safe that this is a serial device.
  102. NOTE: If there is indeed a serial port at the address specified
  103. it will absolutely have interrupts inhibited upon return
  104. from this routine.
  105. Arguments:
  106. Address - address of hw port.
  107. Return Value:
  108. TRUE - Port exists. Party on.
  109. FALSE - Port doesn't exist. Don't use it.
  110. History:
  111. 7/23/97 a-paulbr fixed bug 95050. Init LineControl to 0x00
  112. --*/
  113. {
  114. UCHAR IerContents;
  115. UCHAR BaudRateMsb, BaudRateLsb;
  116. BOOLEAN ReturnValue = FALSE;
  117. UCHAR LineControl = 0x00;
  118. UCHAR LineControl_Save;
  119. UCHAR Temp;
  120. //
  121. // Save the original LCR, so we can restore it later
  122. // We won't use it, because the port could be handing us
  123. // a bad initial value. We will use 0x00 instead.
  124. //
  125. LineControl_Save = READ_PORT_UCHAR(Address+LINE_CONTROL_REGISTER);
  126. //
  127. // Read original baud rate divisor and save it.
  128. //
  129. WRITE_PORT_UCHAR(
  130. Address+LINE_CONTROL_REGISTER,
  131. (UCHAR)(LineControl | SERIAL_LCR_DLAB)
  132. );
  133. BaudRateMsb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_MSB);
  134. BaudRateLsb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_LSB);
  135. //
  136. // Change baud rate to 9600.
  137. //
  138. WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_MSB, BAUD_RATE_9600_MSB);
  139. WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_LSB, BAUD_RATE_9600_LSB);
  140. //
  141. // Read IER and save it away.
  142. //
  143. WRITE_PORT_UCHAR(
  144. Address+LINE_CONTROL_REGISTER,
  145. (UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
  146. );
  147. IerContents = READ_PORT_UCHAR(
  148. Address + INTERRUPT_ENABLE_REGISTER
  149. );
  150. WRITE_PORT_UCHAR(
  151. Address + INTERRUPT_ENABLE_REGISTER,
  152. IER_TEST_VALUE
  153. );
  154. //
  155. // Read baud rate divisor. The values we read should be equal to the
  156. // values we set earlier.
  157. //
  158. WRITE_PORT_UCHAR(
  159. Address+LINE_CONTROL_REGISTER,
  160. (UCHAR)(LineControl | SERIAL_LCR_DLAB)
  161. );
  162. Temp = READ_PORT_UCHAR(Address+DIVISOR_LATCH_MSB);
  163. if (Temp != BAUD_RATE_9600_MSB) {
  164. goto AllDone;
  165. }
  166. Temp = READ_PORT_UCHAR(Address+DIVISOR_LATCH_LSB);
  167. if (Temp != BAUD_RATE_9600_LSB) {
  168. goto AllDone;
  169. }
  170. //
  171. // Read IER and it should be equal to the value we set earlier.
  172. //
  173. WRITE_PORT_UCHAR(
  174. Address+LINE_CONTROL_REGISTER,
  175. (UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
  176. );
  177. Temp = READ_PORT_UCHAR(
  178. Address + INTERRUPT_ENABLE_REGISTER
  179. );
  180. if (Temp != IER_TEST_VALUE) {
  181. goto AllDone;
  182. }
  183. ReturnValue = TRUE;
  184. AllDone:
  185. //
  186. // Restore registers which we destroyed .
  187. //
  188. WRITE_PORT_UCHAR(
  189. Address+LINE_CONTROL_REGISTER,
  190. (UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
  191. );
  192. WRITE_PORT_UCHAR(
  193. Address + INTERRUPT_ENABLE_REGISTER,
  194. IerContents
  195. );
  196. WRITE_PORT_UCHAR(
  197. Address+LINE_CONTROL_REGISTER,
  198. (UCHAR)(LineControl | SERIAL_LCR_DLAB)
  199. );
  200. WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_MSB, BaudRateMsb);
  201. WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_LSB, BaudRateLsb);
  202. WRITE_PORT_UCHAR(
  203. Address+LINE_CONTROL_REGISTER,
  204. LineControl_Save
  205. );
  206. return ReturnValue;
  207. }
  208. BOOLEAN
  209. HwInterruptDetection(
  210. IN PUCHAR BasePort,
  211. IN VOID (*InterruptRequestRoutine)(PUCHAR),
  212. IN VOID (*InterruptDismissRoutine)(PUCHAR),
  213. OUT PUSHORT Vector
  214. )
  215. /*++
  216. Routine Description:
  217. This routine attempts to locate the interrupt vector for which
  218. the device is configured. The allowable vectors are
  219. 3 - 7, and 9 - 15. If no interrupt vector is found, or more than
  220. one is found, the routine returns FALSE. Otherwise, TRUE is returned.
  221. Note that we diddle the i8259 interrupt controllers here.
  222. Arguments:
  223. BasePort - the I/O port base for the device.
  224. InterruptRequestRoutine - A pointer to a routine to generate
  225. desired interrupt.
  226. InterruptDismissRoutine - A pointer to a routine to dismiss the interrupt
  227. generated by InterruptRequestRoutine.
  228. Vector - Pointer to the location to store the mouse interrupt vector.
  229. Return Value:
  230. Returns TRUE if the Inport interrupt vector was located; otherwise,
  231. FALSE is returned.
  232. --*/
  233. {
  234. UCHAR OldMasterMask, OldSlaveMask;
  235. UCHAR MasterMask, SlaveMask;
  236. UCHAR InterruptBits;
  237. UCHAR PossibleInterruptBits;
  238. int i;
  239. int NumberOfIRQs;
  240. BOOLEAN VectorFound = FALSE;
  241. //
  242. // Get the i8259 interrupt masks.
  243. //
  244. OldMasterMask = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
  245. OldSlaveMask = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
  246. //
  247. // Raise IRQL to the highest priority IRQL the inport would use.
  248. //
  249. WRITE_PORT_UCHAR(
  250. (PUCHAR) PIC1_PORT1,
  251. (UCHAR) 0xff ^ ((UCHAR)(1 << LOWEST_IRQ) - 1)
  252. );
  253. WRITE_PORT_UCHAR(
  254. (PUCHAR) PIC2_PORT1,
  255. (UCHAR) 0xfe
  256. );
  257. //
  258. // Get the master i8259 interrupt mask.
  259. //
  260. MasterMask = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
  261. //
  262. // Disable potential device interrupts.
  263. //
  264. WRITE_PORT_UCHAR(
  265. (PUCHAR) PIC1_PORT1,
  266. (UCHAR) (MasterMask | MASTER_IRQ_MASK_BITS)
  267. );
  268. //
  269. // Attempt to locate the interrupt line on the master i8259.
  270. // Why try this 10 times? It's magic...
  271. //
  272. PossibleInterruptBits = MASTER_IRQ_MASK_BITS;
  273. for (i = 0; i < 10; i++) {
  274. //
  275. // Generate a 0 on the master 8259 interrupt line
  276. //
  277. (*InterruptDismissRoutine)(BasePort);
  278. //
  279. // Read the interrupt bits off the master i8259. Only bits
  280. // 3 - 7 are of interest. Eliminate non-functional
  281. // IRQs. Only continue looking at the master i8259 if there
  282. // is at least one functional IRQ.
  283. //
  284. _asm {cli}
  285. WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_IRR);
  286. InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC1_PORT0);
  287. _asm {sti}
  288. InterruptBits &= MASTER_IRQ_MASK_BITS;
  289. InterruptBits ^= MASTER_IRQ_MASK_BITS;
  290. PossibleInterruptBits &= InterruptBits;
  291. if (!PossibleInterruptBits) {
  292. break;
  293. }
  294. //
  295. // Generate an interrupt from the desired device.
  296. //
  297. (*InterruptRequestRoutine)(BasePort);
  298. //
  299. // Read the interrupt bits off the master i8259. Only bits
  300. // 3 - 7 are of interest. Eliminate non-functional
  301. // IRQs. Only continue looking at the master i8259 if there
  302. // is at least one functional IRQ.
  303. //
  304. _asm {cli}
  305. WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_IRR);
  306. InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC1_PORT0);
  307. _asm {sti}
  308. InterruptBits &= MASTER_IRQ_MASK_BITS;
  309. PossibleInterruptBits &= InterruptBits;
  310. if (!PossibleInterruptBits) {
  311. break;
  312. }
  313. }
  314. if (PossibleInterruptBits) {
  315. //
  316. // We found at least one IRQ on the master i8259 that could belong
  317. // to the Inport mouse. Count how many we found. If there is
  318. // more than one, we haven't found the vector. Otherwise, we've
  319. // successfully located the Inport interrupt vector on the master
  320. // i8259 (provided the interrupt vector is 3, 4, 5, or 7).
  321. //
  322. PossibleInterruptBits >>= 3;
  323. NumberOfIRQs = 0;
  324. for (i = 3; i <= 7; i++) {
  325. if (PossibleInterruptBits & 1) {
  326. NumberOfIRQs += 1;
  327. *Vector = (CCHAR) i;
  328. }
  329. PossibleInterruptBits >>= 1;
  330. }
  331. if (NumberOfIRQs == 1) {
  332. VectorFound = TRUE;
  333. }
  334. }
  335. //
  336. // If we didn't locate the interrupt vector on the master i8259, attempt
  337. // to locate it on the slave i8259.
  338. //
  339. if (!VectorFound) {
  340. //
  341. // Get the slave i8259 interrupt mask.
  342. //
  343. SlaveMask = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
  344. //
  345. // Attempt to locate the interupt line on the slave i8259.
  346. // Why try this 20 times? It's magic...
  347. //
  348. PossibleInterruptBits = SLAVE_IRQ_MASK_BITS;
  349. for (i = 0; i < 20; i++) {
  350. //
  351. // Generate a 0 on the Inport IRQ on the slave i8259.
  352. //
  353. (*InterruptDismissRoutine)(BasePort);
  354. //
  355. // Read the interrupt bits off the slave i8259.
  356. // Eliminate non-functional IRQs. Only continue
  357. // looking at the slave i8259 if there is at least one
  358. // functional IRQ.
  359. //
  360. _asm {cli}
  361. WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_IRR);
  362. InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC2_PORT0);
  363. _asm {sti}
  364. InterruptBits &= SLAVE_IRQ_MASK_BITS;
  365. InterruptBits ^= SLAVE_IRQ_MASK_BITS;
  366. PossibleInterruptBits &= InterruptBits;
  367. if (!PossibleInterruptBits) {
  368. break;
  369. }
  370. //
  371. // Generate a 1 on the Inport IRQ on the slave i8259.
  372. //
  373. (*InterruptRequestRoutine)(BasePort);
  374. //
  375. // Read the interrupt bits off the slave i8259.
  376. // Eliminate non-functional IRQs. Only continue
  377. // looking at the slave i8259 if there is at least one
  378. // functional IRQ.
  379. //
  380. _asm {cli}
  381. WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_IRR);
  382. InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC2_PORT0);
  383. _asm {sti}
  384. InterruptBits &= SLAVE_IRQ_MASK_BITS;
  385. PossibleInterruptBits &= InterruptBits;
  386. if (!PossibleInterruptBits) {
  387. break;
  388. }
  389. }
  390. if (PossibleInterruptBits) {
  391. //
  392. // We found at least one IRQ on the slave i8259 that could belong
  393. // to the device. Count how many we found. If there is
  394. // more than one, we haven't found the vector. Otherwise, we've
  395. // successfully located the device interrupt vector on the slave
  396. // i8259.
  397. //
  398. PossibleInterruptBits >>= 1;
  399. NumberOfIRQs = 0;
  400. for (i = 9; i <= 15; i++) {
  401. if (PossibleInterruptBits & 1) {
  402. NumberOfIRQs += 1;
  403. *Vector = (CCHAR) i;
  404. }
  405. PossibleInterruptBits >>= 1;
  406. }
  407. if (NumberOfIRQs == 1) {
  408. VectorFound = TRUE;
  409. }
  410. }
  411. //
  412. // Restore the i8259 slave.
  413. //
  414. WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_ISR);
  415. //
  416. // Restore the i8259 slave interrupt mask.
  417. //
  418. WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT1, SlaveMask);
  419. }
  420. //
  421. // Dismiss interrupt on the device
  422. //
  423. (*InterruptDismissRoutine)(BasePort);
  424. //
  425. // Restore the i8259 master.
  426. //
  427. WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_ISR);
  428. //
  429. // Restore the i8259 master interrupt mask.
  430. //
  431. WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT1, MasterMask);
  432. //
  433. // Restore the previous IRQL.
  434. //
  435. WRITE_PORT_UCHAR(
  436. (PUCHAR) PIC1_PORT1,
  437. OldMasterMask
  438. );
  439. WRITE_PORT_UCHAR(
  440. (PUCHAR) PIC2_PORT1,
  441. OldSlaveMask
  442. );
  443. return(VectorFound);
  444. }
  445. FPFWCONFIGURATION_COMPONENT_DATA
  446. GetComportInformation (
  447. VOID
  448. )
  449. /*++
  450. Routine Description:
  451. This routine will attempt to detect the comports information
  452. for the system. The information includes port address, irq
  453. level.
  454. Note that this routine can only detect up to 4 comports and
  455. it assumes that if MCA, COM3 and COM4 use irq 4. Otherwise,
  456. COM3 uses irq 4 and COM4 uses irq 3. Also, the number of ports
  457. for COMPORT is set to 8 (for example, COM2 uses ports 2F8 - 2FF)
  458. Arguments:
  459. None.
  460. Return Value:
  461. A pointer to a stucture of type FWCONFIGURATION_COMPONENT_DATA
  462. which is the root of comport component list.
  463. If no comport exists, a value of NULL is returned.
  464. --*/
  465. {
  466. FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry, PreviousEntry = NULL;
  467. FPFWCONFIGURATION_COMPONENT_DATA FirstComport = NULL;
  468. FPFWCONFIGURATION_COMPONENT Component;
  469. HWCONTROLLER_DATA ControlData;
  470. UCHAR i, j, z;
  471. SHORT Port;
  472. UCHAR ComportName[] = "COM?";
  473. CM_SERIAL_DEVICE_DATA SerialData;
  474. ULONG BaudClock = 1843200;
  475. USHORT Vector;
  476. BOOLEAN PortExist;
  477. USHORT IoPorts[MAX_COM_PORTS] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
  478. //
  479. // BIOS DATA area 40:0 is the port address of the first valid COM port
  480. //
  481. USHORT far *pPortAddress = (USHORT far *)0x00400000;
  482. //
  483. // Initialize serial device specific data
  484. //
  485. SerialData.Version = 0;
  486. SerialData.Revision = 0;
  487. SerialData.BaudClock = 1843200;
  488. //
  489. // Initialize default COM port address.
  490. // Some BIOS puts incorrect comport address to the 40:0 area.
  491. // To cope with this problem, we test the port address supplied
  492. // by BIOS first. If it fail, we try our default port.
  493. //
  494. for (i = 0; i < MAX_COM_PORTS; i++) {
  495. for (j = 0; j < MAX_COM_PORTS; j++) {
  496. if (IoPorts[i] == *(pPortAddress + j)) {
  497. IoPorts[i] = 0;
  498. break;
  499. }
  500. }
  501. }
  502. for (i = 0; i < MAX_COM_PORTS; i++) {
  503. PortExist = FALSE;
  504. //
  505. // Initialize Controller data
  506. //
  507. ControlData.NumberPortEntries = 0;
  508. ControlData.NumberIrqEntries = 0;
  509. ControlData.NumberMemoryEntries = 0;
  510. ControlData.NumberDmaEntries = 0;
  511. z = 0;
  512. //
  513. // Load the port address from the BIOS data area, if it exists
  514. //
  515. Port = *(pPortAddress + i);
  516. //
  517. // Determine if the port exists
  518. //
  519. if (Port != 0) {
  520. if (DoesPortExist((PUCHAR)Port)) {
  521. PortExist = TRUE;
  522. }
  523. }
  524. if (!PortExist && (Port = IoPorts[i])) {
  525. if (PortExist = DoesPortExist((PUCHAR)Port)) {
  526. IoPorts[i] = 0;
  527. *(pPortAddress+i) = (USHORT)Port;
  528. }
  529. }
  530. if (PortExist) {
  531. //
  532. // Remember the port address in our global variable
  533. // such that other detection code (e.g. Serial Mouse) can
  534. // get the information.
  535. //
  536. ComPortAddress[i] = Port;
  537. CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
  538. sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
  539. if (!FirstComport) {
  540. FirstComport = CurrentEntry;
  541. }
  542. Component = &CurrentEntry->ComponentEntry;
  543. Component->Class = ControllerClass;
  544. Component->Type = SerialController;
  545. Component->Flags.ConsoleOut = 1;
  546. Component->Flags.ConsoleIn = 1;
  547. Component->Flags.Output = 1;
  548. Component->Flags.Input = 1;
  549. Component->Version = 0;
  550. Component->Key = i;
  551. Component->AffinityMask = 0xffffffff;
  552. //
  553. // Set up type string.
  554. //
  555. ComportName[3] = i + (UCHAR)'1';
  556. //
  557. // Set up Port information
  558. //
  559. ControlData.NumberPortEntries = 1;
  560. ControlData.DescriptorList[z].Type = RESOURCE_PORT;
  561. ControlData.DescriptorList[z].ShareDisposition =
  562. CmResourceShareDeviceExclusive;
  563. ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
  564. ControlData.DescriptorList[z].u.Port.Start.LowPart = (ULONG)Port;
  565. ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
  566. ControlData.DescriptorList[z].u.Port.Length = 7;
  567. z++;
  568. //
  569. // Set up Irq information
  570. //
  571. ControlData.NumberIrqEntries = 1;
  572. ControlData.DescriptorList[z].Type = RESOURCE_INTERRUPT;
  573. ControlData.DescriptorList[z].ShareDisposition =
  574. CmResourceShareUndetermined;
  575. if (HwBusType == MACHINE_TYPE_MCA) {
  576. ControlData.DescriptorList[z].Flags = LEVEL_SENSITIVE;
  577. if (i == 0) { // COM1 - irql4; COM2 - COM3 - irq3
  578. ControlData.DescriptorList[z].u.Interrupt.Level = 4;
  579. ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
  580. } else {
  581. ControlData.DescriptorList[z].u.Interrupt.Level = 3;
  582. ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
  583. }
  584. } else {
  585. //
  586. // For EISA the LevelTriggered is temporarily set to FALSE.
  587. // COM1 and COM3 use irq 4; COM2 and COM4 use irq3
  588. //
  589. ControlData.DescriptorList[z].Flags = EDGE_TRIGGERED;
  590. if (Port == 0x3f8 || Port == 0x3e8) {
  591. ControlData.DescriptorList[z].u.Interrupt.Level = 4;
  592. ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
  593. } else if (Port == 0x2f8 || Port == 0x2e8) {
  594. ControlData.DescriptorList[z].u.Interrupt.Level = 3;
  595. ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
  596. } else if (i == 0 || i == 2) {
  597. ControlData.DescriptorList[z].u.Interrupt.Level = 4;
  598. ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
  599. } else {
  600. ControlData.DescriptorList[z].u.Interrupt.Level = 3;
  601. ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
  602. }
  603. }
  604. ControlData.DescriptorList[z].u.Interrupt.Affinity = ALL_PROCESSORS;
  605. //
  606. // Try to determine the interrupt vector. If we success, the
  607. // new vector will be used to replace the default value.
  608. //
  609. if (HwInterruptDetection((PUCHAR)Port,
  610. SerialInterruptRequest,
  611. SerialInterruptDismiss,
  612. &Vector)) {
  613. ControlData.DescriptorList[z].u.Interrupt.Level =
  614. (ULONG)Vector;
  615. ControlData.DescriptorList[z].u.Interrupt.Vector =
  616. (ULONG)Vector;
  617. }
  618. //
  619. // Since the com port interrupt detection destryed some
  620. // of the com port registers, here we do the clean up.
  621. //
  622. WRITE_PORT_UCHAR ((PUCHAR)(Port + INTERRUPT_ENABLE_REGISTER), 0);
  623. WRITE_PORT_UCHAR ((PUCHAR)(Port + MODEM_CONTROL_REGISTER), 0);
  624. CurrentEntry->ConfigurationData =
  625. HwSetUpResourceDescriptor(Component,
  626. ComportName,
  627. &ControlData,
  628. sizeof(SerialData),
  629. (PUCHAR)&SerialData
  630. );
  631. if (PreviousEntry) {
  632. PreviousEntry->Sibling = CurrentEntry;
  633. }
  634. PreviousEntry = CurrentEntry;
  635. }
  636. }
  637. return(FirstComport);
  638. }
  639. FPFWCONFIGURATION_COMPONENT_DATA
  640. GetLptInformation (
  641. VOID
  642. )
  643. /*++
  644. Routine Description:
  645. This routine will attempt to detect the parallel printer port
  646. information for the system. The information includes port address,
  647. irq level.
  648. Note if this code is run after user established NETWORK LPT
  649. connection. The Network LPT will be counted as regular parallel
  650. port.
  651. Arguments:
  652. None.
  653. Return Value:
  654. A pointer to a stucture of type PONENT_DATA
  655. which is the root of Parallel component list.
  656. If no comport exists, a value of NULL is returned.
  657. --*/
  658. {
  659. FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry, PreviousEntry = NULL;
  660. FPFWCONFIGURATION_COMPONENT_DATA FirstLptPort = NULL;
  661. FPFWCONFIGURATION_COMPONENT Component;
  662. HWCONTROLLER_DATA ControlData;
  663. UCHAR LptPortName[] = "PARALLEL?";
  664. USHORT i, z;
  665. USHORT LptStatus;
  666. ULONG Port;
  667. //
  668. // BIOS DATA area 40:8 is the port address of the first valid COM port
  669. //
  670. USHORT far *pPortAddress = (USHORT far *)0x00400008;
  671. for (i = 0; i < MAX_LPT_PORTS; i++) {
  672. Port = (ULONG)*(pPortAddress + i);
  673. if (Port == 0) {
  674. continue;
  675. } else {
  676. //
  677. // If we think we have a lpt, we will initialize it to
  678. // a known state. In order to make printing work under
  679. // nt, the arbitration level must be disabled. The BIOS
  680. // init function seems to do the trick.
  681. //
  682. _asm {
  683. mov ah, 1
  684. mov dx, i
  685. int 17h
  686. }
  687. }
  688. //
  689. // Initialize Controller data
  690. //
  691. ControlData.NumberPortEntries = 0;
  692. ControlData.NumberIrqEntries = 0;
  693. ControlData.NumberMemoryEntries = 0;
  694. ControlData.NumberDmaEntries = 0;
  695. z = 0;
  696. //
  697. // Determine if the port exists
  698. //
  699. LptStatus = _bios_printer(_PRINTER_STATUS, i , 0);
  700. if (!(LptStatus & 6)){
  701. CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
  702. sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
  703. if (!FirstLptPort) {
  704. FirstLptPort = CurrentEntry;
  705. }
  706. Component = &CurrentEntry->ComponentEntry;
  707. Component->Class = ControllerClass;
  708. Component->Type = ParallelController;
  709. Component->Flags.Output = 1;
  710. Component->Version = 0;
  711. Component->Key = i;
  712. Component->AffinityMask = 0xffffffff;
  713. //
  714. // Set up type string.
  715. //
  716. LptPortName[8] = (UCHAR)i + (UCHAR)'1';
  717. //
  718. // Set up Port information
  719. //
  720. Port = (ULONG)*(pPortAddress + i);
  721. ControlData.NumberPortEntries = 1;
  722. ControlData.DescriptorList[z].Type = RESOURCE_PORT;
  723. ControlData.DescriptorList[z].ShareDisposition =
  724. CmResourceShareDeviceExclusive;
  725. ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
  726. ControlData.DescriptorList[z].u.Port.Start.LowPart = Port;
  727. ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
  728. ControlData.DescriptorList[z].u.Port.Length = 3;
  729. z++;
  730. //
  731. // Set up Irq information
  732. //
  733. ControlData.NumberIrqEntries = 1;
  734. ControlData.DescriptorList[z].Type = RESOURCE_INTERRUPT;
  735. ControlData.DescriptorList[z].ShareDisposition =
  736. CmResourceShareUndetermined;
  737. ControlData.DescriptorList[z].u.Interrupt.Affinity = ALL_PROCESSORS;
  738. if (i ==0) {
  739. ControlData.DescriptorList[z].u.Interrupt.Level = 7;
  740. ControlData.DescriptorList[z].u.Interrupt.Vector = 7;
  741. } else {
  742. ControlData.DescriptorList[z].u.Interrupt.Level = 5;
  743. ControlData.DescriptorList[z].u.Interrupt.Vector = 5;
  744. }
  745. if (HwBusType == MACHINE_TYPE_MCA) {
  746. ControlData.DescriptorList[z].Flags = LEVEL_SENSITIVE;
  747. } else {
  748. //
  749. // For EISA the LevelTriggered is temporarily set to FALSE.
  750. //
  751. ControlData.DescriptorList[z].Flags = EDGE_TRIGGERED;
  752. }
  753. CurrentEntry->ConfigurationData =
  754. HwSetUpResourceDescriptor(Component,
  755. LptPortName,
  756. &ControlData,
  757. 0,
  758. NULL
  759. );
  760. if (PreviousEntry) {
  761. PreviousEntry->Sibling = CurrentEntry;
  762. }
  763. PreviousEntry = CurrentEntry;
  764. }
  765. }
  766. return(FirstLptPort);
  767. }