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.

1402 lines
36 KiB

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