Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1422 lines
32 KiB

  1. /*++
  2. Copyright (c) 1991, 1992, 1993 - 1997 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. --*/
  13. #include "precomp.h"
  14. BOOLEAN
  15. SerialGiveWriteToIsr(
  16. IN PVOID Context
  17. );
  18. VOID
  19. SerialCancelCurrentWrite(
  20. PDEVICE_OBJECT DeviceObject,
  21. PIRP Irp
  22. );
  23. BOOLEAN
  24. SerialGrabWriteFromIsr(
  25. IN PVOID Context
  26. );
  27. BOOLEAN
  28. SerialGrabXoffFromIsr(
  29. IN PVOID Context
  30. );
  31. VOID
  32. SerialCancelCurrentXoff(
  33. PDEVICE_OBJECT DeviceObject,
  34. PIRP Irp
  35. );
  36. BOOLEAN
  37. SerialGiveXoffToIsr(
  38. IN PVOID Context
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text(PAGESER,SerialProcessEmptyTransmit)
  42. #pragma alloc_text(PAGESER,SerialWrite)
  43. #pragma alloc_text(PAGESER,SerialStartWrite)
  44. #pragma alloc_text(PAGESER,SerialGetNextWrite)
  45. #pragma alloc_text(PAGESER,SerialGiveWriteToIsr)
  46. #pragma alloc_text(PAGESER,SerialCancelCurrentWrite)
  47. #pragma alloc_text(PAGESER,SerialGrabWriteFromIsr)
  48. #pragma alloc_text(PAGESER,SerialGrabXoffFromIsr)
  49. #pragma alloc_text(PAGESER,SerialCancelCurrentXoff)
  50. #pragma alloc_text(PAGESER,SerialGiveXoffToIsr)
  51. #endif
  52. NTSTATUS
  53. SerialWrite(
  54. IN PDEVICE_OBJECT DeviceObject,
  55. IN PIRP Irp
  56. )
  57. /*++
  58. Routine Description:
  59. This is the dispatch routine for write. It validates the parameters
  60. for the write request and if all is ok then it places the request
  61. on the work queue.
  62. Arguments:
  63. DeviceObject - Pointer to the device object for this device
  64. Irp - Pointer to the IRP for the current request
  65. Return Value:
  66. If the io is zero length then it will return STATUS_SUCCESS,
  67. otherwise this routine will return STATUS_PENDING.
  68. --*/
  69. {
  70. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  71. NTSTATUS status;
  72. SERIAL_LOCKED_PAGED_CODE();
  73. SerialDump(SERTRACECALLS, ("Entering SerialWrite\n"));
  74. if ((status = SerialIRPPrologue(Irp, Extension)) != STATUS_SUCCESS) {
  75. SerialCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
  76. SerialDump(SERTRACECALLS, ("Leaving SerialWrite (1)\n"));
  77. return status;
  78. }
  79. SerialDump(
  80. SERIRPPATH,
  81. ("SERIAL: Dispatch entry for: %x\n",Irp)
  82. );
  83. if (SerialCompleteIfError(
  84. DeviceObject,
  85. Irp
  86. ) != STATUS_SUCCESS) {
  87. SerialDump(SERTRACECALLS, ("Leaving SerialWrite (2)\n"));
  88. return STATUS_CANCELLED;
  89. }
  90. Irp->IoStatus.Information = 0L;
  91. //
  92. // Quick check for a zero length write. If it is zero length
  93. // then we are already done!
  94. //
  95. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) {
  96. //
  97. // Well it looks like we actually have to do some
  98. // work. Put the write on the queue so that we can
  99. // process it when our previous writes are done.
  100. //
  101. SerialDump(SERTRACECALLS, ("Leaving SerialWrite (3)\n"));
  102. return SerialStartOrQueue(
  103. Extension,
  104. Irp,
  105. &Extension->WriteQueue,
  106. &Extension->CurrentWriteIrp,
  107. SerialStartWrite
  108. );
  109. } else {
  110. Irp->IoStatus.Status = STATUS_SUCCESS;
  111. SerialDump(
  112. SERIRPPATH,
  113. ("SERIAL: Complete Irp: %x\n",Irp)
  114. );
  115. SerialCompleteRequest(Extension, Irp, 0);
  116. SerialDump(SERTRACECALLS, ("Leaving SerialWrite (4)\n"));
  117. return STATUS_SUCCESS;
  118. }
  119. }
  120. NTSTATUS
  121. SerialStartWrite(
  122. IN PSERIAL_DEVICE_EXTENSION Extension
  123. )
  124. /*++
  125. Routine Description:
  126. This routine is used to start off any write. It initializes
  127. the Iostatus fields of the irp. It will set up any timers
  128. that are used to control the write.
  129. Arguments:
  130. Extension - Points to the serial device extension
  131. Return Value:
  132. This routine will return STATUS_PENDING for all writes
  133. other than those that we find are cancelled.
  134. --*/
  135. {
  136. PIRP NewIrp;
  137. KIRQL OldIrql;
  138. LARGE_INTEGER TotalTime;
  139. BOOLEAN UseATimer;
  140. SERIAL_TIMEOUTS Timeouts;
  141. BOOLEAN SetFirstStatus = FALSE;
  142. NTSTATUS FirstStatus;
  143. SERIAL_LOCKED_PAGED_CODE();
  144. SerialDump(SERTRACECALLS, ("SERIAL: SerialStartWrite\n"));
  145. do {
  146. //
  147. // If there is an xoff counter then complete it.
  148. //
  149. IoAcquireCancelSpinLock(&OldIrql);
  150. //
  151. // We see if there is a actually an Xoff counter irp.
  152. //
  153. // If there is, we put the write irp back on the head
  154. // of the write list. We then kill the xoff counter.
  155. // The xoff counter killing code will actually make the
  156. // xoff counter back into the current write irp, and
  157. // in the course of completing the xoff (which is now
  158. // the current write) we will restart this irp.
  159. //
  160. if (Extension->CurrentXoffIrp) {
  161. if (SERIAL_REFERENCE_COUNT(Extension->CurrentXoffIrp)) {
  162. //
  163. // The reference count is non-zero. This implies that
  164. // the xoff irp has not made it through the completion
  165. // path yet. We will increment the reference count
  166. // and attempt to complete it ourseleves.
  167. //
  168. SERIAL_SET_REFERENCE(
  169. Extension->CurrentXoffIrp,
  170. SERIAL_REF_XOFF_REF
  171. );
  172. //
  173. // The following call will actually release the
  174. // cancel spin lock.
  175. //
  176. SerialTryToCompleteCurrent(
  177. Extension,
  178. SerialGrabXoffFromIsr,
  179. OldIrql,
  180. STATUS_SERIAL_MORE_WRITES,
  181. &Extension->CurrentXoffIrp,
  182. NULL,
  183. NULL,
  184. &Extension->XoffCountTimer,
  185. NULL,
  186. NULL,
  187. SERIAL_REF_XOFF_REF
  188. );
  189. } else {
  190. //
  191. // The irp is well on its way to being finished.
  192. // We can let the regular completion code do the
  193. // work. Just release the spin lock.
  194. //
  195. IoReleaseCancelSpinLock(OldIrql);
  196. }
  197. } else {
  198. IoReleaseCancelSpinLock(OldIrql);
  199. }
  200. UseATimer = FALSE;
  201. //
  202. // Calculate the timeout value needed for the
  203. // request. Note that the values stored in the
  204. // timeout record are in milliseconds. Note that
  205. // if the timeout values are zero then we won't start
  206. // the timer.
  207. //
  208. KeAcquireSpinLock(
  209. &Extension->ControlLock,
  210. &OldIrql
  211. );
  212. Timeouts = Extension->Timeouts;
  213. KeReleaseSpinLock(
  214. &Extension->ControlLock,
  215. OldIrql
  216. );
  217. if (Timeouts.WriteTotalTimeoutConstant ||
  218. Timeouts.WriteTotalTimeoutMultiplier) {
  219. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(
  220. Extension->CurrentWriteIrp
  221. );
  222. UseATimer = TRUE;
  223. //
  224. // We have some timer values to calculate.
  225. //
  226. // Take care, we might have an xoff counter masquerading
  227. // as a write.
  228. //
  229. TotalTime.QuadPart =
  230. ((LONGLONG)((UInt32x32To64(
  231. (IrpSp->MajorFunction == IRP_MJ_WRITE)?
  232. (IrpSp->Parameters.Write.Length):
  233. (1),
  234. Timeouts.WriteTotalTimeoutMultiplier
  235. )
  236. + Timeouts.WriteTotalTimeoutConstant)))
  237. * -10000;
  238. }
  239. //
  240. // The irp may be going to the isr shortly. Now
  241. // is a good time to initialize its reference counts.
  242. //
  243. SERIAL_INIT_REFERENCE(Extension->CurrentWriteIrp);
  244. //
  245. // We need to see if this irp should be canceled.
  246. //
  247. IoAcquireCancelSpinLock(&OldIrql);
  248. if (Extension->CurrentWriteIrp->Cancel) {
  249. IoReleaseCancelSpinLock(OldIrql);
  250. Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;
  251. if (!SetFirstStatus) {
  252. FirstStatus = STATUS_CANCELLED;
  253. SetFirstStatus = TRUE;
  254. }
  255. } else {
  256. if (!SetFirstStatus) {
  257. //
  258. // If we haven't set our first status, then
  259. // this is the only irp that could have possibly
  260. // not been on the queue. (It could have been
  261. // on the queue if this routine is being invoked
  262. // from the completion routine.) Since this
  263. // irp might never have been on the queue we
  264. // should mark it as pending.
  265. //
  266. IoMarkIrpPending(Extension->CurrentWriteIrp);
  267. SetFirstStatus = TRUE;
  268. FirstStatus = STATUS_PENDING;
  269. }
  270. //
  271. // We give the irp to to the isr to write out.
  272. // We set a cancel routine that knows how to
  273. // grab the current write away from the isr.
  274. //
  275. // Since the cancel routine has an implicit reference
  276. // to this irp up the reference count.
  277. //
  278. IoSetCancelRoutine(
  279. Extension->CurrentWriteIrp,
  280. SerialCancelCurrentWrite
  281. );
  282. SERIAL_SET_REFERENCE(
  283. Extension->CurrentWriteIrp,
  284. SERIAL_REF_CANCEL
  285. );
  286. if (UseATimer) {
  287. SerialSetTimer(
  288. &Extension->WriteRequestTotalTimer,
  289. TotalTime,
  290. &Extension->TotalWriteTimeoutDpc,
  291. Extension
  292. );
  293. //
  294. // This timer now has a reference to the irp.
  295. //
  296. SERIAL_SET_REFERENCE(
  297. Extension->CurrentWriteIrp,
  298. SERIAL_REF_TOTAL_TIMER
  299. );
  300. }
  301. KeSynchronizeExecution(
  302. Extension->Interrupt,
  303. SerialGiveWriteToIsr,
  304. Extension
  305. );
  306. IoReleaseCancelSpinLock(OldIrql);
  307. break;
  308. }
  309. //
  310. // Well the write was canceled before we could start it up.
  311. // Try to get another.
  312. //
  313. SerialGetNextWrite(
  314. &Extension->CurrentWriteIrp,
  315. &Extension->WriteQueue,
  316. &NewIrp,
  317. TRUE,
  318. Extension
  319. );
  320. } while (NewIrp);
  321. return FirstStatus;
  322. }
  323. VOID
  324. SerialGetNextWrite(
  325. IN PIRP *CurrentOpIrp,
  326. IN PLIST_ENTRY QueueToProcess,
  327. IN PIRP *NewIrp,
  328. IN BOOLEAN CompleteCurrent,
  329. PSERIAL_DEVICE_EXTENSION Extension
  330. )
  331. /*++
  332. Routine Description:
  333. This routine completes the old write as well as getting
  334. a pointer to the next write.
  335. The reason that we have have pointers to the current write
  336. queue as well as the current write irp is so that this
  337. routine may be used in the common completion code for
  338. read and write.
  339. Arguments:
  340. CurrentOpIrp - Pointer to the pointer that points to the
  341. current write irp.
  342. QueueToProcess - Pointer to the write queue.
  343. NewIrp - A pointer to a pointer to the irp that will be the
  344. current irp. Note that this could end up pointing
  345. to a null pointer. This does NOT necessaryly mean
  346. that there is no current write. What could occur
  347. is that while the cancel lock is held the write
  348. queue ended up being empty, but as soon as we release
  349. the cancel spin lock a new irp came in from
  350. SerialStartWrite.
  351. CompleteCurrent - Flag indicates whether the CurrentOpIrp should
  352. be completed.
  353. Return Value:
  354. None.
  355. --*/
  356. {
  357. // PSERIAL_DEVICE_EXTENSION Extension = CONTAINING_RECORD(
  358. // QueueToProcess,
  359. // SERIAL_DEVICE_EXTENSION,
  360. // WriteQueue
  361. // );
  362. SERIAL_LOCKED_PAGED_CODE();
  363. SerialDump(SERTRACECALLS, ("SERIAL: SerialGetNextWrite\n"));
  364. do {
  365. //
  366. // We could be completing a flush.
  367. //
  368. if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
  369. == IRP_MJ_WRITE) {
  370. KIRQL OldIrql;
  371. ASSERT(Extension->TotalCharsQueued >=
  372. (IoGetCurrentIrpStackLocation(*CurrentOpIrp)
  373. ->Parameters.Write.Length));
  374. IoAcquireCancelSpinLock(&OldIrql);
  375. Extension->TotalCharsQueued -=
  376. IoGetCurrentIrpStackLocation(*CurrentOpIrp)
  377. ->Parameters.Write.Length;
  378. IoReleaseCancelSpinLock(OldIrql);
  379. } else if (IoGetCurrentIrpStackLocation(*CurrentOpIrp)->MajorFunction
  380. == IRP_MJ_DEVICE_CONTROL) {
  381. KIRQL OldIrql;
  382. PIRP Irp;
  383. PSERIAL_XOFF_COUNTER Xc;
  384. IoAcquireCancelSpinLock(&OldIrql);
  385. Irp = *CurrentOpIrp;
  386. Xc = Irp->AssociatedIrp.SystemBuffer;
  387. //
  388. // We should never have a xoff counter when we
  389. // get to this point.
  390. //
  391. ASSERT(!Extension->CurrentXoffIrp);
  392. //
  393. // We absolutely shouldn't have a cancel routine
  394. // at this point.
  395. //
  396. ASSERT(!Irp->CancelRoutine);
  397. //
  398. // This could only be a xoff counter masquerading as
  399. // a write irp.
  400. //
  401. Extension->TotalCharsQueued--;
  402. //
  403. // Check to see of the xoff irp has been set with success.
  404. // This means that the write completed normally. If that
  405. // is the case, and it hasn't been set to cancel in the
  406. // meanwhile, then go on and make it the CurrentXoffIrp.
  407. //
  408. if (Irp->IoStatus.Status != STATUS_SUCCESS) {
  409. //
  410. // Oh well, we can just finish it off.
  411. //
  412. NOTHING;
  413. } else if (Irp->Cancel) {
  414. Irp->IoStatus.Status = STATUS_CANCELLED;
  415. } else {
  416. //
  417. // Give it a new cancel routine, and increment the
  418. // reference count because the cancel routine has
  419. // a reference to it.
  420. //
  421. IoSetCancelRoutine(
  422. Irp,
  423. SerialCancelCurrentXoff
  424. );
  425. SERIAL_SET_REFERENCE(
  426. Irp,
  427. SERIAL_REF_CANCEL
  428. );
  429. //
  430. // We don't want to complete the current irp now. This
  431. // will now get completed by the Xoff counter code.
  432. //
  433. CompleteCurrent = FALSE;
  434. //
  435. // Give the counter to the isr.
  436. //
  437. Extension->CurrentXoffIrp = Irp;
  438. KeSynchronizeExecution(
  439. Extension->Interrupt,
  440. SerialGiveXoffToIsr,
  441. Extension
  442. );
  443. //
  444. // Start the timer for the counter and increment
  445. // the reference count since the timer has a
  446. // reference to the irp.
  447. //
  448. if (Xc->Timeout) {
  449. LARGE_INTEGER delta;
  450. delta.QuadPart = -((LONGLONG)UInt32x32To64(
  451. 1000,
  452. Xc->Timeout
  453. ));
  454. SerialSetTimer(
  455. &Extension->XoffCountTimer,
  456. delta,
  457. &Extension->XoffCountTimeoutDpc,
  458. Extension
  459. );
  460. SERIAL_SET_REFERENCE(
  461. Irp,
  462. SERIAL_REF_TOTAL_TIMER
  463. );
  464. }
  465. }
  466. IoReleaseCancelSpinLock(OldIrql);
  467. }
  468. //
  469. // Note that the following call will (probably) also cause
  470. // the current irp to be completed.
  471. //
  472. SerialGetNextIrp(
  473. CurrentOpIrp,
  474. QueueToProcess,
  475. NewIrp,
  476. CompleteCurrent,
  477. Extension
  478. );
  479. if (!*NewIrp) {
  480. KIRQL OldIrql;
  481. IoAcquireCancelSpinLock(&OldIrql);
  482. KeSynchronizeExecution(
  483. Extension->Interrupt,
  484. SerialProcessEmptyTransmit,
  485. Extension
  486. );
  487. IoReleaseCancelSpinLock(OldIrql);
  488. break;
  489. } else if (IoGetCurrentIrpStackLocation(*NewIrp)->MajorFunction
  490. == IRP_MJ_FLUSH_BUFFERS) {
  491. //
  492. // If we encounter a flush request we just want to get
  493. // the next irp and complete the flush.
  494. //
  495. // Note that if NewIrp is non-null then it is also
  496. // equal to CurrentWriteIrp.
  497. //
  498. ASSERT((*NewIrp) == (*CurrentOpIrp));
  499. (*NewIrp)->IoStatus.Status = STATUS_SUCCESS;
  500. } else {
  501. break;
  502. }
  503. } while (TRUE);
  504. }
  505. VOID
  506. SerialCompleteWrite(
  507. IN PKDPC Dpc,
  508. IN PVOID DeferredContext,
  509. IN PVOID SystemContext1,
  510. IN PVOID SystemContext2
  511. )
  512. /*++
  513. Routine Description:
  514. This routine is merely used to complete any write. It
  515. assumes that the status and the information fields of
  516. the irp are already correctly filled in.
  517. Arguments:
  518. Dpc - Not Used.
  519. DeferredContext - Really points to the device extension.
  520. SystemContext1 - Not Used.
  521. SystemContext2 - Not Used.
  522. Return Value:
  523. None.
  524. --*/
  525. {
  526. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  527. KIRQL OldIrql;
  528. UNREFERENCED_PARAMETER(SystemContext1);
  529. UNREFERENCED_PARAMETER(SystemContext2);
  530. SerialDump(SERTRACECALLS, ("SERIAL: SerialCompleteWrite\n"));
  531. IoAcquireCancelSpinLock(&OldIrql);
  532. SerialTryToCompleteCurrent(
  533. Extension,
  534. NULL,
  535. OldIrql,
  536. STATUS_SUCCESS,
  537. &Extension->CurrentWriteIrp,
  538. &Extension->WriteQueue,
  539. NULL,
  540. &Extension->WriteRequestTotalTimer,
  541. SerialStartWrite,
  542. SerialGetNextWrite,
  543. SERIAL_REF_ISR
  544. );
  545. SerialDpcEpilogue(Extension, Dpc);
  546. }
  547. BOOLEAN
  548. SerialProcessEmptyTransmit(
  549. IN PVOID Context
  550. )
  551. /*++
  552. Routine Description:
  553. This routine is used to determine if conditions are appropriate
  554. to satisfy a wait for transmit empty event, and if so to complete
  555. the irp that is waiting for that event. It also call the code
  556. that checks to see if we should lower the RTS line if we are
  557. doing transmit toggling.
  558. NOTE: This routine is called by KeSynchronizeExecution.
  559. NOTE: This routine assumes that it is called with the cancel
  560. spinlock held.
  561. Arguments:
  562. Context - Really a pointer to the device extension.
  563. Return Value:
  564. This routine always returns FALSE.
  565. --*/
  566. {
  567. PSERIAL_DEVICE_EXTENSION Extension = Context;
  568. SERIAL_LOCKED_PAGED_CODE();
  569. if (Extension->IsrWaitMask && (Extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
  570. Extension->EmptiedTransmit && (!Extension->TransmitImmediate) &&
  571. (!Extension->CurrentWriteIrp) && IsListEmpty(&Extension->WriteQueue)) {
  572. Extension->HistoryMask |= SERIAL_EV_TXEMPTY;
  573. if (Extension->IrpMaskLocation) {
  574. *Extension->IrpMaskLocation = Extension->HistoryMask;
  575. Extension->IrpMaskLocation = NULL;
  576. Extension->HistoryMask = 0;
  577. Extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG);
  578. SerialInsertQueueDpc(
  579. &Extension->CommWaitDpc,
  580. NULL,
  581. NULL,
  582. Extension
  583. );
  584. }
  585. Extension->CountOfTryingToLowerRTS++;
  586. SerialPerhapsLowerRTS(Extension);
  587. }
  588. return FALSE;
  589. }
  590. BOOLEAN
  591. SerialGiveWriteToIsr(
  592. IN PVOID Context
  593. )
  594. /*++
  595. Routine Description:
  596. Try to start off the write by slipping it in behind
  597. a transmit immediate char, or if that isn't available
  598. and the transmit holding register is empty, "tickle"
  599. the UART into interrupting with a transmit buffer
  600. empty.
  601. NOTE: This routine is called by KeSynchronizeExecution.
  602. NOTE: This routine assumes that it is called with the
  603. cancel spin lock held.
  604. Arguments:
  605. Context - Really a pointer to the device extension.
  606. Return Value:
  607. This routine always returns FALSE.
  608. --*/
  609. {
  610. PSERIAL_DEVICE_EXTENSION Extension = Context;
  611. //
  612. // The current stack location. This contains all of the
  613. // information we need to process this particular request.
  614. //
  615. PIO_STACK_LOCATION IrpSp;
  616. SERIAL_LOCKED_PAGED_CODE();
  617. IrpSp = IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp);
  618. //
  619. // We might have a xoff counter request masquerading as a
  620. // write. The length of these requests will always be one
  621. // and we can get a pointer to the actual character from
  622. // the data supplied by the user.
  623. //
  624. if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
  625. Extension->WriteLength = IrpSp->Parameters.Write.Length;
  626. Extension->WriteCurrentChar =
  627. Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer;
  628. } else {
  629. Extension->WriteLength = 1;
  630. Extension->WriteCurrentChar =
  631. ((PUCHAR)Extension->CurrentWriteIrp->AssociatedIrp.SystemBuffer) +
  632. FIELD_OFFSET(
  633. SERIAL_XOFF_COUNTER,
  634. XoffChar
  635. );
  636. }
  637. //
  638. // The isr now has a reference to the irp.
  639. //
  640. SERIAL_SET_REFERENCE(
  641. Extension->CurrentWriteIrp,
  642. SERIAL_REF_ISR
  643. );
  644. //
  645. // Check first to see if an immediate char is transmitting.
  646. // If it is then we'll just slip in behind it when its
  647. // done.
  648. //
  649. if (!Extension->TransmitImmediate) {
  650. //
  651. // If there is no immediate char transmitting then we
  652. // will "re-enable" the transmit holding register empty
  653. // interrupt. The 8250 family of devices will always
  654. // signal a transmit holding register empty interrupt
  655. // *ANY* time this bit is set to one. By doing things
  656. // this way we can simply use the normal interrupt code
  657. // to start off this write.
  658. //
  659. // We've been keeping track of whether the transmit holding
  660. // register is empty so it we only need to do this
  661. // if the register is empty.
  662. //
  663. if (Extension->HoldingEmpty) {
  664. DISABLE_ALL_INTERRUPTS(Extension->Controller);
  665. ENABLE_ALL_INTERRUPTS(Extension->Controller);
  666. }
  667. }
  668. //
  669. // The rts line may already be up from previous writes,
  670. // however, it won't take much additional time to turn
  671. // on the RTS line if we are doing transmit toggling.
  672. //
  673. if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
  674. SERIAL_TRANSMIT_TOGGLE) {
  675. SerialSetRTS(Extension);
  676. }
  677. return FALSE;
  678. }
  679. VOID
  680. SerialCancelCurrentWrite(
  681. PDEVICE_OBJECT DeviceObject,
  682. PIRP Irp
  683. )
  684. /*++
  685. Routine Description:
  686. This routine is used to cancel the current write.
  687. Arguments:
  688. DeviceObject - Pointer to the device object for this device
  689. Irp - Pointer to the IRP to be canceled.
  690. Return Value:
  691. None.
  692. --*/
  693. {
  694. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  695. SERIAL_LOCKED_PAGED_CODE();
  696. SerialTryToCompleteCurrent(
  697. Extension,
  698. SerialGrabWriteFromIsr,
  699. Irp->CancelIrql,
  700. STATUS_CANCELLED,
  701. &Extension->CurrentWriteIrp,
  702. &Extension->WriteQueue,
  703. NULL,
  704. &Extension->WriteRequestTotalTimer,
  705. SerialStartWrite,
  706. SerialGetNextWrite,
  707. SERIAL_REF_CANCEL
  708. );
  709. }
  710. VOID
  711. SerialWriteTimeout(
  712. IN PKDPC Dpc,
  713. IN PVOID DeferredContext,
  714. IN PVOID SystemContext1,
  715. IN PVOID SystemContext2
  716. )
  717. /*++
  718. Routine Description:
  719. This routine will try to timeout the current write.
  720. Arguments:
  721. Dpc - Not Used.
  722. DeferredContext - Really points to the device extension.
  723. SystemContext1 - Not Used.
  724. SystemContext2 - Not Used.
  725. Return Value:
  726. None.
  727. --*/
  728. {
  729. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  730. KIRQL OldIrql;
  731. UNREFERENCED_PARAMETER(SystemContext1);
  732. UNREFERENCED_PARAMETER(SystemContext2);
  733. SerialDump(SERTRACECALLS, ("SERIAL: SerialWriteTimeout\n"));
  734. IoAcquireCancelSpinLock(&OldIrql);
  735. SerialTryToCompleteCurrent(
  736. Extension,
  737. SerialGrabWriteFromIsr,
  738. OldIrql,
  739. STATUS_TIMEOUT,
  740. &Extension->CurrentWriteIrp,
  741. &Extension->WriteQueue,
  742. NULL,
  743. &Extension->WriteRequestTotalTimer,
  744. SerialStartWrite,
  745. SerialGetNextWrite,
  746. SERIAL_REF_TOTAL_TIMER
  747. );
  748. SerialDpcEpilogue(Extension, Dpc);
  749. }
  750. BOOLEAN
  751. SerialGrabWriteFromIsr(
  752. IN PVOID Context
  753. )
  754. /*++
  755. Routine Description:
  756. This routine is used to grab the current irp, which could be timing
  757. out or canceling, from the ISR
  758. NOTE: This routine is being called from KeSynchronizeExecution.
  759. NOTE: This routine assumes that the cancel spin lock is held
  760. when this routine is called.
  761. Arguments:
  762. Context - Really a pointer to the device extension.
  763. Return Value:
  764. Always false.
  765. --*/
  766. {
  767. PSERIAL_DEVICE_EXTENSION Extension = Context;
  768. SERIAL_LOCKED_PAGED_CODE();
  769. //
  770. // Check if the write length is non-zero. If it is non-zero
  771. // then the ISR still owns the irp. We calculate the the number
  772. // of characters written and update the information field of the
  773. // irp with the characters written. We then clear the write length
  774. // the isr sees.
  775. //
  776. if (Extension->WriteLength) {
  777. //
  778. // We could have an xoff counter masquerading as a
  779. // write irp. If so, don't update the write length.
  780. //
  781. if (IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
  782. ->MajorFunction == IRP_MJ_WRITE) {
  783. Extension->CurrentWriteIrp->IoStatus.Information =
  784. IoGetCurrentIrpStackLocation(
  785. Extension->CurrentWriteIrp
  786. )->Parameters.Write.Length -
  787. Extension->WriteLength;
  788. } else {
  789. Extension->CurrentWriteIrp->IoStatus.Information = 0;
  790. }
  791. //
  792. // Since the isr no longer references this irp, we can
  793. // decrement it's reference count.
  794. //
  795. SERIAL_CLEAR_REFERENCE(
  796. Extension->CurrentWriteIrp,
  797. SERIAL_REF_ISR
  798. );
  799. Extension->WriteLength = 0;
  800. }
  801. return FALSE;
  802. }
  803. BOOLEAN
  804. SerialGrabXoffFromIsr(
  805. IN PVOID Context
  806. )
  807. /*++
  808. Routine Description:
  809. This routine is used to grab an xoff counter irp from the
  810. isr when it is no longer masquerading as a write irp. This
  811. routine is called by the cancel and timeout code for the
  812. xoff counter ioctl.
  813. NOTE: This routine is being called from KeSynchronizeExecution.
  814. NOTE: This routine assumes that the cancel spin lock is held
  815. when this routine is called.
  816. Arguments:
  817. Context - Really a pointer to the device extension.
  818. Return Value:
  819. Always false.
  820. --*/
  821. {
  822. PSERIAL_DEVICE_EXTENSION Extension = Context;
  823. SERIAL_LOCKED_PAGED_CODE();
  824. if (Extension->CountSinceXoff) {
  825. //
  826. // This is only non-zero when there actually is a Xoff ioctl
  827. // counting down.
  828. //
  829. Extension->CountSinceXoff = 0;
  830. //
  831. // We decrement the count since the isr no longer owns
  832. // the irp.
  833. //
  834. SERIAL_CLEAR_REFERENCE(
  835. Extension->CurrentXoffIrp,
  836. SERIAL_REF_ISR
  837. );
  838. }
  839. return FALSE;
  840. }
  841. VOID
  842. SerialCompleteXoff(
  843. IN PKDPC Dpc,
  844. IN PVOID DeferredContext,
  845. IN PVOID SystemContext1,
  846. IN PVOID SystemContext2
  847. )
  848. /*++
  849. Routine Description:
  850. This routine is merely used to truely complete an xoff counter irp. It
  851. assumes that the status and the information fields of the irp are
  852. already correctly filled in.
  853. Arguments:
  854. Dpc - Not Used.
  855. DeferredContext - Really points to the device extension.
  856. SystemContext1 - Not Used.
  857. SystemContext2 - Not Used.
  858. Return Value:
  859. None.
  860. --*/
  861. {
  862. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  863. KIRQL OldIrql;
  864. UNREFERENCED_PARAMETER(SystemContext1);
  865. UNREFERENCED_PARAMETER(SystemContext2);
  866. SerialDump(SERTRACECALLS, ("SERIAL: SerialCompleteXoff\n"));
  867. IoAcquireCancelSpinLock(&OldIrql);
  868. SerialTryToCompleteCurrent(
  869. Extension,
  870. NULL,
  871. OldIrql,
  872. STATUS_SUCCESS,
  873. &Extension->CurrentXoffIrp,
  874. NULL,
  875. NULL,
  876. &Extension->XoffCountTimer,
  877. NULL,
  878. NULL,
  879. SERIAL_REF_ISR
  880. );
  881. SerialDpcEpilogue(Extension, Dpc);
  882. }
  883. VOID
  884. SerialTimeoutXoff(
  885. IN PKDPC Dpc,
  886. IN PVOID DeferredContext,
  887. IN PVOID SystemContext1,
  888. IN PVOID SystemContext2
  889. )
  890. /*++
  891. Routine Description:
  892. This routine is merely used to truely complete an xoff counter irp,
  893. if its timer has run out.
  894. Arguments:
  895. Dpc - Not Used.
  896. DeferredContext - Really points to the device extension.
  897. SystemContext1 - Not Used.
  898. SystemContext2 - Not Used.
  899. Return Value:
  900. None.
  901. --*/
  902. {
  903. PSERIAL_DEVICE_EXTENSION Extension = DeferredContext;
  904. KIRQL OldIrql;
  905. UNREFERENCED_PARAMETER(SystemContext1);
  906. UNREFERENCED_PARAMETER(SystemContext2);
  907. SerialDump(SERTRACECALLS, ("SERIAL: SerialTimeoutXoff\n"));
  908. IoAcquireCancelSpinLock(&OldIrql);
  909. SerialTryToCompleteCurrent(
  910. Extension,
  911. SerialGrabXoffFromIsr,
  912. OldIrql,
  913. STATUS_SERIAL_COUNTER_TIMEOUT,
  914. &Extension->CurrentXoffIrp,
  915. NULL,
  916. NULL,
  917. NULL,
  918. NULL,
  919. NULL,
  920. SERIAL_REF_TOTAL_TIMER
  921. );
  922. SerialDpcEpilogue(Extension, Dpc);
  923. }
  924. VOID
  925. SerialCancelCurrentXoff(
  926. PDEVICE_OBJECT DeviceObject,
  927. PIRP Irp
  928. )
  929. /*++
  930. Routine Description:
  931. This routine is used to cancel the current write.
  932. Arguments:
  933. DeviceObject - Pointer to the device object for this device
  934. Irp - Pointer to the IRP to be canceled.
  935. Return Value:
  936. None.
  937. --*/
  938. {
  939. PSERIAL_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
  940. SERIAL_LOCKED_PAGED_CODE();
  941. SerialTryToCompleteCurrent(
  942. Extension,
  943. SerialGrabXoffFromIsr,
  944. Irp->CancelIrql,
  945. STATUS_CANCELLED,
  946. &Extension->CurrentXoffIrp,
  947. NULL,
  948. NULL,
  949. &Extension->XoffCountTimer,
  950. NULL,
  951. NULL,
  952. SERIAL_REF_CANCEL
  953. );
  954. }
  955. BOOLEAN
  956. SerialGiveXoffToIsr(
  957. IN PVOID Context
  958. )
  959. /*++
  960. Routine Description:
  961. This routine starts off the xoff counter. It merely
  962. has to set the xoff count and increment the reference
  963. count to denote that the isr has a reference to the irp.
  964. NOTE: This routine is called by KeSynchronizeExecution.
  965. NOTE: This routine assumes that it is called with the
  966. cancel spin lock held.
  967. Arguments:
  968. Context - Really a pointer to the device extension.
  969. Return Value:
  970. This routine always returns FALSE.
  971. --*/
  972. {
  973. PSERIAL_DEVICE_EXTENSION Extension = Context;
  974. //
  975. // The current stack location. This contains all of the
  976. // information we need to process this particular request.
  977. //
  978. PSERIAL_XOFF_COUNTER Xc =
  979. Extension->CurrentXoffIrp->AssociatedIrp.SystemBuffer;
  980. SERIAL_LOCKED_PAGED_CODE();
  981. ASSERT(Extension->CurrentXoffIrp);
  982. Extension->CountSinceXoff = Xc->Counter;
  983. //
  984. // The isr now has a reference to the irp.
  985. //
  986. SERIAL_SET_REFERENCE(
  987. Extension->CurrentXoffIrp,
  988. SERIAL_REF_ISR
  989. );
  990. return FALSE;
  991. }