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.

1922 lines
56 KiB

  1. /*--------------------------------------------------------------------------
  2. *
  3. * Copyright (C) Cyclades Corporation, 1997-2001.
  4. * All rights reserved.
  5. *
  6. * Cyclades-Z Port Driver
  7. *
  8. * This file: cyzread.c
  9. *
  10. * Description: This module contains the code related to read
  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. VOID
  31. CyzCancelCurrentRead(
  32. PDEVICE_OBJECT DeviceObject,
  33. PIRP Irp
  34. );
  35. BOOLEAN
  36. CyzGrabReadFromIsr(
  37. IN PVOID Context
  38. );
  39. BOOLEAN
  40. CyzUpdateReadByIsr(
  41. IN PVOID Context
  42. );
  43. ULONG
  44. CyzGetCharsFromIntBuffer(
  45. PCYZ_DEVICE_EXTENSION Extension
  46. );
  47. BOOLEAN
  48. CyzUpdateInterruptBuffer(
  49. IN PVOID Context
  50. );
  51. BOOLEAN
  52. CyzUpdateAndSwitchToUser(
  53. IN PVOID Context
  54. );
  55. NTSTATUS
  56. CyzResizeBuffer(
  57. IN PCYZ_DEVICE_EXTENSION Extension
  58. );
  59. ULONG
  60. CyzMoveToNewIntBuffer(
  61. PCYZ_DEVICE_EXTENSION Extension,
  62. PUCHAR NewBuffer
  63. );
  64. BOOLEAN
  65. CyzUpdateAndSwitchToNew(
  66. IN PVOID Context
  67. );
  68. #ifdef ALLOC_PRAGMA
  69. #pragma alloc_text(PAGESER,CyzRead)
  70. #pragma alloc_text(PAGESER,CyzStartRead)
  71. #pragma alloc_text(PAGESER,CyzCancelCurrentRead)
  72. #pragma alloc_text(PAGESER,CyzGrabReadFromIsr)
  73. #pragma alloc_text(PAGESER,CyzUpdateReadByIsr)
  74. #pragma alloc_text(PAGESER,CyzGetCharsFromIntBuffer)
  75. #pragma alloc_text(PAGESER,CyzUpdateInterruptBuffer)
  76. #pragma alloc_text(PAGESER,CyzUpdateAndSwitchToUser)
  77. #pragma alloc_text(PAGESER,CyzResizeBuffer)
  78. #pragma alloc_text(PAGESER,CyzMoveToNewIntBuffer)
  79. #pragma alloc_text(PAGESER,CyzUpdateAndSwitchToNew)
  80. #endif
  81. //removed in Win2000
  82. //#pragma alloc_text(PAGESER,CyzCompleteRead)
  83. //#pragma alloc_text(PAGESER,CyzReadTimeout)
  84. //#pragma alloc_text(PAGESER,CyzIntervalReadTimeout)
  85. NTSTATUS
  86. CyzRead(
  87. IN PDEVICE_OBJECT DeviceObject,
  88. IN PIRP Irp
  89. )
  90. /*--------------------------------------------------------------------------
  91. CyzRead()
  92. Routine Description: This is the dispatch routine for reading. It
  93. validates the parameters for the read request and if all is ok then
  94. it places the request on the work queue.
  95. Arguments:
  96. DeviceObject - Pointer to the device object for this device
  97. Irp - Pointer to the IRP for the current request
  98. Return Value:
  99. If the io is zero length then it will return STATUS_SUCCESS,
  100. otherwise this routine will return the status returned by
  101. the actual start read routine.
  102. --------------------------------------------------------------------------*/
  103. {
  104. PCYZ_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
  105. BOOLEAN acceptingIRPs;
  106. NTSTATUS status;
  107. CYZ_LOCKED_PAGED_CODE();
  108. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzRead(%X, %X)\n", DeviceObject,
  109. Irp);
  110. if ((status = CyzIRPPrologue(Irp, extension)) != STATUS_SUCCESS) {
  111. if(status != STATUS_PENDING) {
  112. CyzCompleteRequest(extension, Irp, IO_NO_INCREMENT);
  113. }
  114. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzRead (1) %X\n", status);
  115. return status;
  116. }
  117. CyzDbgPrintEx(CYZIRPPATH, "Dispatch entry for: %x\n", Irp);
  118. if (CyzCompleteIfError(DeviceObject, Irp) != STATUS_SUCCESS) {
  119. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzRead (2) %X\n",
  120. STATUS_CANCELLED);
  121. return STATUS_CANCELLED;
  122. }
  123. Irp->IoStatus.Information = 0L;
  124. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length) {
  125. //
  126. // Well it looks like we actually have to do some
  127. // work. Put the read on the queue so that we can
  128. // process it when our previous reads are done.
  129. //
  130. status = CyzStartOrQueue(extension, Irp, &extension->ReadQueue,
  131. &extension->CurrentReadIrp, CyzStartRead);
  132. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzRead (3) %X\n", status);
  133. return status;
  134. } else {
  135. Irp->IoStatus.Status = STATUS_SUCCESS;
  136. CyzCompleteRequest(extension, Irp, 0);
  137. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzRead (4) %X\n",
  138. STATUS_SUCCESS);
  139. return STATUS_SUCCESS;
  140. }
  141. }
  142. NTSTATUS
  143. CyzStartRead(
  144. IN PCYZ_DEVICE_EXTENSION Extension
  145. )
  146. /*--------------------------------------------------------------------------
  147. CyzStartRead()
  148. Routine Description: This routine is used to start off any read.
  149. It initializes the Iostatus fields of the irp. It will set up any
  150. timers that are used to control the read. It will attempt to
  151. complete the read from data already in the interrupt buffer. If the
  152. read can be completed quickly it will start off another if
  153. necessary.
  154. Arguments:
  155. Extension - Simply a pointer to the serial device extension.
  156. Return Value: This routine will return the status of the first read
  157. irp. This is useful in that if we have a read that can complete
  158. right away (AND there had been nothing in the queue before it) the
  159. read could return SUCCESS and the application won't have to do a wait.
  160. --------------------------------------------------------------------------*/
  161. {
  162. SERIAL_UPDATE_CHAR updateChar;
  163. PIRP newIrp;
  164. KIRQL oldIrql;
  165. KIRQL controlIrql;
  166. #ifdef POLL
  167. KIRQL pollIrql;
  168. #endif
  169. BOOLEAN returnWithWhatsPresent;
  170. BOOLEAN os2ssreturn;
  171. BOOLEAN crunchDownToOne;
  172. BOOLEAN useTotalTimer;
  173. BOOLEAN useIntervalTimer;
  174. ULONG multiplierVal;
  175. ULONG constantVal;
  176. LARGE_INTEGER totalTime;
  177. SERIAL_TIMEOUTS timeoutsForIrp;
  178. BOOLEAN setFirstStatus = FALSE;
  179. NTSTATUS firstStatus;
  180. CYZ_LOCKED_PAGED_CODE();
  181. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzStartRead(%X)\n", Extension);
  182. updateChar.Extension = Extension;
  183. do {
  184. // Check to see if this is a resize request. If it is
  185. // then go to a routine that specializes in that.
  186. if (IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
  187. ->MajorFunction != IRP_MJ_READ) {
  188. NTSTATUS localStatus = CyzResizeBuffer(Extension);
  189. if (!setFirstStatus) {
  190. firstStatus = localStatus;
  191. setFirstStatus = TRUE;
  192. }
  193. } else {
  194. Extension->NumberNeededForRead =
  195. IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
  196. ->Parameters.Read.Length;
  197. // Calculate the timeout value needed for the
  198. // request. Note that the values stored in the
  199. // timeout record are in milliseconds.
  200. useTotalTimer = FALSE;
  201. returnWithWhatsPresent = FALSE;
  202. os2ssreturn = FALSE;
  203. crunchDownToOne = FALSE;
  204. useIntervalTimer = FALSE;
  205. //
  206. // CIMEXCIMEX -- this is a lie
  207. //
  208. // Always initialize the timer objects so that the
  209. // completion code can tell when it attempts to
  210. // cancel the timers whether the timers had ever
  211. // been Set.
  212. //
  213. // CIMEXCIMEX -- this is the truth
  214. //
  215. // What we want to do is just make sure the timers are
  216. // cancelled to the best of our ability and move on with
  217. // life.
  218. //
  219. CyzCancelTimer(&Extension->ReadRequestTotalTimer, Extension);
  220. CyzCancelTimer(&Extension->ReadRequestIntervalTimer, Extension);
  221. // KeInitializeTimer(&Extension->ReadRequestTotalTimer);
  222. // KeInitializeTimer(&Extension->ReadRequestIntervalTimer);
  223. // We get the *current* timeout values to use for timing
  224. // this read.
  225. KeAcquireSpinLock(&Extension->ControlLock,&controlIrql);
  226. timeoutsForIrp = Extension->Timeouts;
  227. KeReleaseSpinLock(&Extension->ControlLock,controlIrql);
  228. // Calculate the interval timeout for the read.
  229. if (timeoutsForIrp.ReadIntervalTimeout &&
  230. (timeoutsForIrp.ReadIntervalTimeout !=
  231. MAXULONG)) {
  232. useIntervalTimer = TRUE;
  233. Extension->IntervalTime.QuadPart =
  234. UInt32x32To64(
  235. timeoutsForIrp.ReadIntervalTimeout,
  236. 10000
  237. );
  238. if (Extension->IntervalTime.QuadPart >=
  239. Extension->CutOverAmount.QuadPart) {
  240. Extension->IntervalTimeToUse =
  241. &Extension->LongIntervalAmount;
  242. } else {
  243. Extension->IntervalTimeToUse =
  244. &Extension->ShortIntervalAmount;
  245. }
  246. }
  247. if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG) {
  248. //
  249. // We need to do special return quickly stuff here.
  250. //
  251. // 1) If both constant and multiplier are
  252. // 0 then we return immediately with whatever
  253. // we've got, even if it was zero.
  254. //
  255. // 2) If constant and multiplier are not MAXULONG
  256. // then return immediately if any characters
  257. // are present, but if nothing is there, then
  258. // use the timeouts as specified.
  259. //
  260. // 3) If multiplier is MAXULONG then do as in
  261. // "2" but return when the first character
  262. // arrives.
  263. //
  264. if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
  265. !timeoutsForIrp.ReadTotalTimeoutMultiplier) {
  266. returnWithWhatsPresent = TRUE;
  267. } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  268. &&
  269. (timeoutsForIrp.ReadTotalTimeoutMultiplier
  270. != MAXULONG)) {
  271. useTotalTimer = TRUE;
  272. os2ssreturn = TRUE;
  273. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  274. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  275. } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  276. &&
  277. (timeoutsForIrp.ReadTotalTimeoutMultiplier
  278. == MAXULONG)) {
  279. useTotalTimer = TRUE;
  280. os2ssreturn = TRUE;
  281. crunchDownToOne = TRUE;
  282. multiplierVal = 0;
  283. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  284. }
  285. } else {
  286. //
  287. // If both the multiplier and the constant are
  288. // zero then don't do any total timeout processing.
  289. //
  290. if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
  291. timeoutsForIrp.ReadTotalTimeoutConstant) {
  292. //
  293. // We have some timer values to calculate.
  294. //
  295. useTotalTimer = TRUE;
  296. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  297. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  298. }
  299. }
  300. if (useTotalTimer) {
  301. totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
  302. Extension->NumberNeededForRead,
  303. multiplierVal
  304. )
  305. + constantVal))
  306. * -10000;
  307. }
  308. //
  309. // We do this copy in the hope of getting most (if not
  310. // all) of the characters out of the interrupt buffer.
  311. //
  312. // Note that we need to protect this operation with a
  313. // spinlock since we don't want a purge to hose us.
  314. //
  315. KeAcquireSpinLock(
  316. &Extension->ControlLock,
  317. &controlIrql
  318. );
  319. updateChar.CharsCopied = CyzGetCharsFromIntBuffer(Extension);
  320. //
  321. // See if we have any cause to return immediately.
  322. //
  323. if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
  324. (os2ssreturn &&
  325. Extension->CurrentReadIrp->IoStatus.Information)) {
  326. //
  327. // We got all we needed for this read.
  328. // Update the number of characters in the
  329. // interrupt read buffer.
  330. //
  331. #ifdef POLL
  332. KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
  333. CyzUpdateInterruptBuffer(&updateChar);
  334. KeReleaseSpinLock(&Extension->PollLock,pollIrql);
  335. #else
  336. KeSynchronizeExecution(
  337. Extension->Interrupt,
  338. CyzUpdateInterruptBuffer,
  339. &updateChar
  340. );
  341. #endif
  342. KeReleaseSpinLock(
  343. &Extension->ControlLock,
  344. controlIrql
  345. );
  346. Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
  347. if (!setFirstStatus) {
  348. firstStatus = STATUS_SUCCESS;
  349. setFirstStatus = TRUE;
  350. }
  351. } else {
  352. //
  353. // The irp might go under control of the isr. It
  354. // won't hurt to initialize the reference count
  355. // right now.
  356. //
  357. SERIAL_INIT_REFERENCE(Extension->CurrentReadIrp);
  358. IoAcquireCancelSpinLock(&oldIrql);
  359. //
  360. // We need to see if this irp should be canceled.
  361. //
  362. if (Extension->CurrentReadIrp->Cancel) {
  363. IoReleaseCancelSpinLock(oldIrql);
  364. KeReleaseSpinLock(
  365. &Extension->ControlLock,
  366. controlIrql
  367. );
  368. Extension->CurrentReadIrp->IoStatus.Status =
  369. STATUS_CANCELLED;
  370. Extension->CurrentReadIrp->IoStatus.Information = 0;
  371. if (!setFirstStatus) {
  372. firstStatus = STATUS_CANCELLED;
  373. setFirstStatus = TRUE;
  374. }
  375. } else {
  376. //
  377. // If we are supposed to crunch the read down to
  378. // one character, then update the read length
  379. // in the irp and truncate the number needed for
  380. // read down to one. Note that if we are doing
  381. // this crunching, then the information must be
  382. // zero (or we would have completed above) and
  383. // the number needed for the read must still be
  384. // equal to the read length.
  385. //
  386. if (crunchDownToOne) {
  387. ASSERT(
  388. (!Extension->CurrentReadIrp->IoStatus.Information)
  389. &&
  390. (Extension->NumberNeededForRead ==
  391. IoGetCurrentIrpStackLocation(
  392. Extension->CurrentReadIrp
  393. )->Parameters.Read.Length)
  394. );
  395. Extension->NumberNeededForRead = 1;
  396. IoGetCurrentIrpStackLocation(
  397. Extension->CurrentReadIrp
  398. )->Parameters.Read.Length = 1;
  399. }
  400. //
  401. // We still need to get more characters for this read.
  402. // synchronize with the isr so that we can update the
  403. // number of characters and if necessary it will have the
  404. // isr switch to copying into the users buffer.
  405. //
  406. #ifdef POLL
  407. KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
  408. CyzUpdateAndSwitchToUser(&updateChar);
  409. KeReleaseSpinLock(&Extension->PollLock,pollIrql);
  410. #else
  411. KeSynchronizeExecution(
  412. Extension->Interrupt,
  413. CyzUpdateAndSwitchToUser,
  414. &updateChar
  415. );
  416. #endif
  417. if (!updateChar.Completed) {
  418. //
  419. // The irp still isn't complete. The
  420. // completion routines will end up reinvoking
  421. // this routine. So we simply leave.
  422. //
  423. // First thought we should start off the total
  424. // timer for the read and increment the reference
  425. // count that the total timer has on the current
  426. // irp. Note that this is safe, because even if
  427. // the io has been satisfied by the isr it can't
  428. // complete yet because we still own the cancel
  429. // spinlock.
  430. //
  431. if (useTotalTimer) {
  432. SERIAL_SET_REFERENCE(
  433. Extension->CurrentReadIrp,
  434. SERIAL_REF_TOTAL_TIMER
  435. );
  436. CyzSetTimer(
  437. &Extension->ReadRequestTotalTimer,
  438. totalTime,
  439. &Extension->TotalReadTimeoutDpc,
  440. Extension
  441. );
  442. }
  443. if (useIntervalTimer) {
  444. SERIAL_SET_REFERENCE(
  445. Extension->CurrentReadIrp,
  446. SERIAL_REF_INT_TIMER
  447. );
  448. KeQuerySystemTime(
  449. &Extension->LastReadTime
  450. );
  451. CyzSetTimer(
  452. &Extension->ReadRequestIntervalTimer,
  453. *Extension->IntervalTimeToUse,
  454. &Extension->IntervalReadTimeoutDpc,
  455. Extension
  456. );
  457. }
  458. IoMarkIrpPending(Extension->CurrentReadIrp);
  459. IoReleaseCancelSpinLock(oldIrql);
  460. KeReleaseSpinLock(
  461. &Extension->ControlLock,
  462. controlIrql
  463. );
  464. if (!setFirstStatus) {
  465. firstStatus = STATUS_PENDING;
  466. }
  467. return firstStatus;
  468. } else {
  469. IoReleaseCancelSpinLock(oldIrql);
  470. KeReleaseSpinLock(
  471. &Extension->ControlLock,
  472. controlIrql
  473. );
  474. Extension->CurrentReadIrp->IoStatus.Status =
  475. STATUS_SUCCESS;
  476. if (!setFirstStatus) {
  477. firstStatus = STATUS_SUCCESS;
  478. setFirstStatus = TRUE;
  479. }
  480. }
  481. }
  482. }
  483. }
  484. //
  485. // Well the operation is complete.
  486. //
  487. CyzGetNextIrp(&Extension->CurrentReadIrp, &Extension->ReadQueue,
  488. &newIrp, TRUE, Extension);
  489. } while (newIrp);
  490. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzStartRead %X\n", firstStatus);
  491. return firstStatus;
  492. }
  493. VOID
  494. CyzCompleteRead(
  495. IN PKDPC Dpc,
  496. IN PVOID DeferredContext,
  497. IN PVOID SystemContext1,
  498. IN PVOID SystemContext2
  499. )
  500. /*--------------------------------------------------------------------------
  501. CyzCompleteRead()
  502. Routine Description: This routine is merely used to complete any read
  503. that ended up being used by the Isr. It assumes that the status and
  504. the information fields of the irp are already correctly filled in.
  505. Arguments:
  506. Dpc - Not Used.
  507. DeferredContext - Really points to the device extension.
  508. SystemContext1 - Not Used.
  509. SystemContext2 - Not Used.
  510. Return Value: None.
  511. --------------------------------------------------------------------------*/
  512. {
  513. PCYZ_DEVICE_EXTENSION extension = DeferredContext;
  514. KIRQL oldIrql;
  515. UNREFERENCED_PARAMETER(SystemContext1);
  516. UNREFERENCED_PARAMETER(SystemContext2);
  517. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzCompleteRead(%X)\n",
  518. extension);
  519. IoAcquireCancelSpinLock(&oldIrql);
  520. //
  521. // We set this to indicate to the interval timer
  522. // that the read has completed.
  523. //
  524. // Recall that the interval timer dpc can be lurking in some
  525. // DPC queue.
  526. //
  527. extension->CountOnLastRead = CYZ_COMPLETE_READ_COMPLETE;
  528. CyzTryToCompleteCurrent(
  529. extension,
  530. NULL,
  531. oldIrql,
  532. STATUS_SUCCESS,
  533. &extension->CurrentReadIrp,
  534. &extension->ReadQueue,
  535. &extension->ReadRequestIntervalTimer,
  536. &extension->ReadRequestTotalTimer,
  537. CyzStartRead,
  538. CyzGetNextIrp,
  539. SERIAL_REF_ISR
  540. );
  541. CyzDpcEpilogue(extension, Dpc);
  542. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzCompleteRead(%X)\n");
  543. }
  544. VOID
  545. CyzCancelCurrentRead(
  546. PDEVICE_OBJECT DeviceObject,
  547. PIRP Irp
  548. )
  549. /*++
  550. Routine Description:
  551. This routine is used to cancel the current read.
  552. Arguments:
  553. DeviceObject - Pointer to the device object for this device
  554. Irp - Pointer to the IRP to be canceled.
  555. Return Value:
  556. None.
  557. --*/
  558. {
  559. PCYZ_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
  560. CYZ_LOCKED_PAGED_CODE();
  561. //
  562. // We set this to indicate to the interval timer
  563. // that the read has encountered a cancel.
  564. //
  565. // Recall that the interval timer dpc can be lurking in some
  566. // DPC queue.
  567. //
  568. extension->CountOnLastRead = CYZ_COMPLETE_READ_CANCEL;
  569. CyzTryToCompleteCurrent(
  570. extension,
  571. CyzGrabReadFromIsr,
  572. Irp->CancelIrql,
  573. STATUS_CANCELLED,
  574. &extension->CurrentReadIrp,
  575. &extension->ReadQueue,
  576. &extension->ReadRequestIntervalTimer,
  577. &extension->ReadRequestTotalTimer,
  578. CyzStartRead,
  579. CyzGetNextIrp,
  580. SERIAL_REF_CANCEL
  581. );
  582. }
  583. BOOLEAN
  584. CyzGrabReadFromIsr(
  585. IN PVOID Context
  586. )
  587. /*++
  588. Routine Description:
  589. This routine is used to grab (if possible) the irp from the
  590. isr. If it finds that the isr still owns the irp it grabs
  591. the ipr away (updating the number of characters copied into the
  592. users buffer). If it grabs it away it also decrements the
  593. reference count on the irp since it no longer belongs to the
  594. isr (and the dpc that would complete it).
  595. NOTE: This routine assumes that if the current buffer that the
  596. ISR is copying characters into is the interrupt buffer then
  597. the dpc has already been queued.
  598. NOTE: This routine is being called from KeSynchronizeExecution.
  599. NOTE: This routine assumes that it is called with the cancel spin
  600. lock held.
  601. Arguments:
  602. Context - Really a pointer to the device extension.
  603. Return Value:
  604. Always false.
  605. --*/
  606. {
  607. PCYZ_DEVICE_EXTENSION extension = Context;
  608. CYZ_LOCKED_PAGED_CODE();
  609. if (extension->ReadBufferBase !=
  610. extension->InterruptReadBuffer) {
  611. //
  612. // We need to set the information to the number of characters
  613. // that the read wanted minus the number of characters that
  614. // didn't get read into the interrupt buffer.
  615. //
  616. extension->CurrentReadIrp->IoStatus.Information =
  617. IoGetCurrentIrpStackLocation(
  618. extension->CurrentReadIrp
  619. )->Parameters.Read.Length -
  620. ((extension->LastCharSlot - extension->CurrentCharSlot) + 1);
  621. //
  622. // Switch back to the interrupt buffer.
  623. //
  624. extension->ReadBufferBase = extension->InterruptReadBuffer;
  625. extension->CurrentCharSlot = extension->InterruptReadBuffer;
  626. extension->FirstReadableChar = extension->InterruptReadBuffer;
  627. extension->LastCharSlot = extension->InterruptReadBuffer +
  628. (extension->BufferSize - 1);
  629. extension->CharsInInterruptBuffer = 0;
  630. SERIAL_CLEAR_REFERENCE(
  631. extension->CurrentReadIrp,
  632. SERIAL_REF_ISR
  633. );
  634. }
  635. return FALSE;
  636. }
  637. VOID
  638. CyzReadTimeout(
  639. IN PKDPC Dpc,
  640. IN PVOID DeferredContext,
  641. IN PVOID SystemContext1,
  642. IN PVOID SystemContext2
  643. )
  644. /*++
  645. Routine Description:
  646. This routine is used to complete a read because its total
  647. timer has expired.
  648. Arguments:
  649. Dpc - Not Used.
  650. DeferredContext - Really points to the device extension.
  651. SystemContext1 - Not Used.
  652. SystemContext2 - Not Used.
  653. Return Value:
  654. None.
  655. --*/
  656. {
  657. PCYZ_DEVICE_EXTENSION extension = DeferredContext;
  658. KIRQL oldIrql;
  659. UNREFERENCED_PARAMETER(SystemContext1);
  660. UNREFERENCED_PARAMETER(SystemContext2);
  661. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzReadTimeout(%X)\n",
  662. extension);
  663. IoAcquireCancelSpinLock(&oldIrql);
  664. //
  665. // We set this to indicate to the interval timer
  666. // that the read has completed due to total timeout.
  667. //
  668. // Recall that the interval timer dpc can be lurking in some
  669. // DPC queue.
  670. //
  671. extension->CountOnLastRead = CYZ_COMPLETE_READ_TOTAL;
  672. CyzTryToCompleteCurrent(
  673. extension,
  674. CyzGrabReadFromIsr,
  675. oldIrql,
  676. STATUS_TIMEOUT,
  677. &extension->CurrentReadIrp,
  678. &extension->ReadQueue,
  679. &extension->ReadRequestIntervalTimer,
  680. &extension->ReadRequestTotalTimer,
  681. CyzStartRead,
  682. CyzGetNextIrp,
  683. SERIAL_REF_TOTAL_TIMER
  684. );
  685. CyzDpcEpilogue(extension, Dpc);
  686. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzReadTimeout\n");
  687. }
  688. BOOLEAN
  689. CyzUpdateReadByIsr(
  690. IN PVOID Context
  691. )
  692. /*++
  693. Routine Description:
  694. This routine is used to update the count of characters read
  695. by the isr since the last interval timer experation.
  696. NOTE: This routine is being called from KeSynchronizeExecution.
  697. NOTE: This routine assumes that it is called with the cancel spin
  698. lock held.
  699. Arguments:
  700. Context - Really a pointer to the device extension.
  701. Return Value:
  702. Always false.
  703. --*/
  704. {
  705. PCYZ_DEVICE_EXTENSION extension = Context;
  706. CYZ_LOCKED_PAGED_CODE();
  707. extension->CountOnLastRead = extension->ReadByIsr;
  708. extension->ReadByIsr = 0;
  709. return FALSE;
  710. }
  711. VOID
  712. CyzIntervalReadTimeout(
  713. IN PKDPC Dpc,
  714. IN PVOID DeferredContext,
  715. IN PVOID SystemContext1,
  716. IN PVOID SystemContext2
  717. )
  718. /*++
  719. Routine Description:
  720. This routine is used timeout the request if the time between
  721. characters exceed the interval time. A global is kept in
  722. the device extension that records the count of characters read
  723. the last the last time this routine was invoked (This dpc
  724. will resubmit the timer if the count has changed). If the
  725. count has not changed then this routine will attempt to complete
  726. the irp. Note the special case of the last count being zero.
  727. The timer isn't really in effect until the first character is
  728. read.
  729. Arguments:
  730. Dpc - Not Used.
  731. DeferredContext - Really points to the device extension.
  732. SystemContext1 - Not Used.
  733. SystemContext2 - Not Used.
  734. Return Value:
  735. None.
  736. --*/
  737. {
  738. PCYZ_DEVICE_EXTENSION extension = DeferredContext;
  739. KIRQL oldIrql;
  740. #ifdef POLL
  741. KIRQL pollIrql;
  742. #endif
  743. UNREFERENCED_PARAMETER(SystemContext1);
  744. UNREFERENCED_PARAMETER(SystemContext2);
  745. IoAcquireCancelSpinLock(&oldIrql);
  746. #if 0 //Removed for Hyperterminal
  747. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, ">CyzIntervalReadTimeout(%X)\n",
  748. extension);
  749. #endif
  750. if (extension->CountOnLastRead == CYZ_COMPLETE_READ_TOTAL) {
  751. //
  752. // This value is only set by the total
  753. // timer to indicate that it has fired.
  754. // If so, then we should simply try to complete.
  755. //
  756. CyzTryToCompleteCurrent(
  757. extension,
  758. CyzGrabReadFromIsr,
  759. oldIrql,
  760. STATUS_TIMEOUT,
  761. &extension->CurrentReadIrp,
  762. &extension->ReadQueue,
  763. &extension->ReadRequestIntervalTimer,
  764. &extension->ReadRequestTotalTimer,
  765. CyzStartRead,
  766. CyzGetNextIrp,
  767. SERIAL_REF_INT_TIMER
  768. );
  769. } else if (extension->CountOnLastRead == CYZ_COMPLETE_READ_COMPLETE) {
  770. //
  771. // This value is only set by the regular
  772. // completion routine.
  773. //
  774. // If so, then we should simply try to complete.
  775. //
  776. CyzTryToCompleteCurrent(
  777. extension,
  778. CyzGrabReadFromIsr,
  779. oldIrql,
  780. STATUS_SUCCESS,
  781. &extension->CurrentReadIrp,
  782. &extension->ReadQueue,
  783. &extension->ReadRequestIntervalTimer,
  784. &extension->ReadRequestTotalTimer,
  785. CyzStartRead,
  786. CyzGetNextIrp,
  787. SERIAL_REF_INT_TIMER
  788. );
  789. } else if (extension->CountOnLastRead == CYZ_COMPLETE_READ_CANCEL) {
  790. //
  791. // This value is only set by the cancel
  792. // read routine.
  793. //
  794. // If so, then we should simply try to complete.
  795. //
  796. CyzTryToCompleteCurrent(
  797. extension,
  798. CyzGrabReadFromIsr,
  799. oldIrql,
  800. STATUS_CANCELLED,
  801. &extension->CurrentReadIrp,
  802. &extension->ReadQueue,
  803. &extension->ReadRequestIntervalTimer,
  804. &extension->ReadRequestTotalTimer,
  805. CyzStartRead,
  806. CyzGetNextIrp,
  807. SERIAL_REF_INT_TIMER
  808. );
  809. } else if (extension->CountOnLastRead || extension->ReadByIsr) {
  810. //
  811. // Something has happened since we last came here. We
  812. // check to see if the ISR has read in any more characters.
  813. // If it did then we should update the isr's read count
  814. // and resubmit the timer.
  815. //
  816. if (extension->ReadByIsr) {
  817. #ifdef POLL
  818. KeAcquireSpinLock(&extension->PollLock,&pollIrql);
  819. CyzUpdateReadByIsr(extension);
  820. KeReleaseSpinLock(&extension->PollLock,pollIrql);
  821. #else
  822. KeSynchronizeExecution(
  823. extension->Interrupt,
  824. CyzUpdateReadByIsr,
  825. extension
  826. );
  827. #endif
  828. //
  829. // Save off the "last" time something was read.
  830. // As we come back to this routine we will compare
  831. // the current time to the "last" time. If the
  832. // difference is ever larger then the interval
  833. // requested by the user, then time out the request.
  834. //
  835. KeQuerySystemTime(
  836. &extension->LastReadTime
  837. );
  838. CyzSetTimer(
  839. &extension->ReadRequestIntervalTimer,
  840. *extension->IntervalTimeToUse,
  841. &extension->IntervalReadTimeoutDpc,
  842. extension
  843. );
  844. IoReleaseCancelSpinLock(oldIrql);
  845. } else {
  846. //
  847. // Take the difference between the current time
  848. // and the last time we had characters and
  849. // see if it is greater then the interval time.
  850. // if it is, then time out the request. Otherwise
  851. // go away again for a while.
  852. //
  853. //
  854. // No characters read in the interval time. Kill
  855. // this read.
  856. //
  857. LARGE_INTEGER currentTime;
  858. KeQuerySystemTime(
  859. &currentTime
  860. );
  861. if ((currentTime.QuadPart - extension->LastReadTime.QuadPart) >=
  862. extension->IntervalTime.QuadPart) {
  863. CyzTryToCompleteCurrent(
  864. extension,
  865. CyzGrabReadFromIsr,
  866. oldIrql,
  867. STATUS_TIMEOUT,
  868. &extension->CurrentReadIrp,
  869. &extension->ReadQueue,
  870. &extension->ReadRequestIntervalTimer,
  871. &extension->ReadRequestTotalTimer,
  872. CyzStartRead,
  873. CyzGetNextIrp,
  874. SERIAL_REF_INT_TIMER
  875. );
  876. } else {
  877. CyzSetTimer(
  878. &extension->ReadRequestIntervalTimer,
  879. *extension->IntervalTimeToUse,
  880. &extension->IntervalReadTimeoutDpc,
  881. extension
  882. );
  883. IoReleaseCancelSpinLock(oldIrql);
  884. }
  885. }
  886. } else {
  887. //
  888. // Timer doesn't really start until the first character.
  889. // So we should simply resubmit ourselves.
  890. //
  891. CyzSetTimer(
  892. &extension->ReadRequestIntervalTimer,
  893. *extension->IntervalTimeToUse,
  894. &extension->IntervalReadTimeoutDpc,
  895. extension
  896. );
  897. IoReleaseCancelSpinLock(oldIrql);
  898. }
  899. CyzDpcEpilogue(extension, Dpc);
  900. #if 0 //Removed for Hyperterminal
  901. CyzDbgPrintEx(DPFLTR_TRACE_LEVEL, "<CyzIntervalReadTimeout\n");
  902. #endif
  903. }
  904. ULONG
  905. CyzGetCharsFromIntBuffer(
  906. PCYZ_DEVICE_EXTENSION Extension
  907. )
  908. /*++
  909. Routine Description:
  910. This routine is used to copy any characters out of the interrupt
  911. buffer into the users buffer. It will be reading values that
  912. are updated with the ISR but this is safe since this value is
  913. only decremented by synchronization routines. This routine will
  914. return the number of characters copied so some other routine
  915. can call a synchronization routine to update what is seen at
  916. interrupt level.
  917. Arguments:
  918. Extension - A pointer to the device extension.
  919. Return Value:
  920. The number of characters that were copied into the user
  921. buffer.
  922. --*/
  923. {
  924. //
  925. // This value will be the number of characters that this
  926. // routine returns. It will be the minimum of the number
  927. // of characters currently in the buffer or the number of
  928. // characters required for the read.
  929. //
  930. ULONG numberOfCharsToGet;
  931. //
  932. // This holds the number of characters between the first
  933. // readable character and - the last character we will read or
  934. // the real physical end of the buffer (not the last readable
  935. // character).
  936. //
  937. ULONG firstTryNumberToGet;
  938. CYZ_LOCKED_PAGED_CODE();
  939. //
  940. // The minimum of the number of characters we need and
  941. // the number of characters available
  942. //
  943. numberOfCharsToGet = Extension->CharsInInterruptBuffer;
  944. if (numberOfCharsToGet > Extension->NumberNeededForRead) {
  945. numberOfCharsToGet = Extension->NumberNeededForRead;
  946. }
  947. if (numberOfCharsToGet) {
  948. //
  949. // This will hold the number of characters between the
  950. // first available character and the end of the buffer.
  951. // Note that the buffer could wrap around but for the
  952. // purposes of the first copy we don't care about that.
  953. //
  954. firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
  955. Extension->FirstReadableChar) + 1;
  956. if (firstTryNumberToGet > numberOfCharsToGet) {
  957. //
  958. // The characters don't wrap. Actually they may wrap but
  959. // we don't care for the purposes of this read since the
  960. // characters we need are available before the wrap.
  961. //
  962. RtlMoveMemory(
  963. ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
  964. + (IoGetCurrentIrpStackLocation(
  965. Extension->CurrentReadIrp
  966. )->Parameters.Read.Length
  967. - Extension->NumberNeededForRead
  968. ),
  969. Extension->FirstReadableChar,
  970. numberOfCharsToGet
  971. );
  972. Extension->NumberNeededForRead -= numberOfCharsToGet;
  973. //
  974. // We now will move the pointer to the first character after
  975. // what we just copied into the users buffer.
  976. //
  977. // We need to check if the stream of readable characters
  978. // is wrapping around to the beginning of the buffer.
  979. //
  980. // Note that we may have just taken the last characters
  981. // at the end of the buffer.
  982. //
  983. if ((Extension->FirstReadableChar + (numberOfCharsToGet - 1)) ==
  984. Extension->LastCharSlot) {
  985. Extension->FirstReadableChar = Extension->InterruptReadBuffer;
  986. } else {
  987. Extension->FirstReadableChar += numberOfCharsToGet;
  988. }
  989. } else {
  990. //
  991. // The characters do wrap. Get up until the end of the buffer.
  992. //
  993. RtlMoveMemory(
  994. ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
  995. + (IoGetCurrentIrpStackLocation(
  996. Extension->CurrentReadIrp
  997. )->Parameters.Read.Length
  998. - Extension->NumberNeededForRead
  999. ),
  1000. Extension->FirstReadableChar,
  1001. firstTryNumberToGet
  1002. );
  1003. Extension->NumberNeededForRead -= firstTryNumberToGet;
  1004. //
  1005. // Now get the rest of the characters from the beginning of the
  1006. // buffer.
  1007. //
  1008. RtlMoveMemory(
  1009. ((PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer))
  1010. + (IoGetCurrentIrpStackLocation(
  1011. Extension->CurrentReadIrp
  1012. )->Parameters.Read.Length
  1013. - Extension->NumberNeededForRead
  1014. ),
  1015. Extension->InterruptReadBuffer,
  1016. numberOfCharsToGet - firstTryNumberToGet
  1017. );
  1018. Extension->FirstReadableChar = Extension->InterruptReadBuffer +
  1019. (numberOfCharsToGet -
  1020. firstTryNumberToGet);
  1021. Extension->NumberNeededForRead -= (numberOfCharsToGet -
  1022. firstTryNumberToGet);
  1023. }
  1024. }
  1025. Extension->CurrentReadIrp->IoStatus.Information += numberOfCharsToGet;
  1026. return numberOfCharsToGet;
  1027. }
  1028. BOOLEAN
  1029. CyzUpdateInterruptBuffer(
  1030. IN PVOID Context
  1031. )
  1032. /*++
  1033. Routine Description:
  1034. This routine is used to update the number of characters that
  1035. remain in the interrupt buffer. We need to use this routine
  1036. since the count could be updated during the update by execution
  1037. of the ISR.
  1038. NOTE: This is called by KeSynchronizeExecution.
  1039. Arguments:
  1040. Context - Points to a structure that contains a pointer to the
  1041. device extension and count of the number of characters
  1042. that we previously copied into the users buffer. The
  1043. structure actually has a third field that we don't
  1044. use in this routine.
  1045. Return Value:
  1046. Always FALSE.
  1047. --*/
  1048. {
  1049. PSERIAL_UPDATE_CHAR update = Context;
  1050. PCYZ_DEVICE_EXTENSION extension = update->Extension;
  1051. CYZ_LOCKED_PAGED_CODE();
  1052. ASSERT(extension->CharsInInterruptBuffer >= update->CharsCopied);
  1053. extension->CharsInInterruptBuffer -= update->CharsCopied;
  1054. //
  1055. // Deal with flow control if necessary.
  1056. //
  1057. CyzHandleReducedIntBuffer(extension);
  1058. return FALSE;
  1059. }
  1060. BOOLEAN
  1061. CyzUpdateAndSwitchToUser(
  1062. IN PVOID Context
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. This routine gets the (hopefully) few characters that
  1067. remain in the interrupt buffer after the first time we tried
  1068. to get them out. If we still don't have enough characters
  1069. to satisfy the read it will then we set things up so that the
  1070. ISR uses the user buffer copy into.
  1071. This routine is also used to update a count that is maintained
  1072. by the ISR to keep track of the number of characters in its buffer.
  1073. NOTE: This is called by KeSynchronizeExecution.
  1074. Arguments:
  1075. Context - Points to a structure that contains a pointer to the
  1076. device extension, a count of the number of characters
  1077. that we previously copied into the users buffer, and
  1078. a boolean that we will set that defines whether we
  1079. switched the ISR to copy into the users buffer.
  1080. Return Value:
  1081. Always FALSE.
  1082. --*/
  1083. {
  1084. PSERIAL_UPDATE_CHAR updateChar = Context;
  1085. PCYZ_DEVICE_EXTENSION extension = updateChar->Extension;
  1086. CYZ_LOCKED_PAGED_CODE();
  1087. CyzUpdateInterruptBuffer(Context);
  1088. //
  1089. // There are more characters to get to satisfy this read.
  1090. // Copy any characters that have arrived since we got
  1091. // the last batch.
  1092. //
  1093. updateChar->CharsCopied = CyzGetCharsFromIntBuffer(extension);
  1094. CyzUpdateInterruptBuffer(Context);
  1095. //
  1096. // No more new characters will be "received" until we exit
  1097. // this routine. We again check to make sure that we
  1098. // haven't satisfied this read, and if we haven't we set things
  1099. // up so that the ISR copies into the user buffer.
  1100. //
  1101. if (extension->NumberNeededForRead) {
  1102. //
  1103. // We shouldn't be switching unless there are no
  1104. // characters left.
  1105. //
  1106. ASSERT(!extension->CharsInInterruptBuffer);
  1107. //
  1108. // We use the following to values to do inteval timing.
  1109. //
  1110. // CountOnLastRead is mostly used to simply prevent
  1111. // the interval timer from timing out before any characters
  1112. // are read. (Interval timing should only be effective
  1113. // after the first character is read.)
  1114. //
  1115. // After the first time the interval timer fires and
  1116. // characters have be read we will simply update with
  1117. // the value of ReadByIsr and then set ReadByIsr to zero.
  1118. // (We do that in a synchronization routine.
  1119. //
  1120. // If the interval timer dpc routine ever encounters
  1121. // ReadByIsr == 0 when CountOnLastRead is non-zero it
  1122. // will timeout the read.
  1123. //
  1124. // (Note that we have a special case of CountOnLastRead
  1125. // < 0. This is done by the read completion routines other
  1126. // than the total timeout dpc to indicate that the total
  1127. // timeout has expired.)
  1128. //
  1129. extension->CountOnLastRead =
  1130. (LONG)extension->CurrentReadIrp->IoStatus.Information;
  1131. extension->ReadByIsr = 0;
  1132. //
  1133. // By compareing the read buffer base address to the
  1134. // the base address of the interrupt buffer the ISR
  1135. // can determine whether we are using the interrupt
  1136. // buffer or the user buffer.
  1137. //
  1138. extension->ReadBufferBase =
  1139. extension->CurrentReadIrp->AssociatedIrp.SystemBuffer;
  1140. //
  1141. // The current char slot is after the last copied in
  1142. // character. We know there is always room since we
  1143. // we wouldn't have gotten here if there wasn't.
  1144. //
  1145. extension->CurrentCharSlot = extension->ReadBufferBase +
  1146. extension->CurrentReadIrp->IoStatus.Information;
  1147. //
  1148. // The last position that a character can go is on the
  1149. // last byte of user buffer. While the actual allocated
  1150. // buffer space may be bigger, we know that there is at
  1151. // least as much as the read length.
  1152. //
  1153. extension->LastCharSlot = extension->ReadBufferBase +
  1154. (IoGetCurrentIrpStackLocation(
  1155. extension->CurrentReadIrp
  1156. )->Parameters.Read.Length
  1157. - 1);
  1158. //
  1159. // Mark the irp as being in a cancelable state.
  1160. //
  1161. IoSetCancelRoutine(
  1162. extension->CurrentReadIrp,
  1163. CyzCancelCurrentRead
  1164. );
  1165. //
  1166. // Increment the reference count twice.
  1167. //
  1168. // Once for the Isr owning the irp and once
  1169. // because the cancel routine has a reference
  1170. // to it.
  1171. //
  1172. SERIAL_SET_REFERENCE(
  1173. extension->CurrentReadIrp,
  1174. SERIAL_REF_ISR
  1175. );
  1176. SERIAL_SET_REFERENCE(
  1177. extension->CurrentReadIrp,
  1178. SERIAL_REF_CANCEL
  1179. );
  1180. updateChar->Completed = FALSE;
  1181. } else {
  1182. updateChar->Completed = TRUE;
  1183. }
  1184. return FALSE;
  1185. }
  1186. //
  1187. // We use this structure only to communicate to the synchronization
  1188. // routine when we are switching to the resized buffer.
  1189. //
  1190. typedef struct _SERIAL_RESIZE_PARAMS {
  1191. PCYZ_DEVICE_EXTENSION Extension;
  1192. PUCHAR OldBuffer;
  1193. PUCHAR NewBuffer;
  1194. ULONG NewBufferSize;
  1195. ULONG NumberMoved;
  1196. } SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
  1197. NTSTATUS
  1198. CyzResizeBuffer(
  1199. IN PCYZ_DEVICE_EXTENSION Extension
  1200. )
  1201. /*++
  1202. Routine Description:
  1203. This routine will process the resize buffer request.
  1204. If size requested for the RX buffer is smaller than
  1205. the current buffer then we will simply return
  1206. STATUS_SUCCESS. (We don't want to make buffers smaller.
  1207. If we did that then we all of a sudden have "overrun"
  1208. problems to deal with as well as flow control to deal
  1209. with - very painful.) We ignore the TX buffer size
  1210. request since we don't use a TX buffer.
  1211. Arguments:
  1212. Extension - Pointer to the device extension for the port.
  1213. Return Value:
  1214. STATUS_SUCCESS if everything worked out ok.
  1215. STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
  1216. memory for the buffer.
  1217. --*/
  1218. {
  1219. PSERIAL_QUEUE_SIZE rs = Extension->CurrentReadIrp->AssociatedIrp
  1220. .SystemBuffer;
  1221. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(
  1222. Extension->CurrentReadIrp
  1223. );
  1224. PVOID newBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
  1225. CYZ_LOCKED_PAGED_CODE();
  1226. irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  1227. Extension->CurrentReadIrp->IoStatus.Information = 0L;
  1228. Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
  1229. if (rs->InSize <= Extension->BufferSize) {
  1230. //
  1231. // Nothing to do. We don't make buffers smaller. Just
  1232. // agree with the user. We must deallocate the memory
  1233. // that was already allocated in the ioctl dispatch routine.
  1234. //
  1235. ExFreePool(newBuffer);
  1236. } else {
  1237. SERIAL_RESIZE_PARAMS rp;
  1238. KIRQL controlIrql;
  1239. #ifdef POLL
  1240. KIRQL pollIrql;
  1241. #endif
  1242. //
  1243. // Hmmm, looks like we actually have to go
  1244. // through with this. We need to move all the
  1245. // data that is in the current buffer into this
  1246. // new buffer. We'll do this in two steps.
  1247. //
  1248. // First we go up to dispatch level and try to
  1249. // move as much as we can without stopping the
  1250. // ISR from running. We go up to dispatch level
  1251. // by acquiring the control lock. We do it at
  1252. // dispatch using the control lock so that:
  1253. //
  1254. // 1) We can't be context switched in the middle
  1255. // of the move. Our pointers into the buffer
  1256. // could be *VERY* stale by the time we got back.
  1257. //
  1258. // 2) We use the control lock since we don't want
  1259. // some pesky purge irp to come along while
  1260. // we are trying to move.
  1261. //
  1262. // After the move, but while we still hold the control
  1263. // lock, we synch with the ISR and get those last
  1264. // (hopefully) few characters that have come in since
  1265. // we started the copy. We switch all of our pointers,
  1266. // counters, and such to point to this new buffer. NOTE:
  1267. // we need to be careful. If the buffer we were using
  1268. // was not the default one created when we initialized
  1269. // the device (i.e. it was created via a previous IRP of
  1270. // this type), we should deallocate it.
  1271. //
  1272. rp.Extension = Extension;
  1273. rp.OldBuffer = Extension->InterruptReadBuffer;
  1274. rp.NewBuffer = newBuffer;
  1275. rp.NewBufferSize = rs->InSize;
  1276. KeAcquireSpinLock(
  1277. &Extension->ControlLock,
  1278. &controlIrql
  1279. );
  1280. rp.NumberMoved = CyzMoveToNewIntBuffer(
  1281. Extension,
  1282. newBuffer
  1283. );
  1284. #ifdef POLL
  1285. KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
  1286. CyzUpdateAndSwitchToNew(&rp);
  1287. KeReleaseSpinLock(&Extension->PollLock,pollIrql);
  1288. #else
  1289. KeSynchronizeExecution(
  1290. Extension->Interrupt,
  1291. CyzUpdateAndSwitchToNew,
  1292. &rp
  1293. );
  1294. #endif
  1295. KeReleaseSpinLock(
  1296. &Extension->ControlLock,
  1297. controlIrql
  1298. );
  1299. //
  1300. // Free up the memory that the old buffer consumed.
  1301. //
  1302. ExFreePool(rp.OldBuffer);
  1303. }
  1304. return STATUS_SUCCESS;
  1305. }
  1306. ULONG
  1307. CyzMoveToNewIntBuffer(
  1308. PCYZ_DEVICE_EXTENSION Extension,
  1309. PUCHAR NewBuffer
  1310. )
  1311. /*++
  1312. Routine Description:
  1313. This routine is used to copy any characters out of the interrupt
  1314. buffer into the "new" buffer. It will be reading values that
  1315. are updated with the ISR but this is safe since this value is
  1316. only decremented by synchronization routines. This routine will
  1317. return the number of characters copied so some other routine
  1318. can call a synchronization routine to update what is seen at
  1319. interrupt level.
  1320. Arguments:
  1321. Extension - A pointer to the device extension.
  1322. NewBuffer - Where the characters are to be move to.
  1323. Return Value:
  1324. The number of characters that were copied into the user
  1325. buffer.
  1326. --*/
  1327. {
  1328. ULONG numberOfCharsMoved = Extension->CharsInInterruptBuffer;
  1329. CYZ_LOCKED_PAGED_CODE();
  1330. if (numberOfCharsMoved) {
  1331. //
  1332. // This holds the number of characters between the first
  1333. // readable character and the last character we will read or
  1334. // the real physical end of the buffer (not the last readable
  1335. // character).
  1336. //
  1337. ULONG firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
  1338. Extension->FirstReadableChar) + 1;
  1339. if (firstTryNumberToGet >= numberOfCharsMoved) {
  1340. //
  1341. // The characters don't wrap.
  1342. //
  1343. RtlMoveMemory(
  1344. NewBuffer,
  1345. Extension->FirstReadableChar,
  1346. numberOfCharsMoved
  1347. );
  1348. if ((Extension->FirstReadableChar+(numberOfCharsMoved-1)) ==
  1349. Extension->LastCharSlot) {
  1350. Extension->FirstReadableChar = Extension->InterruptReadBuffer;
  1351. } else {
  1352. Extension->FirstReadableChar += numberOfCharsMoved;
  1353. }
  1354. } else {
  1355. //
  1356. // The characters do wrap. Get up until the end of the buffer.
  1357. //
  1358. RtlMoveMemory(
  1359. NewBuffer,
  1360. Extension->FirstReadableChar,
  1361. firstTryNumberToGet
  1362. );
  1363. //
  1364. // Now get the rest of the characters from the beginning of the
  1365. // buffer.
  1366. //
  1367. RtlMoveMemory(
  1368. NewBuffer+firstTryNumberToGet,
  1369. Extension->InterruptReadBuffer,
  1370. numberOfCharsMoved - firstTryNumberToGet
  1371. );
  1372. Extension->FirstReadableChar = Extension->InterruptReadBuffer +
  1373. numberOfCharsMoved - firstTryNumberToGet;
  1374. }
  1375. }
  1376. return numberOfCharsMoved;
  1377. }
  1378. BOOLEAN
  1379. CyzUpdateAndSwitchToNew(
  1380. IN PVOID Context
  1381. )
  1382. /*++
  1383. Routine Description:
  1384. This routine gets the (hopefully) few characters that
  1385. remain in the interrupt buffer after the first time we tried
  1386. to get them out.
  1387. NOTE: This is called by KeSynchronizeExecution.
  1388. Arguments:
  1389. Context - Points to a structure that contains a pointer to the
  1390. device extension, a pointer to the buffer we are moving
  1391. to, and a count of the number of characters
  1392. that we previously copied into the new buffer, and the
  1393. actual size of the new buffer.
  1394. Return Value:
  1395. Always FALSE.
  1396. --*/
  1397. {
  1398. PSERIAL_RESIZE_PARAMS params = Context;
  1399. PCYZ_DEVICE_EXTENSION extension = params->Extension;
  1400. ULONG tempCharsInInterruptBuffer = extension->CharsInInterruptBuffer;
  1401. CYZ_LOCKED_PAGED_CODE();
  1402. ASSERT(extension->CharsInInterruptBuffer >= params->NumberMoved);
  1403. //
  1404. // We temporarily reduce the chars in interrupt buffer to
  1405. // "fool" the move routine. We will restore it after the
  1406. // move.
  1407. //
  1408. extension->CharsInInterruptBuffer -= params->NumberMoved;
  1409. if (extension->CharsInInterruptBuffer) {
  1410. CyzMoveToNewIntBuffer(
  1411. extension,
  1412. params->NewBuffer + params->NumberMoved
  1413. );
  1414. }
  1415. extension->CharsInInterruptBuffer = tempCharsInInterruptBuffer;
  1416. extension->LastCharSlot = params->NewBuffer + (params->NewBufferSize - 1);
  1417. extension->FirstReadableChar = params->NewBuffer;
  1418. extension->ReadBufferBase = params->NewBuffer;
  1419. extension->InterruptReadBuffer = params->NewBuffer;
  1420. extension->BufferSize = params->NewBufferSize;
  1421. //
  1422. // We *KNOW* that the new interrupt buffer is larger than the
  1423. // old buffer. We don't need to worry about it being full.
  1424. //
  1425. extension->CurrentCharSlot = extension->InterruptReadBuffer +
  1426. extension->CharsInInterruptBuffer;
  1427. //
  1428. // We set up the default xon/xoff limits.
  1429. //
  1430. extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
  1431. extension->HandFlow.XonLimit = extension->BufferSize >> 1;
  1432. extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
  1433. extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
  1434. extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
  1435. (extension->BufferSize>>4));
  1436. //
  1437. // Since we (essentially) reduced the percentage of the interrupt
  1438. // buffer being full, we need to handle any flow control.
  1439. //
  1440. CyzHandleReducedIntBuffer(extension);
  1441. return FALSE;
  1442. }
  1443.