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.

1064 lines
30 KiB

  1. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. Copyright (c) 1991, 1992, 1993 Microsoft Corporation
  3. Module Name:
  4. write.c
  5. Abstract:
  6. This module contains the code that is very specific to write
  7. operations in the serial driver
  8. Author:
  9. Anthony V. Ercolano 26-Sep-1991
  10. Environment:
  11. Kernel mode
  12. Revision History :
  13. -----------------------------------------------------------------------------*/
  14. #include "precomp.h"
  15. //Prototypes
  16. BOOLEAN SerialGiveWriteToIsr(IN PVOID Context);
  17. VOID SerialCancelCurrentWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp);
  18. BOOLEAN SerialGrabWriteFromIsr(IN PVOID Context);
  19. BOOLEAN SerialGrabXoffFromIsr(IN PVOID Context);
  20. VOID SerialCancelCurrentXoff(PDEVICE_OBJECT DeviceObject, PIRP Irp);
  21. BOOLEAN SerialGiveXoffToIsr(IN PVOID Context);
  22. //End of prototypes.
  23. #ifdef ALLOC_PRAGMA
  24. #endif
  25. NTSTATUS
  26. SerialWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
  27. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  28. Routine Description:
  29. This is the dispatch routine for write. It validates the parameters
  30. for the write request and if all is ok then it places the request
  31. on the work queue.
  32. Arguments:
  33. DeviceObject - Pointer to the device object for this device
  34. Irp - Pointer to the IRP for the current request
  35. Return Value:
  36. If the io is zero length then it will return STATUS_SUCCESS,
  37. otherwise this routine will return STATUS_PENDING.
  38. -----------------------------------------------------------------------------*/
  39. {
  40. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  41. SerialDump(SERIRPPATH, ("Write Irp dispatch entry for: %x\n", Irp));
  42. SpxIRPCounter(pPort, Irp, IRP_SUBMITTED); // Increment counter for performance stats.
  43. if(SerialCompleteIfError(DeviceObject, Irp) != STATUS_SUCCESS)
  44. return STATUS_CANCELLED;
  45. Irp->IoStatus.Information = 0L;
  46. //
  47. // Quick check for a zero length write. If it is zero length
  48. // then we are already done!
  49. //
  50. if(IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length)
  51. {
  52. //
  53. // Well it looks like we actually have to do some
  54. // work. Put the write on the queue so that we can
  55. // process it when our previous writes are done.
  56. //
  57. return SerialStartOrQueue( pPort,
  58. Irp,
  59. &pPort->WriteQueue,
  60. &pPort->CurrentWriteIrp,
  61. SerialStartWrite);
  62. }
  63. else
  64. {
  65. Irp->IoStatus.Status = STATUS_SUCCESS;
  66. SerialDump(SERIRPPATH,("Complete Write Irp: %x\n",Irp));
  67. SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
  68. IoCompleteRequest(Irp,0);
  69. return STATUS_SUCCESS;
  70. }
  71. }
  72. NTSTATUS
  73. SerialStartWrite(IN PPORT_DEVICE_EXTENSION pPort)
  74. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  75. Routine Description:
  76. This routine is used to start off any write. It initializes
  77. the Iostatus fields of the irp. It will set up any timers
  78. that are used to control the write.
  79. Arguments:
  80. Extension - Points to the serial device extension
  81. Return Value:
  82. This routine will return STATUS_PENDING for all writes
  83. other than those that we find are cancelled.
  84. -----------------------------------------------------------------------------*/
  85. {
  86. PIRP NewIrp;
  87. KIRQL OldIrql;
  88. LARGE_INTEGER TotalTime;
  89. BOOLEAN UseATimer;
  90. SERIAL_TIMEOUTS Timeouts;
  91. BOOLEAN SetFirstStatus = FALSE;
  92. NTSTATUS FirstStatus;
  93. do
  94. {
  95. // If there is an xoff counter then complete it.
  96. IoAcquireCancelSpinLock(&OldIrql);
  97. //
  98. // We see if there is a actually an Xoff counter irp.
  99. //
  100. // If there is, we put the write irp back on the head
  101. // of the write list. We then kill the xoff counter.
  102. // The xoff counter killing code will actually make the
  103. // xoff counter back into the current write irp, and
  104. // in the course of completing the xoff (which is now
  105. // the current write) we will restart this irp.
  106. //
  107. if(pPort->CurrentXoffIrp)
  108. {
  109. if(SERIAL_REFERENCE_COUNT(pPort->CurrentXoffIrp))
  110. {
  111. //
  112. // The reference count is non-zero. This implies that
  113. // the xoff irp has not made it through the completion
  114. // path yet. We will increment the reference count
  115. // and attempt to complete it ourseleves.
  116. //
  117. SERIAL_SET_REFERENCE(pPort->CurrentXoffIrp, SERIAL_REF_XOFF_REF);
  118. //
  119. // The following call will actually release the
  120. // cancel spin lock.
  121. //
  122. SerialTryToCompleteCurrent( pPort,
  123. SerialGrabXoffFromIsr,
  124. OldIrql,
  125. STATUS_SERIAL_MORE_WRITES,
  126. &pPort->CurrentXoffIrp,
  127. NULL,
  128. NULL,
  129. &pPort->XoffCountTimer,
  130. NULL,
  131. NULL,
  132. SERIAL_REF_XOFF_REF);
  133. }
  134. else
  135. {
  136. //
  137. // The irp is well on its way to being finished.
  138. // We can let the regular completion code do the
  139. // work. Just release the spin lock.
  140. //
  141. IoReleaseCancelSpinLock(OldIrql);
  142. }
  143. }
  144. else
  145. {
  146. IoReleaseCancelSpinLock(OldIrql);
  147. }
  148. UseATimer = FALSE;
  149. //
  150. // Calculate the timeout value needed for the
  151. // request. Note that the values stored in the
  152. // timeout record are in milliseconds. Note that
  153. // if the timeout values are zero then we won't start
  154. // the timer.
  155. //
  156. KeAcquireSpinLock(&pPort->ControlLock, &OldIrql);
  157. Timeouts = pPort->Timeouts;
  158. KeReleaseSpinLock(&pPort->ControlLock, OldIrql);
  159. if(Timeouts.WriteTotalTimeoutConstant || Timeouts.WriteTotalTimeoutMultiplier)
  160. {
  161. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp);
  162. UseATimer = TRUE;
  163. //
  164. // We have some timer values to calculate.
  165. //
  166. // Take care, we might have an xoff counter masquerading
  167. // as a write.
  168. //
  169. TotalTime.QuadPart = ((LONGLONG)((UInt32x32To64((IrpSp->MajorFunction == IRP_MJ_WRITE)
  170. ? (IrpSp->Parameters.Write.Length) : (1),
  171. Timeouts.WriteTotalTimeoutMultiplier) + Timeouts.WriteTotalTimeoutConstant)))
  172. * -10000;
  173. }
  174. //
  175. // The irp may be going to the isr shortly. Now
  176. // is a good time to initialize its reference counts.
  177. //
  178. SERIAL_INIT_REFERENCE(pPort->CurrentWriteIrp);
  179. //
  180. // We need to see if this irp should be canceled.
  181. //
  182. IoAcquireCancelSpinLock(&OldIrql);
  183. if(pPort->CurrentWriteIrp->Cancel)
  184. {
  185. IoReleaseCancelSpinLock(OldIrql);
  186. pPort->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
  187. if(!SetFirstStatus)
  188. {
  189. FirstStatus = STATUS_CANCELLED;
  190. SetFirstStatus = TRUE;
  191. }
  192. }
  193. else
  194. {
  195. if(!SetFirstStatus)
  196. {
  197. //
  198. // If we haven't set our first status, then
  199. // this is the only irp that could have possibly
  200. // not been on the queue. (It could have been
  201. // on the queue if this routine is being invoked
  202. // from the completion routine.) Since this
  203. // irp might never have been on the queue we
  204. // should mark it as pending.
  205. //
  206. IoMarkIrpPending(pPort->CurrentWriteIrp);
  207. SetFirstStatus = TRUE;
  208. FirstStatus = STATUS_PENDING;
  209. }
  210. //
  211. // We give the irp to to the isr to write out.
  212. // We set a cancel routine that knows how to
  213. // grab the current write away from the isr.
  214. //
  215. // Since the cancel routine has an implicit reference
  216. // to this irp up the reference count.
  217. //
  218. IoSetCancelRoutine(pPort->CurrentWriteIrp, SerialCancelCurrentWrite);
  219. SERIAL_SET_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_CANCEL);
  220. if(UseATimer)
  221. {
  222. KeSetTimer(&pPort->WriteRequestTotalTimer, TotalTime, &pPort->TotalWriteTimeoutDpc);
  223. // This timer now has a reference to the irp.
  224. SERIAL_SET_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_TOTAL_TIMER);
  225. }
  226. KeSynchronizeExecution(pPort->Interrupt, SerialGiveWriteToIsr, pPort);
  227. IoReleaseCancelSpinLock(OldIrql);
  228. break;
  229. }
  230. //
  231. // Well the write was canceled before we could start it up.
  232. // Try to get another.
  233. //
  234. SerialGetNextWrite(pPort, &pPort->CurrentWriteIrp, &pPort->WriteQueue, &NewIrp, TRUE);
  235. } while (NewIrp);
  236. return FirstStatus;
  237. }
  238. VOID
  239. SerialGetNextWrite(IN PPORT_DEVICE_EXTENSION pPort,
  240. IN PIRP *CurrentOpIrp,
  241. IN PLIST_ENTRY QueueToProcess,
  242. IN PIRP *NewIrp,
  243. IN BOOLEAN CompleteCurrent)
  244. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  245. Routine Description:
  246. This routine completes the old write as well as getting
  247. a pointer to the next write.
  248. The reason that we have have pointers to the current write
  249. queue as well as the current write irp is so that this
  250. routine may be used in the common completion code for
  251. read and write.
  252. Arguments:
  253. CurrentOpIrp - Pointer to the pointer that points to the
  254. current write irp.
  255. QueueToProcess - Pointer to the write queue.
  256. NewIrp - A pointer to a pointer to the irp that will be the
  257. current irp. Note that this could end up pointing
  258. to a null pointer. This does NOT necessaryly mean
  259. that there is no current write. What could occur
  260. is that while the cancel lock is held the write
  261. queue ended up being empty, but as soon as we release
  262. the cancel spin lock a new irp came in from
  263. SerialStartWrite.
  264. CompleteCurrent - Flag indicates whether the CurrentOpIrp should
  265. be completed.
  266. Return Value:
  267. None.
  268. -----------------------------------------------------------------------------*/
  269. {
  270. PCARD_DEVICE_EXTENSION pCard = NULL;
  271. pPort = CONTAINING_RECORD(QueueToProcess, PORT_DEVICE_EXTENSION, WriteQueue);
  272. pCard = pPort->pParentCardExt;
  273. do
  274. {
  275. // We could be completing a flush.
  276. if(IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction == IRP_MJ_WRITE)
  277. {
  278. KIRQL OldIrql;
  279. ASSERT(pPort->TotalCharsQueued
  280. >= (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->Parameters.Write.Length));
  281. IoAcquireCancelSpinLock(&OldIrql);
  282. pPort->TotalCharsQueued -= IoGetCurrentIrpStackLocation(*CurrentOpIrp)->Parameters.Write.Length;
  283. IoReleaseCancelSpinLock(OldIrql);
  284. }
  285. else if(IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction == IRP_MJ_DEVICE_CONTROL)
  286. {
  287. KIRQL OldIrql;
  288. PIRP Irp;
  289. PSERIAL_XOFF_COUNTER Xc;
  290. IoAcquireCancelSpinLock(&OldIrql);
  291. Irp = *CurrentOpIrp;
  292. Xc = Irp->AssociatedIrp.SystemBuffer;
  293. // We should never have a xoff counter when we get to this point.
  294. ASSERT(!pPort->CurrentXoffIrp);
  295. // We absolutely shouldn't have a cancel routine at this point.
  296. ASSERT(!Irp->CancelRoutine);
  297. // This could only be a xoff counter masquerading as a write irp.
  298. pPort->TotalCharsQueued--;
  299. //
  300. // Check to see of the xoff irp has been set with success.
  301. // This means that the write completed normally. If that
  302. // is the case, and it hasn't been set to cancel in the
  303. // meanwhile, then go on and make it the CurrentXoffIrp.
  304. //
  305. if(Irp->IoStatus.Status != STATUS_SUCCESS)
  306. {
  307. NOTHING; // Oh well, we can just finish it off.
  308. }
  309. else if(Irp->Cancel)
  310. {
  311. Irp->IoStatus.Status = STATUS_CANCELLED;
  312. }
  313. else
  314. {
  315. // Give it a new cancel routine, and increment the
  316. // reference count because the cancel routine has
  317. // a reference to it.
  318. IoSetCancelRoutine(Irp, SerialCancelCurrentXoff);
  319. SERIAL_SET_REFERENCE(Irp, SERIAL_REF_CANCEL);
  320. // We don't want to complete the current irp now. This
  321. // will now get completed by the Xoff counter code.
  322. CompleteCurrent = FALSE;
  323. // Give the counter to the isr.
  324. pPort->CurrentXoffIrp = Irp;
  325. KeSynchronizeExecution(pPort->Interrupt, SerialGiveXoffToIsr, pPort);
  326. //
  327. // Start the timer for the counter and increment
  328. // the reference count since the timer has a
  329. // reference to the irp.
  330. //
  331. if(Xc->Timeout)
  332. {
  333. LARGE_INTEGER delta;
  334. delta.QuadPart = -((LONGLONG)UInt32x32To64(1000, Xc->Timeout));
  335. KeSetTimer(&pPort->XoffCountTimer, delta, &pPort->XoffCountTimeoutDpc);
  336. SERIAL_SET_REFERENCE(Irp, SERIAL_REF_TOTAL_TIMER);
  337. }
  338. }
  339. IoReleaseCancelSpinLock(OldIrql);
  340. }
  341. //
  342. // Note that the following call will (probably) also cause
  343. // the current irp to be completed.
  344. //
  345. SerialGetNextIrp(pPort, CurrentOpIrp, QueueToProcess, NewIrp, CompleteCurrent);
  346. if(!*NewIrp)
  347. {
  348. KIRQL OldIrql;
  349. IoAcquireCancelSpinLock(&OldIrql);
  350. KeSynchronizeExecution(pPort->Interrupt, SerialProcessEmptyTransmit, pPort);
  351. IoReleaseCancelSpinLock(OldIrql);
  352. break;
  353. }
  354. else if(IoGetCurrentIrpStackLocation(*NewIrp)->MajorFunction == IRP_MJ_FLUSH_BUFFERS)
  355. {
  356. //
  357. // If we encounter a flush request we just want to get
  358. // the next irp and complete the flush.
  359. //
  360. // Note that if NewIrp is non-null then it is also
  361. // equal to CurrentWriteIrp.
  362. //
  363. ASSERT((*NewIrp) == (*CurrentOpIrp));
  364. (*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
  365. }
  366. else
  367. {
  368. break;
  369. }
  370. } while (TRUE);
  371. }
  372. VOID
  373. SerialCompleteWrite(IN PKDPC Dpc,
  374. IN PVOID DeferredContext,
  375. IN PVOID SystemContext1,
  376. IN PVOID SystemContext2)
  377. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  378. Routine Description:
  379. This routine is merely used to complete any write. It
  380. assumes that the status and the information fields of
  381. the irp are already correctly filled in.
  382. Arguments:
  383. Dpc - Not Used.
  384. DeferredContext - Really points to the device extension.
  385. SystemContext1 - Not Used.
  386. SystemContext2 - Not Used.
  387. Return Value:
  388. None.
  389. -----------------------------------------------------------------------------*/
  390. {
  391. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  392. KIRQL OldIrql;
  393. UNREFERENCED_PARAMETER(Dpc);
  394. UNREFERENCED_PARAMETER(SystemContext1);
  395. UNREFERENCED_PARAMETER(SystemContext2);
  396. IoAcquireCancelSpinLock(&OldIrql);
  397. // Clear the normal complete reference.
  398. SERIAL_CLEAR_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_COMPLETING);
  399. SerialTryToCompleteCurrent( pPort,
  400. NULL,
  401. OldIrql,
  402. STATUS_SUCCESS,
  403. &pPort->CurrentWriteIrp,
  404. &pPort->WriteQueue,
  405. NULL,
  406. &pPort->WriteRequestTotalTimer,
  407. SerialStartWrite,
  408. SerialGetNextWrite,
  409. SERIAL_REF_ISR);
  410. }
  411. BOOLEAN
  412. SerialProcessEmptyTransmit(IN PVOID Context)
  413. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  414. Routine Description:
  415. This routine is used to determine if conditions are appropriate
  416. to satisfy a wait for transmit empty event, and if so to complete
  417. the irp that is waiting for that event. It also call the code
  418. that checks to see if we should lower the RTS line if we are
  419. doing transmit toggling.
  420. NOTE: This routine is called by KeSynchronizeExecution.
  421. NOTE: This routine assumes that it is called with the cancel
  422. spinlock held.
  423. Arguments:
  424. Context - Really a pointer to the device extension.
  425. Return Value:
  426. This routine always returns FALSE.
  427. -----------------------------------------------------------------------------*/
  428. {
  429. PPORT_DEVICE_EXTENSION pPort = Context;
  430. if(pPort->IsrWaitMask && (pPort->IsrWaitMask & SERIAL_EV_TXEMPTY)
  431. && pPort->EmptiedTransmit && (!pPort->TransmitImmediate)
  432. && (!pPort->CurrentWriteIrp) && IsListEmpty(&pPort->WriteQueue))
  433. {
  434. pPort->HistoryMask |= SERIAL_EV_TXEMPTY;
  435. if(pPort->IrpMaskLocation)
  436. {
  437. *pPort->IrpMaskLocation = pPort->HistoryMask;
  438. pPort->IrpMaskLocation = NULL;
  439. pPort->HistoryMask = 0;
  440. pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
  441. // Mark IRP as about to complete normally to prevent cancel & timer DPCs
  442. // from doing so before DPC is allowed to run.
  443. //SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING);
  444. KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL);
  445. }
  446. pPort->CountOfTryingToLowerRTS++;
  447. SerialPerhapsLowerRTS(pPort);
  448. }
  449. return FALSE;
  450. }
  451. BOOLEAN
  452. SerialGiveWriteToIsr(IN PVOID Context)
  453. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  454. Routine Description:
  455. Try to start off the write by slipping it in behind a transmit immediate
  456. char, or if that isn't available and the transmit holding register is empty,
  457. "tickle" the UART into interrupting with a transmit buffer empty.
  458. NOTE: This routine is called by KeSynchronizeExecution.
  459. NOTE: This routine assumes that it is called with the
  460. cancel spin lock held.
  461. Arguments:
  462. Context - Really a pointer to the device extension.
  463. Return Value:
  464. This routine always returns FALSE.
  465. -----------------------------------------------------------------------------*/
  466. {
  467. PPORT_DEVICE_EXTENSION pPort = Context;
  468. // The current stack location. This contains all of the
  469. // information we need to process this particular request.
  470. PIO_STACK_LOCATION IrpSp;
  471. IrpSp = IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp);
  472. // We might have a xoff counter request masquerading as a
  473. // write. The length of these requests will always be one
  474. // and we can get a pointer to the actual character from
  475. // the data supplied by the user.
  476. if(IrpSp->MajorFunction == IRP_MJ_WRITE)
  477. {
  478. pPort->WriteLength = IrpSp->Parameters.Write.Length;
  479. pPort->WriteCurrentChar = pPort->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
  480. }
  481. else
  482. {
  483. pPort->WriteLength = 1;
  484. pPort->WriteCurrentChar = ((PUCHAR)pPort->CurrentWriteIrp->AssociatedIrp.SystemBuffer)
  485. + FIELD_OFFSET(SERIAL_XOFF_COUNTER, XoffChar);
  486. }
  487. // The isr now has a reference to the irp.
  488. SERIAL_SET_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_ISR);
  489. pPort->pUartLib->UL_WriteData_XXXX(pPort->pUart, pPort->WriteCurrentChar, pPort->WriteLength);
  490. // The rts line may already be up from previous writes,
  491. // however, it won't take much additional time to turn
  492. // on the RTS line if we are doing transmit toggling.
  493. if((pPort->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE)
  494. SerialSetRTS(pPort);
  495. return FALSE;
  496. }
  497. VOID
  498. SerialCancelCurrentWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  499. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  500. Routine Description:
  501. This routine is used to cancel the current write.
  502. Arguments:
  503. DeviceObject - Pointer to the device object for this device
  504. Irp - Pointer to the IRP to be canceled.
  505. Return Value:
  506. None.
  507. -----------------------------------------------------------------------------*/
  508. {
  509. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  510. SerialTryToCompleteCurrent( pPort,
  511. SerialGrabWriteFromIsr,
  512. Irp->CancelIrql,
  513. STATUS_CANCELLED,
  514. &pPort->CurrentWriteIrp,
  515. &pPort->WriteQueue,
  516. NULL,
  517. &pPort->WriteRequestTotalTimer,
  518. SerialStartWrite,
  519. SerialGetNextWrite,
  520. SERIAL_REF_CANCEL);
  521. }
  522. VOID
  523. SerialWriteTimeout(IN PKDPC Dpc,
  524. IN PVOID DeferredContext,
  525. IN PVOID SystemContext1,
  526. IN PVOID SystemContext2)
  527. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  528. Routine Description:
  529. This routine will try to timeout the current write.
  530. Arguments:
  531. Dpc - Not Used.
  532. DeferredContext - Really points to the device extension.
  533. SystemContext1 - Not Used.
  534. SystemContext2 - Not Used.
  535. Return Value:
  536. None.
  537. -----------------------------------------------------------------------------*/
  538. {
  539. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  540. KIRQL OldIrql;
  541. UNREFERENCED_PARAMETER(Dpc);
  542. UNREFERENCED_PARAMETER(SystemContext1);
  543. UNREFERENCED_PARAMETER(SystemContext2);
  544. IoAcquireCancelSpinLock(&OldIrql);
  545. SerialTryToCompleteCurrent( pPort,
  546. SerialGrabWriteFromIsr,
  547. OldIrql,
  548. STATUS_TIMEOUT,
  549. &pPort->CurrentWriteIrp,
  550. &pPort->WriteQueue,
  551. NULL,
  552. &pPort->WriteRequestTotalTimer,
  553. SerialStartWrite,
  554. SerialGetNextWrite,
  555. SERIAL_REF_TOTAL_TIMER);
  556. }
  557. BOOLEAN
  558. SerialGrabWriteFromIsr(IN PVOID Context)
  559. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  560. Routine Description:
  561. This routine is used to grab the current irp, which could be timing
  562. out or canceling, from the ISR
  563. NOTE: This routine is being called from KeSynchronizeExecution.
  564. NOTE: This routine assumes that the cancel spin lock is held
  565. when this routine is called.
  566. Arguments:
  567. Context - Really a pointer to the device extension.
  568. Return Value:
  569. Always false.
  570. -----------------------------------------------------------------------------*/
  571. {
  572. PPORT_DEVICE_EXTENSION pPort = Context;
  573. // Check if the write length is non-zero. If it is non-zero
  574. // then the ISR still owns the irp. We calculate the the number
  575. // of characters written and update the information field of the
  576. // irp with the characters written. We then clear the write length
  577. // the isr sees.
  578. if(pPort->WriteLength)
  579. {
  580. //
  581. // We could have an xoff counter masquerading as a
  582. // write irp. If so, don't update the write length.
  583. //
  584. if(IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp)->MajorFunction == IRP_MJ_WRITE)
  585. {
  586. pPort->CurrentWriteIrp->IoStatus.Information
  587. = IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp)->Parameters.Write.Length
  588. - pPort->WriteLength;
  589. }
  590. else
  591. {
  592. pPort->CurrentWriteIrp->IoStatus.Information = 0;
  593. }
  594. //
  595. // Since the isr no longer references this irp, we can
  596. // decrement it's reference count.
  597. //
  598. SERIAL_CLEAR_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_ISR);
  599. // Flush the output buffer.
  600. pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, NULL, UL_BC_OP_FLUSH, UL_BC_BUFFER | UL_BC_OUT);
  601. pPort->WriteLength = 0;
  602. }
  603. return FALSE;
  604. }
  605. BOOLEAN
  606. SerialGrabXoffFromIsr(IN PVOID Context)
  607. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  608. Routine Description:
  609. This routine is used to grab an xoff counter irp from the
  610. isr when it is no longer masquerading as a write irp. This
  611. routine is called by the cancel and timeout code for the
  612. xoff counter ioctl.
  613. NOTE: This routine is being called from KeSynchronizeExecution.
  614. NOTE: This routine assumes that the cancel spin lock is held
  615. when this routine is called.
  616. Arguments:
  617. Context - Really a pointer to the device extension.
  618. Return Value:
  619. Always false.
  620. -----------------------------------------------------------------------------*/
  621. {
  622. PPORT_DEVICE_EXTENSION pPort = Context;
  623. if(pPort->CountSinceXoff)
  624. {
  625. // This is only non-zero when there actually is a Xoff ioctl counting down.
  626. pPort->CountSinceXoff = 0;
  627. // We decrement the count since the isr no longer owns the irp.
  628. SERIAL_CLEAR_REFERENCE(pPort->CurrentXoffIrp, SERIAL_REF_ISR);
  629. }
  630. return FALSE;
  631. }
  632. VOID
  633. SerialCompleteXoff(IN PKDPC Dpc,
  634. IN PVOID DeferredContext,
  635. IN PVOID SystemContext1,
  636. IN PVOID SystemContext2)
  637. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  638. Routine Description:
  639. This routine is merely used to truely complete an xoff counter irp. It
  640. assumes that the status and the information fields of the irp are
  641. already correctly filled in.
  642. Arguments:
  643. Dpc - Not Used.
  644. DeferredContext - Really points to the device extension.
  645. SystemContext1 - Not Used.
  646. SystemContext2 - Not Used.
  647. Return Value:
  648. None.
  649. -----------------------------------------------------------------------------*/
  650. {
  651. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  652. KIRQL OldIrql;
  653. UNREFERENCED_PARAMETER(Dpc);
  654. UNREFERENCED_PARAMETER(SystemContext1);
  655. UNREFERENCED_PARAMETER(SystemContext2);
  656. IoAcquireCancelSpinLock(&OldIrql);
  657. // Clear the normal complete reference.
  658. SERIAL_CLEAR_REFERENCE(pPort->CurrentXoffIrp, SERIAL_REF_COMPLETING);
  659. SerialTryToCompleteCurrent( pPort,
  660. NULL,
  661. OldIrql,
  662. STATUS_SUCCESS,
  663. &pPort->CurrentXoffIrp,
  664. NULL,
  665. NULL,
  666. &pPort->XoffCountTimer,
  667. NULL,
  668. NULL,
  669. SERIAL_REF_ISR);
  670. }
  671. VOID
  672. SerialTimeoutXoff(IN PKDPC Dpc,
  673. IN PVOID DeferredContext,
  674. IN PVOID SystemContext1,
  675. IN PVOID SystemContext2)
  676. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  677. Routine Description:
  678. This routine is merely used to truely complete an xoff counter irp,
  679. if its timer has run out.
  680. Arguments:
  681. Dpc - Not Used.
  682. DeferredContext - Really points to the device extension.
  683. SystemContext1 - Not Used.
  684. SystemContext2 - Not Used.
  685. Return Value:
  686. None.
  687. -----------------------------------------------------------------------------*/
  688. {
  689. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  690. KIRQL OldIrql;
  691. UNREFERENCED_PARAMETER(Dpc);
  692. UNREFERENCED_PARAMETER(SystemContext1);
  693. UNREFERENCED_PARAMETER(SystemContext2);
  694. IoAcquireCancelSpinLock(&OldIrql);
  695. SerialTryToCompleteCurrent( pPort,
  696. SerialGrabXoffFromIsr,
  697. OldIrql,
  698. STATUS_SERIAL_COUNTER_TIMEOUT,
  699. &pPort->CurrentXoffIrp,
  700. NULL,
  701. NULL,
  702. NULL,
  703. NULL,
  704. NULL,
  705. SERIAL_REF_TOTAL_TIMER);
  706. }
  707. VOID
  708. SerialCancelCurrentXoff(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  709. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  710. Routine Description:
  711. This routine is used to cancel the current write.
  712. Arguments:
  713. DeviceObject - Pointer to the device object for this device
  714. Irp - Pointer to the IRP to be canceled.
  715. Return Value:
  716. None.
  717. -----------------------------------------------------------------------------*/
  718. {
  719. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  720. SerialTryToCompleteCurrent( pPort,
  721. SerialGrabXoffFromIsr,
  722. Irp->CancelIrql,
  723. STATUS_CANCELLED,
  724. &pPort->CurrentXoffIrp,
  725. NULL,
  726. NULL,
  727. &pPort->XoffCountTimer,
  728. NULL,
  729. NULL,
  730. SERIAL_REF_CANCEL);
  731. }
  732. BOOLEAN
  733. SerialGiveXoffToIsr(IN PVOID Context)
  734. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  735. Routine Description:
  736. This routine starts off the xoff counter. It merely
  737. has to set the xoff count and increment the reference
  738. count to denote that the isr has a reference to the irp.
  739. NOTE: This routine is called by KeSynchronizeExecution.
  740. NOTE: This routine assumes that it is called with the
  741. cancel spin lock held.
  742. Arguments:
  743. Context - Really a pointer to the device extension.
  744. Return Value:
  745. This routine always returns FALSE.
  746. -----------------------------------------------------------------------------*/
  747. {
  748. PPORT_DEVICE_EXTENSION pPort = Context;
  749. // The current stack location. This contains all of the
  750. // information we need to process this particular request.
  751. PSERIAL_XOFF_COUNTER Xc = pPort->CurrentXoffIrp->AssociatedIrp.SystemBuffer;
  752. ASSERT(pPort->CurrentXoffIrp);
  753. pPort->CountSinceXoff = Xc->Counter;
  754. // The isr now has a reference to the irp.
  755. SERIAL_SET_REFERENCE(pPort->CurrentXoffIrp, SERIAL_REF_ISR);
  756. return FALSE;
  757. }