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.

1852 lines
54 KiB

  1. /*++
  2. Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
  3. Module Name:
  4. isr.c
  5. Abstract:
  6. This module contains the interrupt service routine for the
  7. serial driver.
  8. Author:
  9. Anthony V. Ercolano 26-Sep-1991
  10. Environment:
  11. Kernel mode
  12. --*/
  13. #include "precomp.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGESER,SerialPutChar)
  16. #pragma alloc_text(PAGESER,SerialProcessLSR)
  17. #endif
  18. BOOLEAN
  19. SerialCIsrSw(IN PKINTERRUPT InterruptObject, IN PVOID Context)
  20. /*++
  21. Routine Description:
  22. All Serial interrupts get vectored through here and switched.
  23. This is necessary so that previously single port serial boards
  24. can be switched to multiport without having to disconnect
  25. the interrupt object etc.
  26. Arguments:
  27. InterruptObject - Points to the interrupt object declared for this
  28. device. We merely pass this parameter along.
  29. Context - Actually a PSERIAL_CISR_SW; a switch structure for
  30. serial ISR's that contains the real function and context to use.
  31. Return Value:
  32. This function will return TRUE if a serial port using this
  33. interrupt was the source of this interrupt, FALSE otherwise.
  34. --*/
  35. {
  36. PSERIAL_CISR_SW csw = (PSERIAL_CISR_SW)Context;
  37. return (*(csw->IsrFunc))(InterruptObject, csw->Context);
  38. }
  39. BOOLEAN
  40. SerialSharerIsr(
  41. IN PKINTERRUPT InterruptObject,
  42. IN PVOID Context
  43. )
  44. /*++
  45. Routine Description:
  46. This is the isr that the system will call if there are any
  47. serial devices sharing the same interrupt and they aren't
  48. all confined to one multiport card. This routine traverses
  49. a linked list structure that contains a pointer to a more
  50. refined isr and context that will indicate whether one of
  51. the ports on this interrupt actually was interrupting.
  52. Arguments:
  53. InterruptObject - Points to the interrupt object declared for this
  54. device. We *do not* use this parameter.
  55. Context - Pointer to a linked list of contextes and isrs.
  56. device.
  57. Return Value:
  58. This function will return TRUE if a serial port using this
  59. interrupt was the source of this interrupt, FALSE otherwise.
  60. --*/
  61. {
  62. BOOLEAN servicedAnInterrupt = FALSE;
  63. BOOLEAN thisPassServiced;
  64. PLIST_ENTRY interruptEntry = ((PLIST_ENTRY)Context)->Flink;
  65. PLIST_ENTRY firstInterruptEntry = Context;
  66. if (IsListEmpty(firstInterruptEntry)) {
  67. return FALSE;
  68. }
  69. do {
  70. thisPassServiced = FALSE;
  71. do {
  72. PSERIAL_DEVICE_EXTENSION extension = CONTAINING_RECORD(
  73. interruptEntry,
  74. SERIAL_DEVICE_EXTENSION,
  75. TopLevelSharers
  76. );
  77. thisPassServiced |= extension->TopLevelOurIsr(
  78. InterruptObject,
  79. extension->TopLevelOurIsrContext
  80. );
  81. servicedAnInterrupt |= thisPassServiced;
  82. interruptEntry = interruptEntry->Flink;
  83. } while (interruptEntry != firstInterruptEntry);
  84. } while (thisPassServiced);
  85. return servicedAnInterrupt;
  86. }
  87. BOOLEAN
  88. SerialIndexedMultiportIsr(
  89. IN PKINTERRUPT InterruptObject,
  90. IN PVOID Context
  91. )
  92. /*++
  93. Routine Description:
  94. This routine is used to figure out if a port on a multiport
  95. card is the source of an interrupt. If so, this routine
  96. uses a dispatch structure to actually call the normal isr
  97. to process the interrupt.
  98. NOTE: This routine is peculiar to Digiboard interrupt status registers.
  99. Arguments:
  100. InterruptObject - Points to the interrupt object declared for this
  101. device. We *do not* use this parameter.
  102. Context - Points to a dispatch structure that contains the
  103. device extension of each port on this multiport card.
  104. Return Value:
  105. This function will return TRUE if a serial port using this
  106. interrupt was the source of this interrupt, FALSE otherwise.
  107. --*/
  108. {
  109. BOOLEAN servicedAnInterrupt = FALSE;
  110. BOOLEAN thisStatusReadServiced;
  111. PSERIAL_MULTIPORT_DISPATCH dispatch = Context;
  112. ULONG whichPort;
  113. UCHAR statusRegister;
  114. do {
  115. thisStatusReadServiced = FALSE;
  116. statusRegister = READ_PORT_UCHAR(
  117. dispatch->InterruptStatus
  118. );
  119. whichPort = statusRegister & 0x07;
  120. //
  121. // We test against 0xff, which signals that no port
  122. // is interruping. The reason 0xff (rather than 0)
  123. // is that that would indicate the 0th (first) port
  124. // or the 0th daisy chained card.
  125. //
  126. if (statusRegister != 0xff) {
  127. if (dispatch->Extensions[whichPort]) {
  128. thisStatusReadServiced = SerialISR(
  129. InterruptObject,
  130. dispatch->Extensions[whichPort]
  131. );
  132. servicedAnInterrupt |= thisStatusReadServiced;
  133. }
  134. }
  135. } while (thisStatusReadServiced);
  136. return servicedAnInterrupt;
  137. }
  138. BOOLEAN
  139. SerialBitMappedMultiportIsr(
  140. IN PKINTERRUPT InterruptObject,
  141. IN PVOID Context
  142. )
  143. /*++
  144. Routine Description:
  145. This routine is used to figure out if a port on a multiport
  146. card is the source of an interrupt. If so, this routine
  147. uses a dispatch structure to actually call the normal isr
  148. to process the interrupt.
  149. NOTE: This routine is peculiar to status registers that use
  150. a bitmask to denote the interrupting port.
  151. Arguments:
  152. InterruptObject - Points to the interrupt object declared for this
  153. device. We *do not* use this parameter.
  154. Context - Points to a dispatch structure that contains the
  155. device extension of each port on this multiport card.
  156. Return Value:
  157. This function will return TRUE if a serial port using this
  158. interrupt was the source of this interrupt, FALSE otherwise.
  159. --*/
  160. {
  161. BOOLEAN servicedAnInterrupt = FALSE;
  162. PSERIAL_MULTIPORT_DISPATCH dispatch = Context;
  163. ULONG whichPort;
  164. UCHAR statusRegister;
  165. do {
  166. statusRegister = READ_PORT_UCHAR(
  167. dispatch->InterruptStatus
  168. );
  169. if (dispatch->MaskInverted) {
  170. statusRegister = ~statusRegister;
  171. }
  172. statusRegister &= dispatch->UsablePortMask;
  173. if (statusRegister) {
  174. if (statusRegister & 0x0f) {
  175. if (statusRegister & 0x03) {
  176. if (statusRegister & 1) {
  177. whichPort = 0;
  178. } else {
  179. whichPort = 1;
  180. }
  181. } else {
  182. if (statusRegister & 0x04) {
  183. whichPort = 2;
  184. } else {
  185. whichPort = 3;
  186. }
  187. }
  188. } else {
  189. if (statusRegister & 0x30) {
  190. if (statusRegister & 0x10) {
  191. whichPort = 4;
  192. } else {
  193. whichPort = 5;
  194. }
  195. } else {
  196. if (statusRegister & 0x40) {
  197. whichPort = 6;
  198. } else {
  199. whichPort = 7;
  200. }
  201. }
  202. }
  203. if (dispatch->Extensions[whichPort]) {
  204. if (SerialISR(
  205. InterruptObject,
  206. dispatch->Extensions[whichPort]
  207. )) {
  208. servicedAnInterrupt = TRUE;
  209. }
  210. }
  211. }
  212. } while (statusRegister);
  213. return servicedAnInterrupt;
  214. }
  215. BOOLEAN
  216. SerialISR(
  217. IN PKINTERRUPT InterruptObject,
  218. IN PVOID Context
  219. )
  220. /*++
  221. Routine Description:
  222. This is the interrupt service routine for the serial port driver.
  223. It will determine whether the serial port is the source of this
  224. interrupt. If it is, then this routine will do the minimum of
  225. processing to quiet the interrupt. It will store any information
  226. necessary for later processing.
  227. Arguments:
  228. InterruptObject - Points to the interrupt object declared for this
  229. device. We *do not* use this parameter.
  230. Context - This is really a pointer to the device extension for this
  231. device.
  232. Return Value:
  233. This function will return TRUE if the serial port is the source
  234. of this interrupt, FALSE otherwise.
  235. --*/
  236. {
  237. //
  238. // Holds the information specific to handling this device.
  239. //
  240. PSERIAL_DEVICE_EXTENSION Extension = Context;
  241. //
  242. // Holds the contents of the interrupt identification record.
  243. // A low bit of zero in this register indicates that there is
  244. // an interrupt pending on this device.
  245. //
  246. UCHAR InterruptIdReg;
  247. //
  248. // Will hold whether we've serviced any interrupt causes in this
  249. // routine.
  250. //
  251. BOOLEAN ServicedAnInterrupt;
  252. UCHAR tempLSR;
  253. UNREFERENCED_PARAMETER(InterruptObject);
  254. //
  255. // Make sure we have an interrupt pending. If we do then
  256. // we need to make sure that the device is open. If the
  257. // device isn't open or powered down then quiet the device. Note that
  258. // if the device isn't opened when we enter this routine
  259. // it can't open while we're in it.
  260. //
  261. InterruptIdReg = READ_INTERRUPT_ID_REG(Extension->Controller);
  262. //
  263. // Apply lock so if close happens concurrently we don't miss the DPC
  264. // queueing
  265. //
  266. InterlockedIncrement(&Extension->DpcCount);
  267. if ((InterruptIdReg & SERIAL_IIR_NO_INTERRUPT_PENDING)) {
  268. ServicedAnInterrupt = FALSE;
  269. } else if (!Extension->DeviceIsOpened
  270. || (Extension->PowerState != PowerDeviceD0)) {
  271. //
  272. // We got an interrupt with the device being closed or when the
  273. // device is supposed to be powered down. This
  274. // is not unlikely with a serial device. We just quietly
  275. // keep servicing the causes until it calms down.
  276. //
  277. ServicedAnInterrupt = TRUE;
  278. do {
  279. InterruptIdReg &= (~SERIAL_IIR_FIFOS_ENABLED);
  280. switch (InterruptIdReg) {
  281. case SERIAL_IIR_RLS: {
  282. READ_LINE_STATUS(Extension->Controller);
  283. break;
  284. }
  285. case SERIAL_IIR_RDA:
  286. case SERIAL_IIR_CTI: {
  287. READ_RECEIVE_BUFFER(Extension->Controller);
  288. break;
  289. }
  290. case SERIAL_IIR_THR: {
  291. //
  292. // Alread clear from reading the iir.
  293. //
  294. // We want to keep close track of whether
  295. // the holding register is empty.
  296. //
  297. Extension->HoldingEmpty = TRUE;
  298. break;
  299. }
  300. case SERIAL_IIR_MS: {
  301. READ_MODEM_STATUS(Extension->Controller);
  302. break;
  303. }
  304. default: {
  305. ASSERT(FALSE);
  306. break;
  307. }
  308. }
  309. } while (!((InterruptIdReg =
  310. READ_INTERRUPT_ID_REG(Extension->Controller))
  311. & SERIAL_IIR_NO_INTERRUPT_PENDING));
  312. } else {
  313. ServicedAnInterrupt = TRUE;
  314. do {
  315. //
  316. // We only care about bits that can denote an interrupt.
  317. //
  318. InterruptIdReg &= SERIAL_IIR_RLS | SERIAL_IIR_RDA |
  319. SERIAL_IIR_CTI | SERIAL_IIR_THR |
  320. SERIAL_IIR_MS;
  321. //
  322. // We have an interrupt. We look for interrupt causes
  323. // in priority order. The presence of a higher interrupt
  324. // will mask out causes of a lower priority. When we service
  325. // and quiet a higher priority interrupt we then need to check
  326. // the interrupt causes to see if a new interrupt cause is
  327. // present.
  328. //
  329. switch (InterruptIdReg) {
  330. case SERIAL_IIR_RLS: {
  331. SerialProcessLSR(Extension);
  332. break;
  333. }
  334. case SERIAL_IIR_RDA:
  335. case SERIAL_IIR_CTI:
  336. {
  337. //
  338. // Reading the receive buffer will quiet this interrupt.
  339. //
  340. // It may also reveal a new interrupt cause.
  341. //
  342. UCHAR ReceivedChar;
  343. do {
  344. ReceivedChar =
  345. READ_RECEIVE_BUFFER(Extension->Controller);
  346. Extension->PerfStats.ReceivedCount++;
  347. Extension->WmiPerfData.ReceivedCount++;
  348. ReceivedChar &= Extension->ValidDataMask;
  349. if (!ReceivedChar &&
  350. (Extension->HandFlow.FlowReplace &
  351. SERIAL_NULL_STRIPPING)) {
  352. //
  353. // If what we got is a null character
  354. // and we're doing null stripping, then
  355. // we simply act as if we didn't see it.
  356. //
  357. goto ReceiveDoLineStatus;
  358. }
  359. if ((Extension->HandFlow.FlowReplace &
  360. SERIAL_AUTO_TRANSMIT) &&
  361. ((ReceivedChar ==
  362. Extension->SpecialChars.XonChar) ||
  363. (ReceivedChar ==
  364. Extension->SpecialChars.XoffChar))) {
  365. //
  366. // No matter what happens this character
  367. // will never get seen by the app.
  368. //
  369. if (ReceivedChar ==
  370. Extension->SpecialChars.XoffChar) {
  371. Extension->TXHolding |= SERIAL_TX_XOFF;
  372. if ((Extension->HandFlow.FlowReplace &
  373. SERIAL_RTS_MASK) ==
  374. SERIAL_TRANSMIT_TOGGLE) {
  375. SerialInsertQueueDpc(
  376. &Extension->StartTimerLowerRTSDpc,
  377. NULL,
  378. NULL,
  379. Extension
  380. )?Extension->CountOfTryingToLowerRTS++:0;
  381. }
  382. } else {
  383. if (Extension->TXHolding & SERIAL_TX_XOFF) {
  384. //
  385. // We got the xon char **AND*** we
  386. // were being held up on transmission
  387. // by xoff. Clear that we are holding
  388. // due to xoff. Transmission will
  389. // automatically restart because of
  390. // the code outside the main loop that
  391. // catches problems chips like the
  392. // SMC and the Winbond.
  393. //
  394. Extension->TXHolding &= ~SERIAL_TX_XOFF;
  395. }
  396. }
  397. goto ReceiveDoLineStatus;
  398. }
  399. //
  400. // Check to see if we should note
  401. // the receive character or special
  402. // character event.
  403. //
  404. if (Extension->IsrWaitMask) {
  405. if (Extension->IsrWaitMask &
  406. SERIAL_EV_RXCHAR) {
  407. Extension->HistoryMask |= SERIAL_EV_RXCHAR;
  408. }
  409. if ((Extension->IsrWaitMask &
  410. SERIAL_EV_RXFLAG) &&
  411. (Extension->SpecialChars.EventChar ==
  412. ReceivedChar)) {
  413. Extension->HistoryMask |= SERIAL_EV_RXFLAG;
  414. }
  415. if (Extension->IrpMaskLocation &&
  416. Extension->HistoryMask) {
  417. *Extension->IrpMaskLocation =
  418. Extension->HistoryMask;
  419. Extension->IrpMaskLocation = NULL;
  420. Extension->HistoryMask = 0;
  421. Extension->CurrentWaitIrp->
  422. IoStatus.Information = sizeof(ULONG);
  423. SerialInsertQueueDpc(
  424. &Extension->CommWaitDpc,
  425. NULL,
  426. NULL,
  427. Extension
  428. );
  429. }
  430. }
  431. SerialPutChar(
  432. Extension,
  433. ReceivedChar
  434. );
  435. //
  436. // If we're doing line status and modem
  437. // status insertion then we need to insert
  438. // a zero following the character we just
  439. // placed into the buffer to mark that this
  440. // was reception of what we are using to
  441. // escape.
  442. //
  443. if (Extension->EscapeChar &&
  444. (Extension->EscapeChar ==
  445. ReceivedChar)) {
  446. SerialPutChar(
  447. Extension,
  448. SERIAL_LSRMST_ESCAPE
  449. );
  450. }
  451. ReceiveDoLineStatus: ;
  452. if (!((tempLSR = SerialProcessLSR(Extension)) &
  453. SERIAL_LSR_DR)) {
  454. //
  455. // No more characters, get out of the
  456. // loop.
  457. //
  458. break;
  459. }
  460. if ((tempLSR & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT |
  461. SERIAL_LSR_DR)) &&
  462. Extension->EscapeChar) {
  463. //
  464. // An error was indicated and inserted into the
  465. // stream, get out of the loop.
  466. //
  467. break;
  468. }
  469. } while (TRUE);
  470. break;
  471. }
  472. case SERIAL_IIR_THR: {
  473. doTrasmitStuff:;
  474. Extension->HoldingEmpty = TRUE;
  475. if (Extension->WriteLength ||
  476. Extension->TransmitImmediate ||
  477. Extension->SendXoffChar ||
  478. Extension->SendXonChar) {
  479. //
  480. // Even though all of the characters being
  481. // sent haven't all been sent, this variable
  482. // will be checked when the transmit queue is
  483. // empty. If it is still true and there is a
  484. // wait on the transmit queue being empty then
  485. // we know we finished transmitting all characters
  486. // following the initiation of the wait since
  487. // the code that initiates the wait will set
  488. // this variable to false.
  489. //
  490. // One reason it could be false is that
  491. // the writes were cancelled before they
  492. // actually started, or that the writes
  493. // failed due to timeouts. This variable
  494. // basically says a character was written
  495. // by the isr at some point following the
  496. // initiation of the wait.
  497. //
  498. Extension->EmptiedTransmit = TRUE;
  499. //
  500. // If we have output flow control based on
  501. // the modem status lines, then we have to do
  502. // all the modem work before we output each
  503. // character. (Otherwise we might miss a
  504. // status line change.)
  505. //
  506. if (Extension->HandFlow.ControlHandShake &
  507. SERIAL_OUT_HANDSHAKEMASK) {
  508. SerialHandleModemUpdate(
  509. Extension,
  510. TRUE
  511. );
  512. }
  513. //
  514. // We can only send the xon character if
  515. // the only reason we are holding is because
  516. // of the xoff. (Hardware flow control or
  517. // sending break preclude putting a new character
  518. // on the wire.)
  519. //
  520. if (Extension->SendXonChar &&
  521. !(Extension->TXHolding & ~SERIAL_TX_XOFF)) {
  522. if ((Extension->HandFlow.FlowReplace &
  523. SERIAL_RTS_MASK) ==
  524. SERIAL_TRANSMIT_TOGGLE) {
  525. //
  526. // We have to raise if we're sending
  527. // this character.
  528. //
  529. SerialSetRTS(Extension);
  530. Extension->PerfStats.TransmittedCount++;
  531. Extension->WmiPerfData.TransmittedCount++;
  532. WRITE_TRANSMIT_HOLDING(
  533. Extension->Controller,
  534. Extension->SpecialChars.XonChar
  535. );
  536. SerialInsertQueueDpc(
  537. &Extension->StartTimerLowerRTSDpc,
  538. NULL,
  539. NULL,
  540. Extension
  541. )?Extension->CountOfTryingToLowerRTS++:0;
  542. } else {
  543. Extension->PerfStats.TransmittedCount++;
  544. Extension->WmiPerfData.TransmittedCount++;
  545. WRITE_TRANSMIT_HOLDING(
  546. Extension->Controller,
  547. Extension->SpecialChars.XonChar
  548. );
  549. }
  550. Extension->SendXonChar = FALSE;
  551. Extension->HoldingEmpty = FALSE;
  552. //
  553. // If we send an xon, by definition we
  554. // can't be holding by Xoff.
  555. //
  556. Extension->TXHolding &= ~SERIAL_TX_XOFF;
  557. //
  558. // If we are sending an xon char then
  559. // by definition we can't be "holding"
  560. // up reception by Xoff.
  561. //
  562. Extension->RXHolding &= ~SERIAL_RX_XOFF;
  563. } else if (Extension->SendXoffChar &&
  564. !Extension->TXHolding) {
  565. if ((Extension->HandFlow.FlowReplace &
  566. SERIAL_RTS_MASK) ==
  567. SERIAL_TRANSMIT_TOGGLE) {
  568. //
  569. // We have to raise if we're sending
  570. // this character.
  571. //
  572. SerialSetRTS(Extension);
  573. Extension->PerfStats.TransmittedCount++;
  574. Extension->WmiPerfData.TransmittedCount++;
  575. WRITE_TRANSMIT_HOLDING(
  576. Extension->Controller,
  577. Extension->SpecialChars.XoffChar
  578. );
  579. SerialInsertQueueDpc(
  580. &Extension->StartTimerLowerRTSDpc,
  581. NULL,
  582. NULL,
  583. Extension
  584. )?Extension->CountOfTryingToLowerRTS++:0;
  585. } else {
  586. Extension->PerfStats.TransmittedCount++;
  587. Extension->WmiPerfData.TransmittedCount++;
  588. WRITE_TRANSMIT_HOLDING(
  589. Extension->Controller,
  590. Extension->SpecialChars.XoffChar
  591. );
  592. }
  593. //
  594. // We can't be sending an Xoff character
  595. // if the transmission is already held
  596. // up because of Xoff. Therefore, if we
  597. // are holding then we can't send the char.
  598. //
  599. //
  600. // If the application has set xoff continue
  601. // mode then we don't actually stop sending
  602. // characters if we send an xoff to the other
  603. // side.
  604. //
  605. if (!(Extension->HandFlow.FlowReplace &
  606. SERIAL_XOFF_CONTINUE)) {
  607. Extension->TXHolding |= SERIAL_TX_XOFF;
  608. if ((Extension->HandFlow.FlowReplace &
  609. SERIAL_RTS_MASK) ==
  610. SERIAL_TRANSMIT_TOGGLE) {
  611. SerialInsertQueueDpc(
  612. &Extension->StartTimerLowerRTSDpc,
  613. NULL,
  614. NULL,
  615. Extension
  616. )?Extension->CountOfTryingToLowerRTS++:0;
  617. }
  618. }
  619. Extension->SendXoffChar = FALSE;
  620. Extension->HoldingEmpty = FALSE;
  621. //
  622. // Even if transmission is being held
  623. // up, we should still transmit an immediate
  624. // character if all that is holding us
  625. // up is xon/xoff (OS/2 rules).
  626. //
  627. } else if (Extension->TransmitImmediate &&
  628. (!Extension->TXHolding ||
  629. (Extension->TXHolding == SERIAL_TX_XOFF)
  630. )) {
  631. Extension->TransmitImmediate = FALSE;
  632. if ((Extension->HandFlow.FlowReplace &
  633. SERIAL_RTS_MASK) ==
  634. SERIAL_TRANSMIT_TOGGLE) {
  635. //
  636. // We have to raise if we're sending
  637. // this character.
  638. //
  639. SerialSetRTS(Extension);
  640. Extension->PerfStats.TransmittedCount++;
  641. Extension->WmiPerfData.TransmittedCount++;
  642. WRITE_TRANSMIT_HOLDING(
  643. Extension->Controller,
  644. Extension->ImmediateChar
  645. );
  646. SerialInsertQueueDpc(
  647. &Extension->StartTimerLowerRTSDpc,
  648. NULL,
  649. NULL,
  650. Extension
  651. )?Extension->CountOfTryingToLowerRTS++:0;
  652. } else {
  653. Extension->PerfStats.TransmittedCount++;
  654. Extension->WmiPerfData.TransmittedCount++;
  655. WRITE_TRANSMIT_HOLDING(
  656. Extension->Controller,
  657. Extension->ImmediateChar
  658. );
  659. }
  660. Extension->HoldingEmpty = FALSE;
  661. SerialInsertQueueDpc(
  662. &Extension->CompleteImmediateDpc,
  663. NULL,
  664. NULL,
  665. Extension
  666. );
  667. } else if (!Extension->TXHolding) {
  668. ULONG amountToWrite;
  669. if (Extension->FifoPresent) {
  670. amountToWrite = (Extension->TxFifoAmount <
  671. Extension->WriteLength)?
  672. Extension->TxFifoAmount:
  673. Extension->WriteLength;
  674. } else {
  675. amountToWrite = 1;
  676. }
  677. if ((Extension->HandFlow.FlowReplace &
  678. SERIAL_RTS_MASK) ==
  679. SERIAL_TRANSMIT_TOGGLE) {
  680. //
  681. // We have to raise if we're sending
  682. // this character.
  683. //
  684. SerialSetRTS(Extension);
  685. if (amountToWrite == 1) {
  686. Extension->PerfStats.TransmittedCount++;
  687. Extension->WmiPerfData.TransmittedCount++;
  688. WRITE_TRANSMIT_HOLDING(
  689. Extension->Controller,
  690. *(Extension->WriteCurrentChar)
  691. );
  692. } else {
  693. Extension->PerfStats.TransmittedCount +=
  694. amountToWrite;
  695. Extension->WmiPerfData.TransmittedCount +=
  696. amountToWrite;
  697. WRITE_TRANSMIT_FIFO_HOLDING(
  698. Extension->Controller,
  699. Extension->WriteCurrentChar,
  700. amountToWrite
  701. );
  702. }
  703. SerialInsertQueueDpc(
  704. &Extension->StartTimerLowerRTSDpc,
  705. NULL,
  706. NULL,
  707. Extension
  708. )?Extension->CountOfTryingToLowerRTS++:0;
  709. } else {
  710. if (amountToWrite == 1) {
  711. Extension->PerfStats.TransmittedCount++;
  712. Extension->WmiPerfData.TransmittedCount++;
  713. WRITE_TRANSMIT_HOLDING(
  714. Extension->Controller,
  715. *(Extension->WriteCurrentChar)
  716. );
  717. } else {
  718. Extension->PerfStats.TransmittedCount +=
  719. amountToWrite;
  720. Extension->WmiPerfData.TransmittedCount +=
  721. amountToWrite;
  722. WRITE_TRANSMIT_FIFO_HOLDING(
  723. Extension->Controller,
  724. Extension->WriteCurrentChar,
  725. amountToWrite
  726. );
  727. }
  728. }
  729. Extension->HoldingEmpty = FALSE;
  730. Extension->WriteCurrentChar += amountToWrite;
  731. Extension->WriteLength -= amountToWrite;
  732. if (!Extension->WriteLength) {
  733. PIO_STACK_LOCATION IrpSp;
  734. //
  735. // No More characters left. This
  736. // write is complete. Take care
  737. // when updating the information field,
  738. // we could have an xoff counter masquerading
  739. // as a write irp.
  740. //
  741. IrpSp = IoGetCurrentIrpStackLocation(
  742. Extension->CurrentWriteIrp
  743. );
  744. Extension->CurrentWriteIrp->
  745. IoStatus.Information =
  746. (IrpSp->MajorFunction == IRP_MJ_WRITE)?
  747. (IrpSp->Parameters.Write.Length):
  748. (1);
  749. SerialInsertQueueDpc(
  750. &Extension->CompleteWriteDpc,
  751. NULL,
  752. NULL,
  753. Extension
  754. );
  755. }
  756. }
  757. }
  758. break;
  759. }
  760. case SERIAL_IIR_MS: {
  761. SerialHandleModemUpdate(
  762. Extension,
  763. FALSE
  764. );
  765. break;
  766. }
  767. }
  768. } while (!((InterruptIdReg =
  769. READ_INTERRUPT_ID_REG(Extension->Controller))
  770. & SERIAL_IIR_NO_INTERRUPT_PENDING));
  771. //
  772. // Besides catching the WINBOND and SMC chip problems this
  773. // will also cause transmission to restart incase of an xon
  774. // char being received. Don't remove.
  775. //
  776. if (SerialProcessLSR(Extension) & SERIAL_LSR_THRE) {
  777. if (!Extension->TXHolding &&
  778. (Extension->WriteLength ||
  779. Extension->TransmitImmediate)) {
  780. goto doTrasmitStuff;
  781. }
  782. }
  783. }
  784. //
  785. // This will "unlock" the close and cause the event
  786. // to fire if we didn't queue any DPC's
  787. //
  788. {
  789. LONG pendingCnt;
  790. //
  791. // Increment once more. This is just a quick test to see if we
  792. // have a chance of causing the event to fire... we don't want
  793. // to run a DPC on every ISR if we don't have to....
  794. //
  795. retryDPCFiring:;
  796. InterlockedIncrement(&Extension->DpcCount);
  797. //
  798. // Decrement and see if the lock above looks like the only one left.
  799. //
  800. pendingCnt = InterlockedDecrement(&Extension->DpcCount);
  801. if (pendingCnt == 1) {
  802. KeInsertQueueDpc(&Extension->IsrUnlockPagesDpc, NULL, NULL);
  803. } else {
  804. if (InterlockedDecrement(&Extension->DpcCount) == 0) {
  805. //
  806. // We missed it. Retry...
  807. //
  808. InterlockedIncrement(&Extension->DpcCount);
  809. goto retryDPCFiring;
  810. }
  811. }
  812. }
  813. return ServicedAnInterrupt;
  814. }
  815. VOID
  816. SerialPutChar(
  817. IN PSERIAL_DEVICE_EXTENSION Extension,
  818. IN UCHAR CharToPut
  819. )
  820. /*++
  821. Routine Description:
  822. This routine, which only runs at device level, takes care of
  823. placing a character into the typeahead (receive) buffer.
  824. Arguments:
  825. Extension - The serial device extension.
  826. Return Value:
  827. None.
  828. --*/
  829. {
  830. SERIAL_LOCKED_PAGED_CODE();
  831. //
  832. // If we have dsr sensitivity enabled then
  833. // we need to check the modem status register
  834. // to see if it has changed.
  835. //
  836. if (Extension->HandFlow.ControlHandShake &
  837. SERIAL_DSR_SENSITIVITY) {
  838. SerialHandleModemUpdate(
  839. Extension,
  840. FALSE
  841. );
  842. if (Extension->RXHolding & SERIAL_RX_DSR) {
  843. //
  844. // We simply act as if we haven't
  845. // seen the character if we have dsr
  846. // sensitivity and the dsr line is low.
  847. //
  848. return;
  849. }
  850. }
  851. //
  852. // If the xoff counter is non-zero then decrement it.
  853. // If the counter then goes to zero, complete that irp.
  854. //
  855. if (Extension->CountSinceXoff) {
  856. Extension->CountSinceXoff--;
  857. if (!Extension->CountSinceXoff) {
  858. Extension->CurrentXoffIrp->IoStatus.Status = STATUS_SUCCESS;
  859. Extension->CurrentXoffIrp->IoStatus.Information = 0;
  860. SerialInsertQueueDpc(
  861. &Extension->XoffCountCompleteDpc,
  862. NULL,
  863. NULL,
  864. Extension
  865. );
  866. }
  867. }
  868. //
  869. // Check to see if we are copying into the
  870. // users buffer or into the interrupt buffer.
  871. //
  872. // If we are copying into the user buffer
  873. // then we know there is always room for one more.
  874. // (We know this because if there wasn't room
  875. // then that read would have completed and we
  876. // would be using the interrupt buffer.)
  877. //
  878. // If we are copying into the interrupt buffer
  879. // then we will need to check if we have enough
  880. // room.
  881. //
  882. if (Extension->ReadBufferBase !=
  883. Extension->InterruptReadBuffer) {
  884. //
  885. // Increment the following value so
  886. // that the interval timer (if one exists
  887. // for this read) can know that a character
  888. // has been read.
  889. //
  890. Extension->ReadByIsr++;
  891. //
  892. // We are in the user buffer. Place the
  893. // character into the buffer. See if the
  894. // read is complete.
  895. //
  896. *Extension->CurrentCharSlot = CharToPut;
  897. if (Extension->CurrentCharSlot ==
  898. Extension->LastCharSlot) {
  899. //
  900. // We've filled up the users buffer.
  901. // Switch back to the interrupt buffer
  902. // and send off a DPC to Complete the read.
  903. //
  904. // It is inherent that when we were using
  905. // a user buffer that the interrupt buffer
  906. // was empty.
  907. //
  908. Extension->ReadBufferBase =
  909. Extension->InterruptReadBuffer;
  910. Extension->CurrentCharSlot =
  911. Extension->InterruptReadBuffer;
  912. Extension->FirstReadableChar =
  913. Extension->InterruptReadBuffer;
  914. Extension->LastCharSlot =
  915. Extension->InterruptReadBuffer +
  916. (Extension->BufferSize - 1);
  917. Extension->CharsInInterruptBuffer = 0;
  918. Extension->CurrentReadIrp->IoStatus.Information =
  919. IoGetCurrentIrpStackLocation(
  920. Extension->CurrentReadIrp
  921. )->Parameters.Read.Length;
  922. SerialInsertQueueDpc(
  923. &Extension->CompleteReadDpc,
  924. NULL,
  925. NULL,
  926. Extension
  927. );
  928. } else {
  929. //
  930. // Not done with the users read.
  931. //
  932. Extension->CurrentCharSlot++;
  933. }
  934. } else {
  935. //
  936. // We need to see if we reached our flow
  937. // control threshold. If we have then
  938. // we turn on whatever flow control the
  939. // owner has specified. If no flow
  940. // control was specified, well..., we keep
  941. // trying to receive characters and hope that
  942. // we have enough room. Note that no matter
  943. // what flow control protocol we are using, it
  944. // will not prevent us from reading whatever
  945. // characters are available.
  946. //
  947. if ((Extension->HandFlow.ControlHandShake
  948. & SERIAL_DTR_MASK) ==
  949. SERIAL_DTR_HANDSHAKE) {
  950. //
  951. // If we are already doing a
  952. // dtr hold then we don't have
  953. // to do anything else.
  954. //
  955. if (!(Extension->RXHolding &
  956. SERIAL_RX_DTR)) {
  957. if ((Extension->BufferSize -
  958. Extension->HandFlow.XoffLimit)
  959. <= (Extension->CharsInInterruptBuffer+1)) {
  960. Extension->RXHolding |= SERIAL_RX_DTR;
  961. SerialClrDTR(Extension);
  962. }
  963. }
  964. }
  965. if ((Extension->HandFlow.FlowReplace
  966. & SERIAL_RTS_MASK) ==
  967. SERIAL_RTS_HANDSHAKE) {
  968. //
  969. // If we are already doing a
  970. // rts hold then we don't have
  971. // to do anything else.
  972. //
  973. if (!(Extension->RXHolding &
  974. SERIAL_RX_RTS)) {
  975. if ((Extension->BufferSize -
  976. Extension->HandFlow.XoffLimit)
  977. <= (Extension->CharsInInterruptBuffer+1)) {
  978. Extension->RXHolding |= SERIAL_RX_RTS;
  979. SerialClrRTS(Extension);
  980. }
  981. }
  982. }
  983. if (Extension->HandFlow.FlowReplace &
  984. SERIAL_AUTO_RECEIVE) {
  985. //
  986. // If we are already doing a
  987. // xoff hold then we don't have
  988. // to do anything else.
  989. //
  990. if (!(Extension->RXHolding &
  991. SERIAL_RX_XOFF)) {
  992. if ((Extension->BufferSize -
  993. Extension->HandFlow.XoffLimit)
  994. <= (Extension->CharsInInterruptBuffer+1)) {
  995. Extension->RXHolding |= SERIAL_RX_XOFF;
  996. //
  997. // If necessary cause an
  998. // off to be sent.
  999. //
  1000. SerialProdXonXoff(
  1001. Extension,
  1002. FALSE
  1003. );
  1004. }
  1005. }
  1006. }
  1007. if (Extension->CharsInInterruptBuffer <
  1008. Extension->BufferSize) {
  1009. *Extension->CurrentCharSlot = CharToPut;
  1010. Extension->CharsInInterruptBuffer++;
  1011. //
  1012. // If we've become 80% full on this character
  1013. // and this is an interesting event, note it.
  1014. //
  1015. if (Extension->CharsInInterruptBuffer ==
  1016. Extension->BufferSizePt8) {
  1017. if (Extension->IsrWaitMask &
  1018. SERIAL_EV_RX80FULL) {
  1019. Extension->HistoryMask |= SERIAL_EV_RX80FULL;
  1020. if (Extension->IrpMaskLocation) {
  1021. *Extension->IrpMaskLocation =
  1022. Extension->HistoryMask;
  1023. Extension->IrpMaskLocation = NULL;
  1024. Extension->HistoryMask = 0;
  1025. Extension->CurrentWaitIrp->
  1026. IoStatus.Information = sizeof(ULONG);
  1027. SerialInsertQueueDpc(
  1028. &Extension->CommWaitDpc,
  1029. NULL,
  1030. NULL,
  1031. Extension
  1032. );
  1033. }
  1034. }
  1035. }
  1036. //
  1037. // Point to the next available space
  1038. // for a received character. Make sure
  1039. // that we wrap around to the beginning
  1040. // of the buffer if this last character
  1041. // received was placed at the last slot
  1042. // in the buffer.
  1043. //
  1044. if (Extension->CurrentCharSlot ==
  1045. Extension->LastCharSlot) {
  1046. Extension->CurrentCharSlot =
  1047. Extension->InterruptReadBuffer;
  1048. } else {
  1049. Extension->CurrentCharSlot++;
  1050. }
  1051. } else {
  1052. //
  1053. // We have a new character but no room for it.
  1054. //
  1055. Extension->PerfStats.BufferOverrunErrorCount++;
  1056. Extension->WmiPerfData.BufferOverrunErrorCount++;
  1057. Extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
  1058. if (Extension->HandFlow.FlowReplace &
  1059. SERIAL_ERROR_CHAR) {
  1060. //
  1061. // Place the error character into the last
  1062. // valid place for a character. Be careful!,
  1063. // that place might not be the previous location!
  1064. //
  1065. if (Extension->CurrentCharSlot ==
  1066. Extension->InterruptReadBuffer) {
  1067. *(Extension->InterruptReadBuffer+
  1068. (Extension->BufferSize-1)) =
  1069. Extension->SpecialChars.ErrorChar;
  1070. } else {
  1071. *(Extension->CurrentCharSlot-1) =
  1072. Extension->SpecialChars.ErrorChar;
  1073. }
  1074. }
  1075. //
  1076. // If the application has requested it, abort all reads
  1077. // and writes on an error.
  1078. //
  1079. if (Extension->HandFlow.ControlHandShake &
  1080. SERIAL_ERROR_ABORT) {
  1081. SerialInsertQueueDpc(
  1082. &Extension->CommErrorDpc,
  1083. NULL,
  1084. NULL,
  1085. Extension
  1086. );
  1087. }
  1088. }
  1089. }
  1090. }
  1091. UCHAR
  1092. SerialProcessLSR(
  1093. IN PSERIAL_DEVICE_EXTENSION Extension
  1094. )
  1095. /*++
  1096. Routine Description:
  1097. This routine, which only runs at device level, reads the
  1098. ISR and totally processes everything that might have
  1099. changed.
  1100. Arguments:
  1101. Extension - The serial device extension.
  1102. Return Value:
  1103. The value of the line status register.
  1104. --*/
  1105. {
  1106. UCHAR LineStatus = READ_LINE_STATUS(Extension->Controller);
  1107. SERIAL_LOCKED_PAGED_CODE();
  1108. Extension->HoldingEmpty = !!(LineStatus & SERIAL_LSR_THRE);
  1109. //
  1110. // If the line status register is just the fact that
  1111. // the trasmit registers are empty or a character is
  1112. // received then we want to reread the interrupt
  1113. // identification register so that we just pick up that.
  1114. //
  1115. if (LineStatus & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT
  1116. | SERIAL_LSR_DR)) {
  1117. //
  1118. // We have some sort of data problem in the receive.
  1119. // For any of these errors we may abort all current
  1120. // reads and writes.
  1121. //
  1122. //
  1123. // If we are inserting the value of the line status
  1124. // into the data stream then we should put the escape
  1125. // character in now.
  1126. //
  1127. if (Extension->EscapeChar) {
  1128. SerialPutChar(
  1129. Extension,
  1130. Extension->EscapeChar
  1131. );
  1132. SerialPutChar(
  1133. Extension,
  1134. (UCHAR)((LineStatus & SERIAL_LSR_DR)?
  1135. (SERIAL_LSRMST_LSR_DATA):(SERIAL_LSRMST_LSR_NODATA))
  1136. );
  1137. SerialPutChar(
  1138. Extension,
  1139. LineStatus
  1140. );
  1141. if (LineStatus & SERIAL_LSR_DR) {
  1142. Extension->PerfStats.ReceivedCount++;
  1143. Extension->WmiPerfData.ReceivedCount++;
  1144. SerialPutChar(
  1145. Extension,
  1146. READ_RECEIVE_BUFFER(Extension->Controller)
  1147. );
  1148. }
  1149. }
  1150. if (LineStatus & SERIAL_LSR_OE) {
  1151. Extension->PerfStats.SerialOverrunErrorCount++;
  1152. Extension->WmiPerfData.SerialOverrunErrorCount++;
  1153. Extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
  1154. if (Extension->HandFlow.FlowReplace &
  1155. SERIAL_ERROR_CHAR) {
  1156. SerialPutChar(
  1157. Extension,
  1158. Extension->SpecialChars.ErrorChar
  1159. );
  1160. if (LineStatus & SERIAL_LSR_DR) {
  1161. Extension->PerfStats.ReceivedCount++;
  1162. Extension->WmiPerfData.ReceivedCount++;
  1163. READ_RECEIVE_BUFFER(Extension->Controller);
  1164. }
  1165. } else {
  1166. if (LineStatus & SERIAL_LSR_DR) {
  1167. Extension->PerfStats.ReceivedCount++;
  1168. Extension->WmiPerfData.ReceivedCount++;
  1169. SerialPutChar(
  1170. Extension,
  1171. READ_RECEIVE_BUFFER(
  1172. Extension->Controller
  1173. )
  1174. );
  1175. }
  1176. }
  1177. }
  1178. if (LineStatus & SERIAL_LSR_BI) {
  1179. Extension->ErrorWord |= SERIAL_ERROR_BREAK;
  1180. if (Extension->HandFlow.FlowReplace &
  1181. SERIAL_BREAK_CHAR) {
  1182. SerialPutChar(
  1183. Extension,
  1184. Extension->SpecialChars.BreakChar
  1185. );
  1186. }
  1187. } else {
  1188. //
  1189. // Framing errors only count if they
  1190. // occur exclusive of a break being
  1191. // received.
  1192. //
  1193. if (LineStatus & SERIAL_LSR_PE) {
  1194. Extension->PerfStats.ParityErrorCount++;
  1195. Extension->WmiPerfData.ParityErrorCount++;
  1196. Extension->ErrorWord |= SERIAL_ERROR_PARITY;
  1197. if (Extension->HandFlow.FlowReplace &
  1198. SERIAL_ERROR_CHAR) {
  1199. SerialPutChar(
  1200. Extension,
  1201. Extension->SpecialChars.ErrorChar
  1202. );
  1203. if (LineStatus & SERIAL_LSR_DR) {
  1204. Extension->PerfStats.ReceivedCount++;
  1205. Extension->WmiPerfData.ReceivedCount++;
  1206. READ_RECEIVE_BUFFER(Extension->Controller);
  1207. }
  1208. }
  1209. }
  1210. if (LineStatus & SERIAL_LSR_FE) {
  1211. Extension->PerfStats.FrameErrorCount++;
  1212. Extension->WmiPerfData.FrameErrorCount++;
  1213. Extension->ErrorWord |= SERIAL_ERROR_FRAMING;
  1214. if (Extension->HandFlow.FlowReplace &
  1215. SERIAL_ERROR_CHAR) {
  1216. SerialPutChar(
  1217. Extension,
  1218. Extension->SpecialChars.ErrorChar
  1219. );
  1220. if (LineStatus & SERIAL_LSR_DR) {
  1221. Extension->PerfStats.ReceivedCount++;
  1222. Extension->WmiPerfData.ReceivedCount++;
  1223. READ_RECEIVE_BUFFER(Extension->Controller);
  1224. }
  1225. }
  1226. }
  1227. }
  1228. //
  1229. // If the application has requested it,
  1230. // abort all the reads and writes
  1231. // on an error.
  1232. //
  1233. if (Extension->HandFlow.ControlHandShake &
  1234. SERIAL_ERROR_ABORT) {
  1235. SerialInsertQueueDpc(
  1236. &Extension->CommErrorDpc,
  1237. NULL,
  1238. NULL,
  1239. Extension
  1240. );
  1241. }
  1242. //
  1243. // Check to see if we have a wait
  1244. // pending on the comm error events. If we
  1245. // do then we schedule a dpc to satisfy
  1246. // that wait.
  1247. //
  1248. if (Extension->IsrWaitMask) {
  1249. if ((Extension->IsrWaitMask & SERIAL_EV_ERR) &&
  1250. (LineStatus & (SERIAL_LSR_OE |
  1251. SERIAL_LSR_PE |
  1252. SERIAL_LSR_FE))) {
  1253. Extension->HistoryMask |= SERIAL_EV_ERR;
  1254. }
  1255. if ((Extension->IsrWaitMask & SERIAL_EV_BREAK) &&
  1256. (LineStatus & SERIAL_LSR_BI)) {
  1257. Extension->HistoryMask |= SERIAL_EV_BREAK;
  1258. }
  1259. if (Extension->IrpMaskLocation &&
  1260. Extension->HistoryMask) {
  1261. *Extension->IrpMaskLocation =
  1262. Extension->HistoryMask;
  1263. Extension->IrpMaskLocation = NULL;
  1264. Extension->HistoryMask = 0;
  1265. Extension->CurrentWaitIrp->IoStatus.Information =
  1266. sizeof(ULONG);
  1267. SerialInsertQueueDpc(
  1268. &Extension->CommWaitDpc,
  1269. NULL,
  1270. NULL,
  1271. Extension
  1272. );
  1273. }
  1274. }
  1275. if (LineStatus & SERIAL_LSR_THRE) {
  1276. //
  1277. // There is a hardware bug in some versions
  1278. // of the 16450 and 550. If THRE interrupt
  1279. // is pending, but a higher interrupt comes
  1280. // in it will only return the higher and
  1281. // *forget* about the THRE.
  1282. //
  1283. // A suitable workaround - whenever we
  1284. // are *all* done reading line status
  1285. // of the device we check to see if the
  1286. // transmit holding register is empty. If it is
  1287. // AND we are currently transmitting data
  1288. // enable the interrupts which should cause
  1289. // an interrupt indication which we quiet
  1290. // when we read the interrupt id register.
  1291. //
  1292. if (Extension->WriteLength |
  1293. Extension->TransmitImmediate) {
  1294. DISABLE_ALL_INTERRUPTS(
  1295. Extension->Controller
  1296. );
  1297. ENABLE_ALL_INTERRUPTS(
  1298. Extension->Controller
  1299. );
  1300. }
  1301. }
  1302. }
  1303. return LineStatus;
  1304. }