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.

1887 lines
55 KiB

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