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.

911 lines
25 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. // Prototypes
  20. BOOLEAN SerialDecrementRTSCounter(IN PVOID Context);
  21. // End of prototypes.
  22. #ifdef ALLOC_PRAGMA
  23. #endif
  24. BOOLEAN
  25. SerialSetDTR(IN PVOID Context)
  26. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  27. Routine Description:
  28. This routine which is only called at interrupt level is used
  29. to set the DTR in the modem control register.
  30. Arguments:
  31. Context - Really a pointer to the device extension.
  32. Return Value:
  33. This routine always returns FALSE.
  34. -----------------------------------------------------------------------------*/
  35. {
  36. PPORT_DEVICE_EXTENSION pPort = Context;
  37. DWORD ModemSignals = UL_MC_DTR;
  38. SpxDbgMsg(SERFLOW, ("%s: Setting DTR for port %d\n", PRODUCT_NAME, pPort->PortNumber));
  39. pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_SET);
  40. pPort->DTR_Set = TRUE;
  41. return FALSE;
  42. }
  43. BOOLEAN
  44. SerialClrDTR(IN PVOID Context)
  45. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  46. Routine Description:
  47. This routine which is only called at interrupt level is used
  48. to clear the DTR in the modem control register.
  49. Arguments:
  50. Context - Really a pointer to the device extension.
  51. Return Value:
  52. This routine always returns FALSE.
  53. -----------------------------------------------------------------------------*/
  54. {
  55. PPORT_DEVICE_EXTENSION pPort = Context;
  56. DWORD ModemSignals = UL_MC_DTR;
  57. SpxDbgMsg(SERFLOW, ("%s: Clearing DTR for port %d\n", PRODUCT_NAME, pPort->PortNumber));
  58. pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_CLEAR);
  59. pPort->DTR_Set = FALSE;
  60. return FALSE;
  61. }
  62. BOOLEAN
  63. SerialSetRTS(IN PVOID Context)
  64. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  65. Routine Description:
  66. This routine which is only called at interrupt level is used
  67. to set the RTS in the modem control register.
  68. Arguments:
  69. Context - Really a pointer to the device extension.
  70. Return Value:
  71. This routine always returns FALSE.
  72. -----------------------------------------------------------------------------*/
  73. {
  74. PPORT_DEVICE_EXTENSION pPort = Context;
  75. DWORD ModemSignals = UL_MC_RTS;
  76. SpxDbgMsg(SERFLOW, ("%s: Setting RTS for port %d\n", PRODUCT_NAME, pPort->PortNumber));
  77. pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_SET);
  78. pPort->RTS_Set = TRUE;
  79. return FALSE;
  80. }
  81. BOOLEAN
  82. SerialClrRTS(IN PVOID Context)
  83. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  84. Routine Description:
  85. This routine which is only called at interrupt level is used
  86. to clear the RTS in the modem control register.
  87. Arguments:
  88. Context - Really a pointer to the device extension.
  89. Return Value:
  90. This routine always returns FALSE.
  91. -----------------------------------------------------------------------------*/
  92. {
  93. PPORT_DEVICE_EXTENSION pPort = Context;
  94. DWORD ModemSignals = UL_MC_RTS;
  95. SpxDbgMsg(SERFLOW, ("%s: Clearing RTS for port %d\n", PRODUCT_NAME, pPort->PortNumber));
  96. pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_BIT_CLEAR);
  97. pPort->RTS_Set = FALSE;
  98. return FALSE;
  99. }
  100. BOOLEAN
  101. SerialSetupNewHandFlow(IN PPORT_DEVICE_EXTENSION pPort, IN PSERIAL_HANDFLOW NewHandFlow)
  102. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  103. Routine Description:
  104. This routine adjusts the flow control based on new control flow.
  105. Arguments:
  106. Extension - A pointer to the serial device extension.
  107. NewHandFlow - A pointer to a serial handflow structure
  108. that is to become the new setup for flow
  109. control.
  110. Return Value:
  111. This routine always returns FALSE.
  112. -----------------------------------------------------------------------------*/
  113. {
  114. SERIAL_HANDFLOW New = *NewHandFlow;
  115. // If the Extension->DeviceIsOpen is FALSE that means
  116. // we are entering this routine in response to an open request.
  117. // If that is so, then we always proceed with the work regardless
  118. // of whether things have changed.
  119. if((!pPort->DeviceIsOpen)
  120. || (pPort->HandFlow.ControlHandShake != New.ControlHandShake)
  121. || (pPort->HandFlow.FlowReplace != New.FlowReplace))
  122. {
  123. // First we take care of the DTR flow control. We only do work if something has changed.
  124. SerialDump(SERFLOW, ("Processing DTR flow for %x\n", pPort->Controller));
  125. switch(New.ControlHandShake & SERIAL_DTR_MASK)
  126. {
  127. case SERIAL_DTR_HANDSHAKE:
  128. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_DTR_HS;
  129. break;
  130. case SERIAL_DTR_CONTROL:
  131. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_NO_DTR_FLOW;
  132. SerialSetDTR(pPort);
  133. break;
  134. default:
  135. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DTR_FLOW_MASK) | UC_FLWC_NO_DTR_FLOW;
  136. SerialClrDTR(pPort);
  137. break;
  138. }
  139. // Time to take care of the RTS Flow control.
  140. SerialDump(SERFLOW,("Processing RTS flow for %x\n", pPort->Controller));
  141. switch(New.FlowReplace & SERIAL_RTS_MASK)
  142. {
  143. case SERIAL_RTS_HANDSHAKE:
  144. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_RTS_HS;
  145. break;
  146. case SERIAL_RTS_CONTROL:
  147. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_NO_RTS_FLOW;
  148. SerialSetRTS(pPort);
  149. break;
  150. case SERIAL_TRANSMIT_TOGGLE:
  151. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_RTS_TOGGLE;
  152. break;
  153. default:
  154. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RTS_FLOW_MASK) | UC_FLWC_NO_RTS_FLOW;
  155. SerialClrRTS(pPort);
  156. break;
  157. }
  158. if(New.ControlHandShake & SERIAL_CTS_HANDSHAKE)
  159. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_CTS_FLOW_MASK) | UC_FLWC_CTS_HS;
  160. else
  161. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_CTS_FLOW_MASK) | UC_FLWC_NO_CTS_FLOW;
  162. if(New.ControlHandShake & SERIAL_DSR_HANDSHAKE)
  163. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DSR_FLOW_MASK) | UC_FLWC_DSR_HS;
  164. else
  165. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_DSR_FLOW_MASK) | UC_FLWC_NO_DSR_FLOW;
  166. //if(New.ControlHandShake & SERIAL_DCD_HANDSHAKE)
  167. if(New.FlowReplace & SERIAL_NULL_STRIPPING)
  168. pPort->UartConfig.SpecialMode |= UC_SM_DO_NULL_STRIPPING;
  169. else
  170. pPort->UartConfig.SpecialMode &= ~UC_SM_DO_NULL_STRIPPING;
  171. //
  172. // We now take care of automatic receive flow control.
  173. //
  174. if(New.FlowReplace & SERIAL_AUTO_RECEIVE)
  175. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RX_XON_XOFF_FLOW_MASK) | UC_FLWC_RX_XON_XOFF_FLOW;
  176. else
  177. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_RX_XON_XOFF_FLOW_MASK) | UC_FLWC_RX_NO_XON_XOFF_FLOW;
  178. //
  179. // We now take care of automatic transmit flow control.
  180. //
  181. if(New.FlowReplace & SERIAL_AUTO_TRANSMIT)
  182. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_TX_XON_XOFF_FLOW_MASK) | UC_FLWC_TX_XON_XOFF_FLOW;
  183. else
  184. pPort->UartConfig.FlowControl = (pPort->UartConfig.FlowControl & ~UC_FLWC_TX_XON_XOFF_FLOW_MASK) | UC_FLWC_TX_NO_XON_XOFF_FLOW;
  185. pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_FLOW_CTRL_MASK | UC_SPECIAL_MODE_MASK);
  186. }
  187. //
  188. // At this point we can simply make sure that entire
  189. // handflow structure in the extension is updated.
  190. //
  191. pPort->HandFlow = New;
  192. return FALSE;
  193. }
  194. BOOLEAN
  195. SerialSetHandFlow(IN PVOID Context)
  196. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  197. Routine Description:
  198. This routine is used to set the handshake and control
  199. flow in the device extension.
  200. Arguments:
  201. Context - Pointer to a structure that contains a pointer to
  202. the device extension and a pointer to a handflow
  203. structure..
  204. Return Value:
  205. This routine always returns FALSE.
  206. -----------------------------------------------------------------------------*/
  207. {
  208. PSERIAL_IOCTL_SYNC S = Context;
  209. PPORT_DEVICE_EXTENSION pPort = S->pPort;
  210. PSERIAL_HANDFLOW HandFlow = S->Data;
  211. SerialSetupNewHandFlow(pPort, HandFlow);
  212. SerialHandleModemUpdate(pPort, FALSE);
  213. return FALSE;
  214. }
  215. BOOLEAN
  216. SerialTurnOnBreak(IN PVOID Context)
  217. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  218. Routine Description:
  219. This routine will turn on break in the hardware and
  220. record the fact the break is on, in the extension variable
  221. that holds reasons that transmission is stopped.
  222. Arguments:
  223. Context - Really a pointer to the device extension.
  224. Return Value:
  225. This routine always returns FALSE.
  226. -----------------------------------------------------------------------------*/
  227. {
  228. PPORT_DEVICE_EXTENSION pPort = Context;
  229. if((pPort->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)
  230. SerialSetRTS(pPort);
  231. // Set break.
  232. pPort->UartConfig.SpecialMode |= UC_SM_TX_BREAK;
  233. pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_SPECIAL_MODE_MASK);
  234. return FALSE;
  235. }
  236. BOOLEAN
  237. SerialTurnOffBreak(IN PVOID Context)
  238. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  239. Routine Description:
  240. This routine will turn off break in the hardware and
  241. record the fact the break is off, in the extension variable
  242. that holds reasons that transmission is stopped.
  243. Arguments:
  244. Context - Really a pointer to the device extension.
  245. Return Value:
  246. This routine always returns FALSE.
  247. -----------------------------------------------------------------------------*/
  248. {
  249. PPORT_DEVICE_EXTENSION pPort = Context;
  250. // Clear break.
  251. pPort->UartConfig.SpecialMode &= ~UC_SM_TX_BREAK;
  252. pPort->pUartLib->UL_SetConfig_XXXX(pPort->pUart, &pPort->UartConfig, UC_SPECIAL_MODE_MASK);
  253. return FALSE;
  254. }
  255. BOOLEAN
  256. SerialPretendXoff(IN PVOID Context)
  257. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  258. Routine Description:
  259. This routine is used to process the Ioctl that request the
  260. driver to act as if an Xoff was received. Even if the
  261. driver does not have automatic Xoff/Xon flowcontrol - This
  262. will still stop the transmission. This is the OS/2 behavior
  263. and is not well specified for Windows. Therefore we adopt
  264. the OS/2 behavior.
  265. Note: If the driver does not have automatic Xoff/Xon enabled
  266. then the only way to restart transmission is for the
  267. application to request we "act" as if we saw the xon.
  268. Arguments:
  269. Context - Really a pointer to the device extension.
  270. Return Value:
  271. This routine always returns FALSE.
  272. -----------------------------------------------------------------------------*/
  273. {
  274. PPORT_DEVICE_EXTENSION pPort = Context;
  275. pPort->TXHolding |= SERIAL_TX_XOFF;
  276. if((pPort->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)
  277. {
  278. KeInsertQueueDpc(&pPort->StartTimerLowerRTSDpc, NULL, NULL) ? pPort->CountOfTryingToLowerRTS++ : 0;
  279. }
  280. return FALSE;
  281. }
  282. BOOLEAN
  283. SerialPretendXon(IN PVOID Context)
  284. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  285. Routine Description:
  286. This routine is used to process the Ioctl that request the
  287. driver to act as if an Xon was received.
  288. Note: If the driver does not have automatic Xoff/Xon enabled
  289. then the only way to restart transmission is for the
  290. application to request we "act" as if we saw the xon.
  291. Arguments:
  292. Context - Really a pointer to the device extension.
  293. Return Value:
  294. This routine always returns FALSE.
  295. -----------------------------------------------------------------------------*/
  296. {
  297. PPORT_DEVICE_EXTENSION pPort = Context;
  298. if(pPort->TXHolding)
  299. {
  300. // We actually have a good reason for testing if transmission
  301. // is holding instead of blindly clearing the bit.
  302. //
  303. // If transmission actually was holding and the result of
  304. // clearing the bit is that we should restart transmission
  305. // then we will poke the interrupt enable bit, which will
  306. // cause an actual interrupt and transmission will then
  307. // restart on its own.
  308. //
  309. // If transmission wasn't holding and we poked the bit
  310. // then we would interrupt before a character actually made
  311. // it out and we could end up over writing a character in
  312. // the transmission hardware.
  313. pPort->TXHolding &= ~SERIAL_TX_XOFF;
  314. }
  315. return FALSE;
  316. }
  317. VOID
  318. SerialHandleReducedIntBuffer(IN PPORT_DEVICE_EXTENSION pPort)
  319. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  320. Routine Description:
  321. This routine is called to handle a reduction in the number
  322. of characters in the interrupt (typeahead) buffer. It
  323. will check the current output flow control and re-enable transmission
  324. as needed.
  325. NOTE: This routine assumes that it is working at interrupt level.
  326. Arguments:
  327. Extension - A pointer to the device extension.
  328. Return Value:
  329. None.
  330. -----------------------------------------------------------------------------*/
  331. {
  332. //
  333. // If we are doing receive side flow control and we are
  334. // currently "holding" then because we've emptied out
  335. // some characters from the interrupt buffer we need to
  336. // see if we can "re-enable" reception.
  337. //
  338. if(pPort->RXHolding)
  339. {
  340. if(pPort->CharsInInterruptBuffer <= (ULONG)pPort->HandFlow.XonLimit)
  341. {
  342. if(pPort->RXHolding & SERIAL_RX_DTR)
  343. {
  344. pPort->RXHolding &= ~SERIAL_RX_DTR;
  345. SerialSetDTR(pPort);
  346. }
  347. if(pPort->RXHolding & SERIAL_RX_RTS)
  348. {
  349. pPort->RXHolding &= ~SERIAL_RX_RTS;
  350. SerialSetRTS(pPort);
  351. }
  352. if(pPort->RXHolding & SERIAL_RX_XOFF)
  353. {
  354. // Prod the transmit code to send xon.
  355. SerialProdXonXoff(pPort, TRUE);
  356. }
  357. }
  358. }
  359. }
  360. VOID
  361. SerialProdXonXoff(IN PPORT_DEVICE_EXTENSION pPort, IN BOOLEAN SendXon)
  362. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  363. Routine Description:
  364. This routine will set up the SendXxxxChar variables if
  365. necessary and determine if we are going to be interrupting
  366. because of current transmission state. It will cause an
  367. interrupt to occur if neccessary, to send the xon/xoff char.
  368. NOTE: This routine assumes that it is called at interrupt
  369. level.
  370. Arguments:
  371. Extension - A pointer to the serial device extension.
  372. SendXon - If a character is to be send, this indicates whether
  373. it should be an Xon or an Xoff.
  374. Return Value:
  375. None.
  376. -----------------------------------------------------------------------------*/
  377. {
  378. //
  379. // We assume that if the prodding is called more than
  380. // once that the last prod has set things up appropriately.
  381. //
  382. // We could get called before the character is sent out
  383. // because the send of the character was blocked because
  384. // of hardware flow control (or break).
  385. //
  386. if(SendXon)
  387. {
  388. pPort->SendXonChar = TRUE;
  389. pPort->SendXoffChar = FALSE;
  390. }
  391. else
  392. {
  393. pPort->SendXonChar = FALSE;
  394. pPort->SendXoffChar = TRUE;
  395. }
  396. }
  397. ULONG
  398. SerialHandleModemUpdate(IN PPORT_DEVICE_EXTENSION pPort, IN BOOLEAN DoingTX)
  399. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  400. Routine Description:
  401. This routine will be to check on the modem status, and
  402. handle any appropriate event notification as well as
  403. any flow control appropriate to modem status lines.
  404. NOTE: This routine assumes that it is called at interrupt
  405. level.
  406. Arguments:
  407. Extension - A pointer to the serial device extension.
  408. DoingTX - This boolean is used to indicate that this call
  409. came from the transmit processing code. If this
  410. is true then there is no need to cause a new interrupt
  411. since the code will be trying to send the next
  412. character as soon as this call finishes.
  413. Return Value:
  414. This returns the old value of the modem status register
  415. (extended into a ULONG).
  416. -----------------------------------------------------------------------------*/
  417. {
  418. // We keep this local so that after we are done
  419. // examining the modem status and we've updated
  420. // the transmission holding value, we know whether
  421. // we've changed from needing to hold up transmission
  422. // to transmission being able to proceed.
  423. ULONG OldTXHolding = pPort->TXHolding;
  424. // Holds the value in the mode status register.
  425. UCHAR ModemStatus = 0;
  426. DWORD ModemSignals = 0;
  427. pPort->pUartLib->UL_ModemControl_XXXX(pPort->pUart, &ModemSignals, UL_MC_OP_STATUS);
  428. // Put data in 16x5x format.
  429. if(ModemSignals & UL_MC_DELTA_CTS)
  430. ModemStatus |= SERIAL_MSR_DCTS;
  431. if(ModemSignals & UL_MC_DELTA_DSR)
  432. ModemStatus |= SERIAL_MSR_DDSR;
  433. if(ModemSignals & UL_MC_TRAILING_RI_EDGE)
  434. ModemStatus |= SERIAL_MSR_TERI;
  435. if(ModemSignals & UL_MC_DELTA_DCD)
  436. ModemStatus |= SERIAL_MSR_DDCD;
  437. if(ModemSignals & UL_MC_CTS)
  438. ModemStatus |= SERIAL_MSR_CTS;
  439. if(ModemSignals & UL_MC_DSR)
  440. ModemStatus |= SERIAL_MSR_DSR;
  441. if(ModemSignals & UL_MC_RI)
  442. ModemStatus |= SERIAL_MSR_RI;
  443. if(ModemSignals & UL_MC_DCD)
  444. ModemStatus |= SERIAL_MSR_DCD;
  445. // If we are placing the modem status into the data stream
  446. // on every change, we should do it now.
  447. if(pPort->EscapeChar)
  448. {
  449. // If a signal changed...
  450. if(ModemStatus & (SERIAL_MSR_DCTS | SERIAL_MSR_DDSR | SERIAL_MSR_TERI | SERIAL_MSR_DDCD))
  451. {
  452. BYTE TmpByte;
  453. TmpByte = pPort->EscapeChar;
  454. pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
  455. TmpByte = SERIAL_LSRMST_MST;
  456. pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
  457. TmpByte = ModemStatus;
  458. pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE);
  459. }
  460. }
  461. // Check to see if we have a wait pending on the modem status events. If we
  462. // do then we schedule a dpc to satisfy that wait.
  463. if(pPort->IsrWaitMask)
  464. {
  465. if((pPort->IsrWaitMask & SERIAL_EV_CTS) && (ModemStatus & SERIAL_MSR_DCTS))
  466. pPort->HistoryMask |= SERIAL_EV_CTS;
  467. if((pPort->IsrWaitMask & SERIAL_EV_DSR) && (ModemStatus & SERIAL_MSR_DDSR))
  468. pPort->HistoryMask |= SERIAL_EV_DSR;
  469. if((pPort->IsrWaitMask & SERIAL_EV_RING) && (ModemStatus & SERIAL_MSR_TERI))
  470. pPort->HistoryMask |= SERIAL_EV_RING;
  471. if((pPort->IsrWaitMask & SERIAL_EV_RLSD) && (ModemStatus & SERIAL_MSR_DDCD))
  472. pPort->HistoryMask |= SERIAL_EV_RLSD;
  473. if(pPort->IrpMaskLocation && pPort->HistoryMask)
  474. {
  475. *pPort->IrpMaskLocation = pPort->HistoryMask;
  476. pPort->IrpMaskLocation = NULL;
  477. pPort->HistoryMask = 0;
  478. pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
  479. // Mark IRP as about to complete normally to prevent cancel & timer DPCs
  480. // from doing so before DPC is allowed to run.
  481. //SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING);
  482. KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL);
  483. }
  484. }
  485. return ((ULONG)ModemStatus);
  486. }
  487. BOOLEAN
  488. SerialPerhapsLowerRTS(IN PVOID Context)
  489. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  490. Routine Description:
  491. This routine checks that the software reasons for lowering
  492. the RTS lines are present. If so, it will then cause the
  493. line status register to be read (and any needed processing
  494. implied by the status register to be done), and if the
  495. shift register is empty it will lower the line. If the
  496. shift register isn't empty, this routine will queue off
  497. a dpc that will start a timer, that will basically call
  498. us back to try again.
  499. NOTE: This routine assumes that it is called at interrupt
  500. level.
  501. Arguments:
  502. Context - Really a pointer to the device extension.
  503. Return Value:
  504. Always FALSE.
  505. -----------------------------------------------------------------------------*/
  506. {
  507. PPORT_DEVICE_EXTENSION pPort = Context;
  508. return FALSE;
  509. }
  510. VOID
  511. SerialStartTimerLowerRTS(IN PKDPC Dpc,
  512. IN PVOID DeferredContext,
  513. IN PVOID SystemContext1,
  514. IN PVOID SystemContext2)
  515. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  516. Routine Description:
  517. This routine starts a timer that when it expires will start
  518. a dpc that will check if it can lower the rts line because
  519. there are no characters in the hardware.
  520. Arguments:
  521. Dpc - Not Used.
  522. DeferredContext - Really points to the device extension.
  523. SystemContext1 - Not Used.
  524. SystemContext2 - Not Used.
  525. Return Value:
  526. None.
  527. -----------------------------------------------------------------------------*/
  528. {
  529. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  530. LARGE_INTEGER CharTime;
  531. KIRQL OldIrql;
  532. UNREFERENCED_PARAMETER(Dpc);
  533. UNREFERENCED_PARAMETER(SystemContext1);
  534. UNREFERENCED_PARAMETER(SystemContext2);
  535. // Take out the lock to prevent the line control
  536. // from changing out from under us while we calculate
  537. // a character time.
  538. KeAcquireSpinLock(&pPort->ControlLock, &OldIrql);
  539. CharTime = SerialGetCharTime(pPort);
  540. KeReleaseSpinLock(&pPort->ControlLock, OldIrql);
  541. CharTime.QuadPart = -CharTime.QuadPart;
  542. if(KeSetTimer(&pPort->LowerRTSTimer, CharTime, &pPort->PerhapsLowerRTSDpc))
  543. {
  544. // The timer was already in the timer queue. This implies
  545. // that one path of execution that was trying to lower
  546. // the RTS has "died". Synchronize with the ISR so that
  547. // we can lower the count.
  548. KeSynchronizeExecution(pPort->Interrupt, SerialDecrementRTSCounter, pPort);
  549. }
  550. }
  551. VOID
  552. SerialInvokePerhapsLowerRTS(IN PKDPC Dpc,
  553. IN PVOID DeferredContext,
  554. IN PVOID SystemContext1,
  555. IN PVOID SystemContext2)
  556. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  557. Routine Description:
  558. This dpc routine exists solely to call the code that
  559. tests if the rts line should be lowered when TRANSMIT
  560. TOGGLE flow control is being used.
  561. Arguments:
  562. Dpc - Not Used.
  563. DeferredContext - Really points to the device extension.
  564. SystemContext1 - Not Used.
  565. SystemContext2 - Not Used.
  566. Return Value:
  567. None.
  568. -----------------------------------------------------------------------------*/
  569. {
  570. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  571. UNREFERENCED_PARAMETER(Dpc);
  572. UNREFERENCED_PARAMETER(SystemContext1);
  573. UNREFERENCED_PARAMETER(SystemContext2);
  574. KeSynchronizeExecution(pPort->Interrupt, SerialPerhapsLowerRTS, pPort);
  575. }
  576. BOOLEAN
  577. SerialDecrementRTSCounter(IN PVOID Context)
  578. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  579. Routine Description:
  580. This routine checks that the software reasons for lowering
  581. the RTS lines are present. If so, it will then cause the
  582. line status register to be read (and any needed processing
  583. implied by the status register to be done), and if the
  584. shift register is empty it will lower the line. If the
  585. shift register isn't empty, this routine will queue off
  586. a dpc that will start a timer, that will basically call
  587. us back to try again.
  588. NOTE: This routine assumes that it is called at interrupt
  589. level.
  590. Arguments:
  591. Context - Really a pointer to the device extension.
  592. Return Value:
  593. Always FALSE.
  594. -----------------------------------------------------------------------------*/
  595. {
  596. PPORT_DEVICE_EXTENSION pPort = Context;
  597. pPort->CountOfTryingToLowerRTS--;
  598. return FALSE;
  599. }