Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1803 lines
45 KiB

  1. /*++
  2. Copyright (c) 1991, 1992, 1993 Microsoft Corporation
  3. Module Name:
  4. modmflow.c
  5. Abstract:
  6. This module contains *MOST* of the code used to manipulate
  7. the modem control and status registers. The vast majority
  8. of the remainder of flow control is concentrated in the
  9. Interrupt service routine. A very small amount resides
  10. in the read code that pull characters out of the interrupt
  11. buffer.
  12. Author:
  13. Anthony V. Ercolano 26-Sep-1991
  14. Environment:
  15. Kernel mode
  16. Revision History :
  17. --*/
  18. #include "precomp.h"
  19. BOOLEAN
  20. SerialDecrementRTSCounter(
  21. IN PVOID Context
  22. );
  23. #ifdef ALLOC_PRAGMA
  24. #if 0
  25. #pragma alloc_text(PAGESER,SerialHandleReducedIntBuffer)
  26. #pragma alloc_text(PAGESER,SerialProdXonXoff)
  27. #pragma alloc_text(PAGESER,SerialHandleModemUpdate)
  28. #pragma alloc_text(PAGESER,SerialPerhapsLowerRTS)
  29. #pragma alloc_text(PAGESER,SerialStartTimerLowerRTS)
  30. #pragma alloc_text(PAGESER,SerialInvokePerhapsLowerRTS)
  31. #pragma alloc_text(PAGESER,SerialSetDTR)
  32. //#pragma alloc_text(PAGESER,SerialClrDTR)
  33. #pragma alloc_text(PAGESER,SerialSetRTS)
  34. //#pragma alloc_text(PAGESER,SerialClrRTS)
  35. //#pragma alloc_text(PAGESER,SerialSetupNewHandFlow)
  36. #pragma alloc_text(PAGESER,SerialSetHandFlow)
  37. #pragma alloc_text(PAGESER,SerialTurnOnBreak)
  38. #pragma alloc_text(PAGESER,SerialTurnOffBreak)
  39. #pragma alloc_text(PAGESER,SerialPretendXoff)
  40. #pragma alloc_text(PAGESER,SerialPretendXon)
  41. #pragma alloc_text(PAGESER,SerialDecrementRTSCounter)
  42. #endif
  43. #endif
  44. BOOLEAN
  45. SerialSetDTR(
  46. IN PVOID Context
  47. )
  48. /*++
  49. Routine Description:
  50. This routine which is only called at interrupt level is used
  51. to set the DTR in the modem control register.
  52. Arguments:
  53. Context - Really a pointer to the device extension.
  54. Return Value:
  55. This routine always returns FALSE.
  56. --*/
  57. {
  58. PSERIAL_DEVICE_EXTENSION Extension = Context;
  59. UCHAR ModemControl;
  60. ModemControl = READ_MODEM_CONTROL(Extension->Controller);
  61. ModemControl |= SERIAL_MCR_DTR;
  62. SerialDump(
  63. SERFLOW,
  64. ("SERIAL: Setting DTR for %x\n",
  65. Extension->Controller)
  66. );
  67. WRITE_MODEM_CONTROL(
  68. Extension->Controller,
  69. ModemControl
  70. );
  71. return FALSE;
  72. }
  73. BOOLEAN
  74. SerialClrDTR(
  75. IN PVOID Context
  76. )
  77. /*++
  78. Routine Description:
  79. This routine which is only called at interrupt level is used
  80. to clear the DTR in the modem control register.
  81. Arguments:
  82. Context - Really a pointer to the device extension.
  83. Return Value:
  84. This routine always returns FALSE.
  85. --*/
  86. {
  87. PSERIAL_DEVICE_EXTENSION Extension = Context;
  88. UCHAR ModemControl;
  89. ModemControl = READ_MODEM_CONTROL(Extension->Controller);
  90. ModemControl &= ~SERIAL_MCR_DTR;
  91. SerialDump(
  92. SERFLOW,
  93. ("SERIAL: Clearing DTR for %x\n",
  94. Extension->Controller)
  95. );
  96. WRITE_MODEM_CONTROL(
  97. Extension->Controller,
  98. ModemControl
  99. );
  100. return FALSE;
  101. }
  102. BOOLEAN
  103. SerialSetRTS(
  104. IN PVOID Context
  105. )
  106. /*++
  107. Routine Description:
  108. This routine which is only called at interrupt level is used
  109. to set the RTS in the modem control register.
  110. Arguments:
  111. Context - Really a pointer to the device extension.
  112. Return Value:
  113. This routine always returns FALSE.
  114. --*/
  115. {
  116. PSERIAL_DEVICE_EXTENSION Extension = Context;
  117. UCHAR ModemControl;
  118. ModemControl = READ_MODEM_CONTROL(Extension->Controller);
  119. ModemControl |= SERIAL_MCR_RTS;
  120. SerialDump(
  121. SERFLOW,
  122. ("SERIAL: Setting Rts for %x\n",
  123. Extension->Controller)
  124. );
  125. WRITE_MODEM_CONTROL(
  126. Extension->Controller,
  127. ModemControl
  128. );
  129. return FALSE;
  130. }
  131. BOOLEAN
  132. SerialClrRTS(
  133. IN PVOID Context
  134. )
  135. /*++
  136. Routine Description:
  137. This routine which is only called at interrupt level is used
  138. to clear the RTS in the modem control register.
  139. Arguments:
  140. Context - Really a pointer to the device extension.
  141. Return Value:
  142. This routine always returns FALSE.
  143. --*/
  144. {
  145. PSERIAL_DEVICE_EXTENSION Extension = Context;
  146. UCHAR ModemControl;
  147. ModemControl = READ_MODEM_CONTROL(Extension->Controller);
  148. ModemControl &= ~SERIAL_MCR_RTS;
  149. SerialDump(
  150. SERFLOW,
  151. ("SERIAL: Clearing Rts for %x\n",
  152. Extension->Controller)
  153. );
  154. WRITE_MODEM_CONTROL(
  155. Extension->Controller,
  156. ModemControl
  157. );
  158. return FALSE;
  159. }
  160. BOOLEAN
  161. SerialSetupNewHandFlow(
  162. IN PSERIAL_DEVICE_EXTENSION Extension,
  163. IN PSERIAL_HANDFLOW NewHandFlow
  164. )
  165. /*++
  166. Routine Description:
  167. This routine adjusts the flow control based on new
  168. control flow.
  169. Arguments:
  170. Extension - A pointer to the serial device extension.
  171. NewHandFlow - A pointer to a serial handflow structure
  172. that is to become the new setup for flow
  173. control.
  174. Return Value:
  175. This routine always returns FALSE.
  176. --*/
  177. {
  178. SERIAL_HANDFLOW New = *NewHandFlow;
  179. //
  180. // If the Extension->DeviceIsOpened is FALSE that means
  181. // we are entering this routine in response to an open request.
  182. // If that is so, then we always proceed with the work regardless
  183. // of whether things have changed.
  184. //
  185. //
  186. // First we take care of the DTR flow control. We only
  187. // do work if something has changed.
  188. //
  189. if ((!Extension->DeviceIsOpened) ||
  190. ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) !=
  191. (New.ControlHandShake & SERIAL_DTR_MASK))) {
  192. SerialDump(
  193. SERFLOW,
  194. ("SERIAL: Processing DTR flow for %x\n",
  195. Extension->Controller)
  196. );
  197. if (New.ControlHandShake & SERIAL_DTR_MASK) {
  198. //
  199. // Well we might want to set DTR.
  200. //
  201. // Before we do, we need to check whether we are doing
  202. // dtr flow control. If we are then we need to check
  203. // if then number of characters in the interrupt buffer
  204. // exceeds the XoffLimit. If it does then we don't
  205. // enable DTR AND we set the RXHolding to record that
  206. // we are holding because of the dtr.
  207. //
  208. if ((New.ControlHandShake & SERIAL_DTR_MASK)
  209. == SERIAL_DTR_HANDSHAKE) {
  210. if ((Extension->BufferSize - New.XoffLimit) >
  211. Extension->CharsInInterruptBuffer) {
  212. //
  213. // However if we are already holding we don't want
  214. // to turn it back on unless we exceed the Xon
  215. // limit.
  216. //
  217. if (Extension->RXHolding & SERIAL_RX_DTR) {
  218. //
  219. // We can assume that its DTR line is already low.
  220. //
  221. if (Extension->CharsInInterruptBuffer >
  222. (ULONG)New.XonLimit) {
  223. SerialDump(
  224. SERFLOW,
  225. ("SERIAL: Removing DTR block on reception for %x\n",
  226. Extension->Controller)
  227. );
  228. Extension->RXHolding &= ~SERIAL_RX_DTR;
  229. SerialSetDTR(Extension);
  230. }
  231. } else {
  232. SerialSetDTR(Extension);
  233. }
  234. } else {
  235. SerialDump(
  236. SERFLOW,
  237. ("SERIAL: Setting DTR block on reception for %x\n",
  238. Extension->Controller)
  239. );
  240. Extension->RXHolding |= SERIAL_RX_DTR;
  241. SerialClrDTR(Extension);
  242. }
  243. } else {
  244. //
  245. // Note that if we aren't currently doing dtr flow control then
  246. // we MIGHT have been. So even if we aren't currently doing
  247. // DTR flow control, we should still check if RX is holding
  248. // because of DTR. If it is, then we should clear the holding
  249. // of this bit.
  250. //
  251. if (Extension->RXHolding & SERIAL_RX_DTR) {
  252. SerialDump(
  253. SERFLOW,
  254. ("SERIAL: Removing dtr block of reception for %x\n",
  255. Extension->Controller)
  256. );
  257. Extension->RXHolding &= ~SERIAL_RX_DTR;
  258. }
  259. SerialSetDTR(Extension);
  260. }
  261. } else {
  262. //
  263. // The end result here will be that DTR is cleared.
  264. //
  265. // We first need to check whether reception is being held
  266. // up because of previous DTR flow control. If it is then
  267. // we should clear that reason in the RXHolding mask.
  268. //
  269. if (Extension->RXHolding & SERIAL_RX_DTR) {
  270. SerialDump(
  271. SERFLOW,
  272. ("SERIAL: removing dtr block of reception for %x\n",
  273. Extension->Controller)
  274. );
  275. Extension->RXHolding &= ~SERIAL_RX_DTR;
  276. }
  277. SerialClrDTR(Extension);
  278. }
  279. }
  280. //
  281. // Time to take care of the RTS Flow control.
  282. //
  283. // First we only do work if something has changed.
  284. //
  285. if ((!Extension->DeviceIsOpened) ||
  286. ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) !=
  287. (New.FlowReplace & SERIAL_RTS_MASK))) {
  288. SerialDump(
  289. SERFLOW,
  290. ("SERIAL: Processing RTS flow\n",
  291. Extension->Controller)
  292. );
  293. if ((New.FlowReplace & SERIAL_RTS_MASK) ==
  294. SERIAL_RTS_HANDSHAKE) {
  295. //
  296. // Well we might want to set RTS.
  297. //
  298. // Before we do, we need to check whether we are doing
  299. // rts flow control. If we are then we need to check
  300. // if then number of characters in the interrupt buffer
  301. // exceeds the XoffLimit. If it does then we don't
  302. // enable RTS AND we set the RXHolding to record that
  303. // we are holding because of the rts.
  304. //
  305. if ((Extension->BufferSize - New.XoffLimit) >
  306. Extension->CharsInInterruptBuffer) {
  307. //
  308. // However if we are already holding we don't want
  309. // to turn it back on unless we exceed the Xon
  310. // limit.
  311. //
  312. if (Extension->RXHolding & SERIAL_RX_RTS) {
  313. //
  314. // We can assume that its RTS line is already low.
  315. //
  316. if (Extension->CharsInInterruptBuffer >
  317. (ULONG)New.XonLimit) {
  318. SerialDump(
  319. SERFLOW,
  320. ("SERIAL: Removing rts block of reception for %x\n",
  321. Extension->Controller)
  322. );
  323. Extension->RXHolding &= ~SERIAL_RX_RTS;
  324. SerialSetRTS(Extension);
  325. }
  326. } else {
  327. SerialSetRTS(Extension);
  328. }
  329. } else {
  330. SerialDump(
  331. SERFLOW,
  332. ("SERIAL: Setting rts block of reception for %x\n",
  333. Extension->Controller)
  334. );
  335. Extension->RXHolding |= SERIAL_RX_RTS;
  336. SerialClrRTS(Extension);
  337. }
  338. } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
  339. SERIAL_RTS_CONTROL) {
  340. //
  341. // Note that if we aren't currently doing rts flow control then
  342. // we MIGHT have been. So even if we aren't currently doing
  343. // RTS flow control, we should still check if RX is holding
  344. // because of RTS. If it is, then we should clear the holding
  345. // of this bit.
  346. //
  347. if (Extension->RXHolding & SERIAL_RX_RTS) {
  348. SerialDump(
  349. SERFLOW,
  350. ("SERIAL: Clearing rts block of reception for %x\n",
  351. Extension->Controller)
  352. );
  353. Extension->RXHolding &= ~SERIAL_RX_RTS;
  354. }
  355. SerialSetRTS(Extension);
  356. } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
  357. SERIAL_TRANSMIT_TOGGLE) {
  358. //
  359. // We first need to check whether reception is being held
  360. // up because of previous RTS flow control. If it is then
  361. // we should clear that reason in the RXHolding mask.
  362. //
  363. if (Extension->RXHolding & SERIAL_RX_RTS) {
  364. SerialDump(
  365. SERFLOW,
  366. ("SERIAL: TOGGLE Clearing rts block of reception for %x\n",
  367. Extension->Controller)
  368. );
  369. Extension->RXHolding &= ~SERIAL_RX_RTS;
  370. }
  371. //
  372. // We have to place the rts value into the Extension
  373. // now so that the code that tests whether the
  374. // rts line should be lowered will find that we
  375. // are "still" doing transmit toggling. The code
  376. // for lowering can be invoked later by a timer so
  377. // it has to test whether it still needs to do its
  378. // work.
  379. //
  380. Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
  381. Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE;
  382. //
  383. // The order of the tests is very important below.
  384. //
  385. // If there is a break then we should turn on the RTS.
  386. //
  387. // If there isn't a break but there are characters in
  388. // the hardware, then turn on the RTS.
  389. //
  390. // If there are writes pending that aren't being held
  391. // up, then turn on the RTS.
  392. //
  393. if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
  394. ((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE |
  395. SERIAL_LSR_TEMT)) !=
  396. (SERIAL_LSR_THRE |
  397. SERIAL_LSR_TEMT)) ||
  398. (Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
  399. (!IsListEmpty(&Extension->WriteQueue)) &&
  400. (!Extension->TXHolding))) {
  401. SerialSetRTS(Extension);
  402. } else {
  403. //
  404. // This routine will check to see if it is time
  405. // to lower the RTS because of transmit toggle
  406. // being on. If it is ok to lower it, it will,
  407. // if it isn't ok, it will schedule things so
  408. // that it will get lowered later.
  409. //
  410. Extension->CountOfTryingToLowerRTS++;
  411. SerialPerhapsLowerRTS(Extension);
  412. }
  413. } else {
  414. //
  415. // The end result here will be that RTS is cleared.
  416. //
  417. // We first need to check whether reception is being held
  418. // up because of previous RTS flow control. If it is then
  419. // we should clear that reason in the RXHolding mask.
  420. //
  421. if (Extension->RXHolding & SERIAL_RX_RTS) {
  422. SerialDump(
  423. SERFLOW,
  424. ("SERIAL: Clearing rts block of reception for %x\n",
  425. Extension->Controller)
  426. );
  427. Extension->RXHolding &= ~SERIAL_RX_RTS;
  428. }
  429. SerialClrRTS(Extension);
  430. }
  431. }
  432. //
  433. // We now take care of automatic receive flow control.
  434. // We only do work if things have changed.
  435. //
  436. if ((!Extension->DeviceIsOpened) ||
  437. ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) !=
  438. (New.FlowReplace & SERIAL_AUTO_RECEIVE))) {
  439. if (New.FlowReplace & SERIAL_AUTO_RECEIVE) {
  440. //
  441. // We wouldn't be here if it had been on before.
  442. //
  443. // We should check to see whether we exceed the turn
  444. // off limits.
  445. //
  446. // Note that since we are following the OS/2 flow
  447. // control rules we will never send an xon if
  448. // when enabling xon/xoff flow control we discover that
  449. // we could receive characters but we are held up do
  450. // to a previous Xoff.
  451. //
  452. if ((Extension->BufferSize - New.XoffLimit) <=
  453. Extension->CharsInInterruptBuffer) {
  454. //
  455. // Cause the Xoff to be sent.
  456. //
  457. Extension->RXHolding |= SERIAL_RX_XOFF;
  458. SerialProdXonXoff(
  459. Extension,
  460. FALSE
  461. );
  462. }
  463. } else {
  464. //
  465. // The app has disabled automatic receive flow control.
  466. //
  467. // If transmission was being held up because of
  468. // an automatic receive Xoff, then we should
  469. // cause an Xon to be sent.
  470. //
  471. if (Extension->RXHolding & SERIAL_RX_XOFF) {
  472. Extension->RXHolding &= ~SERIAL_RX_XOFF;
  473. //
  474. // Cause the Xon to be sent.
  475. //
  476. SerialProdXonXoff(
  477. Extension,
  478. TRUE
  479. );
  480. }
  481. }
  482. }
  483. //
  484. // We now take care of automatic transmit flow control.
  485. // We only do work if things have changed.
  486. //
  487. if ((!Extension->DeviceIsOpened) ||
  488. ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) !=
  489. (New.FlowReplace & SERIAL_AUTO_TRANSMIT))) {
  490. if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) {
  491. //
  492. // We wouldn't be here if it had been on before.
  493. //
  494. // BUG BUG ??? There is some belief that if autotransmit
  495. // was just enabled, I should go look in what we
  496. // already received, and if we find the xoff character
  497. // then we should stop transmitting. I think this
  498. // is an application bug. For now we just care about
  499. // what we see in the future.
  500. //
  501. ;
  502. } else {
  503. //
  504. // The app has disabled automatic transmit flow control.
  505. //
  506. // If transmission was being held up because of
  507. // an automatic transmit Xoff, then we should
  508. // cause an Xon to be sent.
  509. //
  510. if (Extension->TXHolding & SERIAL_TX_XOFF) {
  511. Extension->TXHolding &= ~SERIAL_TX_XOFF;
  512. //
  513. // Cause the Xon to be sent.
  514. //
  515. SerialProdXonXoff(
  516. Extension,
  517. TRUE
  518. );
  519. }
  520. }
  521. }
  522. //
  523. // At this point we can simply make sure that entire
  524. // handflow structure in the extension is updated.
  525. //
  526. Extension->HandFlow = New;
  527. return FALSE;
  528. }
  529. BOOLEAN
  530. SerialSetHandFlow(
  531. IN PVOID Context
  532. )
  533. /*++
  534. Routine Description:
  535. This routine is used to set the handshake and control
  536. flow in the device extension.
  537. Arguments:
  538. Context - Pointer to a structure that contains a pointer to
  539. the device extension and a pointer to a handflow
  540. structure..
  541. Return Value:
  542. This routine always returns FALSE.
  543. --*/
  544. {
  545. PSERIAL_IOCTL_SYNC S = Context;
  546. PSERIAL_DEVICE_EXTENSION Extension = S->Extension;
  547. PSERIAL_HANDFLOW HandFlow = S->Data;
  548. SerialSetupNewHandFlow(
  549. Extension,
  550. HandFlow
  551. );
  552. SerialHandleModemUpdate(
  553. Extension,
  554. FALSE
  555. );
  556. return FALSE;
  557. }
  558. BOOLEAN
  559. SerialTurnOnBreak(
  560. IN PVOID Context
  561. )
  562. /*++
  563. Routine Description:
  564. This routine will turn on break in the hardware and
  565. record the fact the break is on, in the extension variable
  566. that holds reasons that transmission is stopped.
  567. Arguments:
  568. Context - Really a pointer to the device extension.
  569. Return Value:
  570. This routine always returns FALSE.
  571. --*/
  572. {
  573. PSERIAL_DEVICE_EXTENSION Extension = Context;
  574. UCHAR OldLineControl;
  575. if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
  576. SERIAL_TRANSMIT_TOGGLE) {
  577. SerialSetRTS(Extension);
  578. }
  579. OldLineControl = READ_LINE_CONTROL(Extension->Controller);
  580. OldLineControl |= SERIAL_LCR_BREAK;
  581. WRITE_LINE_CONTROL(
  582. Extension->Controller,
  583. OldLineControl
  584. );
  585. Extension->TXHolding |= SERIAL_TX_BREAK;
  586. return FALSE;
  587. }
  588. BOOLEAN
  589. SerialTurnOffBreak(
  590. IN PVOID Context
  591. )
  592. /*++
  593. Routine Description:
  594. This routine will turn off break in the hardware and
  595. record the fact the break is off, in the extension variable
  596. that holds reasons that transmission is stopped.
  597. Arguments:
  598. Context - Really a pointer to the device extension.
  599. Return Value:
  600. This routine always returns FALSE.
  601. --*/
  602. {
  603. PSERIAL_DEVICE_EXTENSION Extension = Context;
  604. UCHAR OldLineControl;
  605. if (Extension->TXHolding & SERIAL_TX_BREAK) {
  606. //
  607. // We actually have a good reason for testing if transmission
  608. // is holding instead of blindly clearing the bit.
  609. //
  610. // If transmission actually was holding and the result of
  611. // clearing the bit is that we should restart transmission
  612. // then we will poke the interrupt enable bit, which will
  613. // cause an actual interrupt and transmission will then
  614. // restart on its own.
  615. //
  616. // If transmission wasn't holding and we poked the bit
  617. // then we would interrupt before a character actually made
  618. // it out and we could end up over writing a character in
  619. // the transmission hardware.
  620. OldLineControl = READ_LINE_CONTROL(Extension->Controller);
  621. OldLineControl &= ~SERIAL_LCR_BREAK;
  622. WRITE_LINE_CONTROL(
  623. Extension->Controller,
  624. OldLineControl
  625. );
  626. Extension->TXHolding &= ~SERIAL_TX_BREAK;
  627. if (!Extension->TXHolding &&
  628. (Extension->TransmitImmediate ||
  629. Extension->WriteLength) &&
  630. Extension->HoldingEmpty) {
  631. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  632. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  633. } else {
  634. //
  635. // The following routine will lower the rts if we
  636. // are doing transmit toggleing and there is no
  637. // reason to keep it up.
  638. //
  639. Extension->CountOfTryingToLowerRTS++;
  640. SerialPerhapsLowerRTS(Extension);
  641. }
  642. }
  643. return FALSE;
  644. }
  645. BOOLEAN
  646. SerialPretendXoff(
  647. IN PVOID Context
  648. )
  649. /*++
  650. Routine Description:
  651. This routine is used to process the Ioctl that request the
  652. driver to act as if an Xoff was received. Even if the
  653. driver does not have automatic Xoff/Xon flowcontrol - This
  654. still will stop the transmission. This is the OS/2 behavior
  655. and is not well specified for Windows. Therefore we adopt
  656. the OS/2 behavior.
  657. Note: If the driver does not have automatic Xoff/Xon enabled
  658. then the only way to restart transmission is for the
  659. application to request we "act" as if we saw the xon.
  660. Arguments:
  661. Context - Really a pointer to the device extension.
  662. Return Value:
  663. This routine always returns FALSE.
  664. --*/
  665. {
  666. PSERIAL_DEVICE_EXTENSION Extension = Context;
  667. Extension->TXHolding |= SERIAL_TX_XOFF;
  668. if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
  669. SERIAL_TRANSMIT_TOGGLE) {
  670. SerialInsertQueueDpc(
  671. &Extension->StartTimerLowerRTSDpc,
  672. NULL,
  673. NULL,
  674. Extension
  675. )?Extension->CountOfTryingToLowerRTS++:0;
  676. }
  677. return FALSE;
  678. }
  679. BOOLEAN
  680. SerialPretendXon(
  681. IN PVOID Context
  682. )
  683. /*++
  684. Routine Description:
  685. This routine is used to process the Ioctl that request the
  686. driver to act as if an Xon was received.
  687. Note: If the driver does not have automatic Xoff/Xon enabled
  688. then the only way to restart transmission is for the
  689. application to request we "act" as if we saw the xon.
  690. Arguments:
  691. Context - Really a pointer to the device extension.
  692. Return Value:
  693. This routine always returns FALSE.
  694. --*/
  695. {
  696. PSERIAL_DEVICE_EXTENSION Extension = Context;
  697. if (Extension->TXHolding) {
  698. //
  699. // We actually have a good reason for testing if transmission
  700. // is holding instead of blindly clearing the bit.
  701. //
  702. // If transmission actually was holding and the result of
  703. // clearing the bit is that we should restart transmission
  704. // then we will poke the interrupt enable bit, which will
  705. // cause an actual interrupt and transmission will then
  706. // restart on its own.
  707. //
  708. // If transmission wasn't holding and we poked the bit
  709. // then we would interrupt before a character actually made
  710. // it out and we could end up over writing a character in
  711. // the transmission hardware.
  712. Extension->TXHolding &= ~SERIAL_TX_XOFF;
  713. if (!Extension->TXHolding &&
  714. (Extension->TransmitImmediate ||
  715. Extension->WriteLength) &&
  716. Extension->HoldingEmpty) {
  717. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  718. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  719. }
  720. }
  721. return FALSE;
  722. }
  723. VOID
  724. SerialHandleReducedIntBuffer(
  725. IN PSERIAL_DEVICE_EXTENSION Extension
  726. )
  727. /*++
  728. Routine Description:
  729. This routine is called to handle a reduction in the number
  730. of characters in the interrupt (typeahead) buffer. It
  731. will check the current output flow control and re-enable transmission
  732. as needed.
  733. NOTE: This routine assumes that it is working at interrupt level.
  734. Arguments:
  735. Extension - A pointer to the device extension.
  736. Return Value:
  737. None.
  738. --*/
  739. {
  740. //
  741. // If we are doing receive side flow control and we are
  742. // currently "holding" then because we've emptied out
  743. // some characters from the interrupt buffer we need to
  744. // see if we can "re-enable" reception.
  745. //
  746. if (Extension->RXHolding) {
  747. if (Extension->CharsInInterruptBuffer <=
  748. (ULONG)Extension->HandFlow.XonLimit) {
  749. if (Extension->RXHolding & SERIAL_RX_DTR) {
  750. Extension->RXHolding &= ~SERIAL_RX_DTR;
  751. SerialSetDTR(Extension);
  752. }
  753. if (Extension->RXHolding & SERIAL_RX_RTS) {
  754. Extension->RXHolding &= ~SERIAL_RX_RTS;
  755. SerialSetRTS(Extension);
  756. }
  757. if (Extension->RXHolding & SERIAL_RX_XOFF) {
  758. //
  759. // Prod the transmit code to send xon.
  760. //
  761. SerialProdXonXoff(
  762. Extension,
  763. TRUE
  764. );
  765. }
  766. }
  767. }
  768. }
  769. VOID
  770. SerialProdXonXoff(
  771. IN PSERIAL_DEVICE_EXTENSION Extension,
  772. IN BOOLEAN SendXon
  773. )
  774. /*++
  775. Routine Description:
  776. This routine will set up the SendXxxxChar variables if
  777. necessary and determine if we are going to be interrupting
  778. because of current transmission state. It will cause an
  779. interrupt to occur if neccessary, to send the xon/xoff char.
  780. NOTE: This routine assumes that it is called at interrupt
  781. level.
  782. Arguments:
  783. Extension - A pointer to the serial device extension.
  784. SendXon - If a character is to be send, this indicates whether
  785. it should be an Xon or an Xoff.
  786. Return Value:
  787. None.
  788. --*/
  789. {
  790. //
  791. // We assume that if the prodding is called more than
  792. // once that the last prod has set things up appropriately.
  793. //
  794. // We could get called before the character is sent out
  795. // because the send of the character was blocked because
  796. // of hardware flow control (or break).
  797. //
  798. if (!Extension->SendXonChar && !Extension->SendXoffChar
  799. && Extension->HoldingEmpty) {
  800. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  801. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  802. }
  803. if (SendXon) {
  804. Extension->SendXonChar = TRUE;
  805. Extension->SendXoffChar = FALSE;
  806. } else {
  807. Extension->SendXonChar = FALSE;
  808. Extension->SendXoffChar = TRUE;
  809. }
  810. }
  811. ULONG
  812. SerialHandleModemUpdate(
  813. IN PSERIAL_DEVICE_EXTENSION Extension,
  814. IN BOOLEAN DoingTX
  815. )
  816. /*++
  817. Routine Description:
  818. This routine will be to check on the modem status, and
  819. handle any appropriate event notification as well as
  820. any flow control appropriate to modem status lines.
  821. NOTE: This routine assumes that it is called at interrupt
  822. level.
  823. Arguments:
  824. Extension - A pointer to the serial device extension.
  825. DoingTX - This boolean is used to indicate that this call
  826. came from the transmit processing code. If this
  827. is true then there is no need to cause a new interrupt
  828. since the code will be trying to send the next
  829. character as soon as this call finishes.
  830. Return Value:
  831. This returns the old value of the modem status register
  832. (extended into a ULONG).
  833. --*/
  834. {
  835. //
  836. // We keep this local so that after we are done
  837. // examining the modem status and we've updated
  838. // the transmission holding value, we know whether
  839. // we've changed from needing to hold up transmission
  840. // to transmission being able to proceed.
  841. //
  842. ULONG OldTXHolding = Extension->TXHolding;
  843. //
  844. // Holds the value in the mode status register.
  845. //
  846. UCHAR ModemStatus;
  847. ModemStatus =
  848. READ_MODEM_STATUS(Extension->Controller);
  849. //
  850. // If we are placeing the modem status into the data stream
  851. // on every change, we should do it now.
  852. //
  853. if (Extension->EscapeChar) {
  854. if (ModemStatus & (SERIAL_MSR_DCTS |
  855. SERIAL_MSR_DDSR |
  856. SERIAL_MSR_TERI |
  857. SERIAL_MSR_DDCD)) {
  858. SerialPutChar(
  859. Extension,
  860. Extension->EscapeChar
  861. );
  862. SerialPutChar(
  863. Extension,
  864. SERIAL_LSRMST_MST
  865. );
  866. SerialPutChar(
  867. Extension,
  868. ModemStatus
  869. );
  870. }
  871. }
  872. //
  873. // Take care of input flow control based on sensitivity
  874. // to the DSR. This is done so that the application won't
  875. // see spurious data generated by odd devices.
  876. //
  877. // Basically, if we are doing dsr sensitivity then the
  878. // driver should only accept data when the dsr bit is
  879. // set.
  880. //
  881. if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
  882. if (ModemStatus & SERIAL_MSR_DSR) {
  883. //
  884. // The line is high. Simply make sure that
  885. // RXHolding does't have the DSR bit.
  886. //
  887. Extension->RXHolding &= ~SERIAL_RX_DSR;
  888. } else {
  889. Extension->RXHolding |= SERIAL_RX_DSR;
  890. }
  891. } else {
  892. //
  893. // We don't have sensitivity due to DSR. Make sure we
  894. // arn't holding. (We might have been, but the app just
  895. // asked that we don't hold for this reason any more.)
  896. //
  897. Extension->RXHolding &= ~SERIAL_RX_DSR;
  898. }
  899. //
  900. // Check to see if we have a wait
  901. // pending on the modem status events. If we
  902. // do then we schedule a dpc to satisfy
  903. // that wait.
  904. //
  905. if (Extension->IsrWaitMask) {
  906. if ((Extension->IsrWaitMask & SERIAL_EV_CTS) &&
  907. (ModemStatus & SERIAL_MSR_DCTS)) {
  908. Extension->HistoryMask |= SERIAL_EV_CTS;
  909. }
  910. if ((Extension->IsrWaitMask & SERIAL_EV_DSR) &&
  911. (ModemStatus & SERIAL_MSR_DDSR)) {
  912. Extension->HistoryMask |= SERIAL_EV_DSR;
  913. }
  914. if ((Extension->IsrWaitMask & SERIAL_EV_RING) &&
  915. (ModemStatus & SERIAL_MSR_TERI)) {
  916. Extension->HistoryMask |= SERIAL_EV_RING;
  917. }
  918. if ((Extension->IsrWaitMask & SERIAL_EV_RLSD) &&
  919. (ModemStatus & SERIAL_MSR_DDCD)) {
  920. Extension->HistoryMask |= SERIAL_EV_RLSD;
  921. }
  922. if (Extension->IrpMaskLocation &&
  923. Extension->HistoryMask) {
  924. *Extension->IrpMaskLocation =
  925. Extension->HistoryMask;
  926. Extension->IrpMaskLocation = NULL;
  927. Extension->HistoryMask = 0;
  928. Extension->CurrentWaitIrp->
  929. IoStatus.Information = sizeof(ULONG);
  930. SerialInsertQueueDpc(
  931. &Extension->CommWaitDpc,
  932. NULL,
  933. NULL,
  934. Extension
  935. );
  936. }
  937. }
  938. //
  939. // If the app has modem line flow control then
  940. // we check to see if we have to hold up transmission.
  941. //
  942. if (Extension->HandFlow.ControlHandShake &
  943. SERIAL_OUT_HANDSHAKEMASK) {
  944. if (Extension->HandFlow.ControlHandShake &
  945. SERIAL_CTS_HANDSHAKE) {
  946. if (ModemStatus & SERIAL_MSR_CTS) {
  947. Extension->TXHolding &= ~SERIAL_TX_CTS;
  948. } else {
  949. Extension->TXHolding |= SERIAL_TX_CTS;
  950. }
  951. } else {
  952. Extension->TXHolding &= ~SERIAL_TX_CTS;
  953. }
  954. if (Extension->HandFlow.ControlHandShake &
  955. SERIAL_DSR_HANDSHAKE) {
  956. if (ModemStatus & SERIAL_MSR_DSR) {
  957. Extension->TXHolding &= ~SERIAL_TX_DSR;
  958. } else {
  959. Extension->TXHolding |= SERIAL_TX_DSR;
  960. }
  961. } else {
  962. Extension->TXHolding &= ~SERIAL_TX_DSR;
  963. }
  964. if (Extension->HandFlow.ControlHandShake &
  965. SERIAL_DCD_HANDSHAKE) {
  966. if (ModemStatus & SERIAL_MSR_DCD) {
  967. Extension->TXHolding &= ~SERIAL_TX_DCD;
  968. } else {
  969. Extension->TXHolding |= SERIAL_TX_DCD;
  970. }
  971. } else {
  972. Extension->TXHolding &= ~SERIAL_TX_DCD;
  973. }
  974. //
  975. // If we hadn't been holding, and now we are then
  976. // queue off a dpc that will lower the RTS line
  977. // if we are doing transmit toggling.
  978. //
  979. if (!OldTXHolding && Extension->TXHolding &&
  980. ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
  981. SERIAL_TRANSMIT_TOGGLE)) {
  982. SerialInsertQueueDpc(
  983. &Extension->StartTimerLowerRTSDpc,
  984. NULL,
  985. NULL,
  986. Extension
  987. )?Extension->CountOfTryingToLowerRTS++:0;
  988. }
  989. //
  990. // We've done any adjusting that needed to be
  991. // done to the holding mask given updates
  992. // to the modem status. If the Holding mask
  993. // is clear (and it wasn't clear to start)
  994. // and we have "write" work to do set things
  995. // up so that the transmission code gets invoked.
  996. //
  997. if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
  998. if (!Extension->TXHolding &&
  999. (Extension->TransmitImmediate ||
  1000. Extension->WriteLength) &&
  1001. Extension->HoldingEmpty) {
  1002. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  1003. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  1004. }
  1005. }
  1006. } else {
  1007. //
  1008. // We need to check if transmission is holding
  1009. // up because of modem status lines. What
  1010. // could have occured is that for some strange
  1011. // reason, the app has asked that we no longer
  1012. // stop doing output flow control based on
  1013. // the modem status lines. If however, we
  1014. // *had* been held up because of the status lines
  1015. // then we need to clear up those reasons.
  1016. //
  1017. if (Extension->TXHolding & (SERIAL_TX_DCD |
  1018. SERIAL_TX_DSR |
  1019. SERIAL_TX_CTS)) {
  1020. Extension->TXHolding &= ~(SERIAL_TX_DCD |
  1021. SERIAL_TX_DSR |
  1022. SERIAL_TX_CTS);
  1023. if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
  1024. if (!Extension->TXHolding &&
  1025. (Extension->TransmitImmediate ||
  1026. Extension->WriteLength) &&
  1027. Extension->HoldingEmpty) {
  1028. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  1029. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. return ((ULONG)ModemStatus);
  1035. }
  1036. BOOLEAN
  1037. SerialPerhapsLowerRTS(
  1038. IN PVOID Context
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This routine checks that the software reasons for lowering
  1043. the RTS lines are present. If so, it will then cause the
  1044. line status register to be read (and any needed processing
  1045. implied by the status register to be done), and if the
  1046. shift register is empty it will lower the line. If the
  1047. shift register isn't empty, this routine will queue off
  1048. a dpc that will start a timer, that will basically call
  1049. us back to try again.
  1050. NOTE: This routine assumes that it is called at interrupt
  1051. level.
  1052. Arguments:
  1053. Context - Really a pointer to the device extension.
  1054. Return Value:
  1055. Always FALSE.
  1056. --*/
  1057. {
  1058. PSERIAL_DEVICE_EXTENSION Extension = Context;
  1059. //
  1060. // We first need to test if we are actually still doing
  1061. // transmit toggle flow control. If we aren't then
  1062. // we have no reason to try be here.
  1063. //
  1064. if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
  1065. SERIAL_TRANSMIT_TOGGLE) {
  1066. //
  1067. // The order of the tests is very important below.
  1068. //
  1069. // If there is a break then we should leave on the RTS,
  1070. // because when the break is turned off, it will submit
  1071. // the code to shut down the RTS.
  1072. //
  1073. // If there are writes pending that aren't being held
  1074. // up, then leave on the RTS, because the end of the write
  1075. // code will cause this code to be reinvoked. If the writes
  1076. // are being held up, its ok to lower the RTS because the
  1077. // upon trying to write the first character after transmission
  1078. // is restarted, we will raise the RTS line.
  1079. //
  1080. if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
  1081. (Extension->CurrentWriteIrp || Extension->TransmitImmediate ||
  1082. (!IsListEmpty(&Extension->WriteQueue)) &&
  1083. (!Extension->TXHolding))) {
  1084. NOTHING;
  1085. } else {
  1086. //
  1087. // Looks good so far. Call the line status check and processing
  1088. // code, it will return the "current" line status value. If
  1089. // the holding and shift register are clear, lower the RTS line,
  1090. // if they aren't clear, queue of a dpc that will cause a timer
  1091. // to reinvoke us later. We do this code here because no one
  1092. // but this routine cares about the characters in the hardware,
  1093. // so no routine by this routine will bother invoking to test
  1094. // if the hardware is empty.
  1095. //
  1096. if ((SerialProcessLSR(Extension) &
  1097. (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
  1098. (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
  1099. //
  1100. // Well it's not empty, try again later.
  1101. //
  1102. SerialInsertQueueDpc(
  1103. &Extension->StartTimerLowerRTSDpc,
  1104. NULL,
  1105. NULL,
  1106. Extension
  1107. )?Extension->CountOfTryingToLowerRTS++:0;
  1108. } else {
  1109. //
  1110. // Nothing in the hardware, Lower the RTS.
  1111. //
  1112. SerialClrRTS(Extension);
  1113. }
  1114. }
  1115. }
  1116. //
  1117. // We decement the counter to indicate that we've reached
  1118. // the end of the execution path that is trying to push
  1119. // down the RTS line.
  1120. //
  1121. Extension->CountOfTryingToLowerRTS--;
  1122. return FALSE;
  1123. }
  1124. VOID
  1125. SerialStartTimerLowerRTS(
  1126. IN PKDPC Dpc,
  1127. IN PVOID DeferredContext,
  1128. IN PVOID SystemContext1,
  1129. IN PVOID SystemContext2
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. This routine starts a timer that when it expires will start
  1134. a dpc that will check if it can lower the rts line because
  1135. there are no characters in the hardware.
  1136. Arguments:
  1137. Dpc - Not Used.
  1138. DeferredContext - Really points to the device extension.
  1139. SystemContext1 - Not Used.
  1140. SystemContext2 - Not Used.
  1141. Return Value:
  1142. None.
  1143. --*/
  1144. {
  1145. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  1146. LARGE_INTEGER CharTime;
  1147. KIRQL OldIrql;
  1148. UNREFERENCED_PARAMETER(Dpc);
  1149. UNREFERENCED_PARAMETER(SystemContext1);
  1150. UNREFERENCED_PARAMETER(SystemContext2);
  1151. SerialDump(SERTRACECALLS, ("SERIAL: SerialStartTimerLowerRTS\n"));
  1152. //
  1153. // Take out the lock to prevent the line control
  1154. // from changing out from under us while we calculate
  1155. // a character time.
  1156. //
  1157. KeAcquireSpinLock(
  1158. &Extension->ControlLock,
  1159. &OldIrql
  1160. );
  1161. CharTime = SerialGetCharTime(Extension);
  1162. KeReleaseSpinLock(
  1163. &Extension->ControlLock,
  1164. OldIrql
  1165. );
  1166. CharTime.QuadPart = -CharTime.QuadPart;
  1167. if (SerialSetTimer(
  1168. &Extension->LowerRTSTimer,
  1169. CharTime,
  1170. &Extension->PerhapsLowerRTSDpc,
  1171. Extension
  1172. )) {
  1173. //
  1174. // The timer was already in the timer queue. This implies
  1175. // that one path of execution that was trying to lower
  1176. // the RTS has "died". Synchronize with the ISR so that
  1177. // we can lower the count.
  1178. //
  1179. KeSynchronizeExecution(
  1180. Extension->Interrupt,
  1181. SerialDecrementRTSCounter,
  1182. Extension
  1183. );
  1184. }
  1185. SerialDpcEpilogue(Extension, Dpc);
  1186. }
  1187. VOID
  1188. SerialInvokePerhapsLowerRTS(
  1189. IN PKDPC Dpc,
  1190. IN PVOID DeferredContext,
  1191. IN PVOID SystemContext1,
  1192. IN PVOID SystemContext2
  1193. )
  1194. /*++
  1195. Routine Description:
  1196. This dpc routine exists solely to call the code that
  1197. tests if the rts line should be lowered when TRANSMIT
  1198. TOGGLE flow control is being used.
  1199. Arguments:
  1200. Dpc - Not Used.
  1201. DeferredContext - Really points to the device extension.
  1202. SystemContext1 - Not Used.
  1203. SystemContext2 - Not Used.
  1204. Return Value:
  1205. None.
  1206. --*/
  1207. {
  1208. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  1209. UNREFERENCED_PARAMETER(Dpc);
  1210. UNREFERENCED_PARAMETER(SystemContext1);
  1211. UNREFERENCED_PARAMETER(SystemContext2);
  1212. KeSynchronizeExecution(
  1213. Extension->Interrupt,
  1214. SerialPerhapsLowerRTS,
  1215. Extension
  1216. );
  1217. SerialDpcEpilogue(Extension, Dpc);
  1218. }
  1219. BOOLEAN
  1220. SerialDecrementRTSCounter(
  1221. IN PVOID Context
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. This routine checks that the software reasons for lowering
  1226. the RTS lines are present. If so, it will then cause the
  1227. line status register to be read (and any needed processing
  1228. implied by the status register to be done), and if the
  1229. shift register is empty it will lower the line. If the
  1230. shift register isn't empty, this routine will queue off
  1231. a dpc that will start a timer, that will basically call
  1232. us back to try again.
  1233. NOTE: This routine assumes that it is called at interrupt
  1234. level.
  1235. Arguments:
  1236. Context - Really a pointer to the device extension.
  1237. Return Value:
  1238. Always FALSE.
  1239. --*/
  1240. {
  1241. PSERIAL_DEVICE_EXTENSION Extension = Context;
  1242. Extension->CountOfTryingToLowerRTS--;
  1243. return FALSE;
  1244. }