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.

1318 lines
34 KiB

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