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.

1048 lines
36 KiB

  1. /*-------------------------------------------------------------------
  2. | read.c -
  3. 1-22-99 - add missing IoReleaseCancelSpinLock(oldIrql) to CompleteRead().
  4. Error introduced after V3.23. kpb
  5. 1-18-99 - adjust VS timeout settings., take out some old #ifdef's. kpb.
  6. 3-23-98 - adjust VS so we have minimum per-character timeout value to
  7. compensate for vs networking.
  8. 3-04-98 Beef up synch locks with isr service routine(blue-screens on MP systems). kpb.
  9. 3-04-98 Take out data move from inter-character timer processing - kpb.
  10. 9-22-97 V1.16 - add check to avoid crash on modem detection.
  11. Copyright 1993-98 Comtrol Corporation. All rights reserved.
  12. |--------------------------------------------------------------------*/
  13. #include "precomp.h"
  14. // #define TIMEOUT_TRACING
  15. // #define TESTING_READ 1
  16. //--- local funcs
  17. VOID SerialCancelCurrentRead(PDEVICE_OBJECT DeviceObject, PIRP Irp);
  18. BOOLEAN SerialGrabReadFromIsr(PSERIAL_DEVICE_EXTENSION Extension);
  19. /*************************************************************************
  20. Routine Description:
  21. This is the dispatch routine for reading. It validates the parameters
  22. for the read request and if all is ok then it places the request
  23. on the work queue.
  24. Arguments:
  25. DeviceObject - Pointer to the device object for this device
  26. Irp - Pointer to the IRP for the current request
  27. Return Value:
  28. If the io is zero length then it will return STATUS_SUCCESS,
  29. otherwise this routine will return the status returned by
  30. the actual start read routine.
  31. *************************************************************************/
  32. NTSTATUS
  33. SerialRead(
  34. IN PDEVICE_OBJECT DeviceObject,
  35. IN PIRP Irp
  36. )
  37. {
  38. PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
  39. NTSTATUS Status;
  40. BOOLEAN acceptingIRPs;
  41. acceptingIRPs = SerialIRPPrologue(extension);
  42. if (acceptingIRPs == FALSE) {
  43. Irp->IoStatus.Information = 0;
  44. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  45. SerialCompleteRequest(extension, Irp, IO_NO_INCREMENT);
  46. return STATUS_NO_SUCH_DEVICE;
  47. };
  48. if (extension->DeviceType == DEV_BOARD)
  49. {
  50. Irp->IoStatus.Information = 0;
  51. Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  52. SerialCompleteRequest (extension, Irp, IO_NO_INCREMENT);
  53. return STATUS_NOT_SUPPORTED;
  54. };
  55. ExtTrace1(extension,D_Ioctl,"Read Start Len:%d",
  56. IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length);
  57. if (extension->ErrorWord)
  58. {
  59. if (SerialCompleteIfError( DeviceObject, Irp ) != STATUS_SUCCESS)
  60. {
  61. ExtTrace(extension,D_Ioctl,"ErrSet!");
  62. return STATUS_CANCELLED;
  63. }
  64. }
  65. Irp->IoStatus.Information = 0L;
  66. // If this is a zero length read then we are already done.
  67. if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length)
  68. {
  69. // Put the read on the queue so that we can
  70. // process it when our previous reads are done.
  71. ++extension->rec_packets;
  72. Status = SerialStartOrQueue(
  73. extension,
  74. Irp,
  75. &extension->ReadQueue,
  76. &extension->CurrentReadIrp,
  77. SerialStartRead
  78. );
  79. if (Status == STATUS_PENDING)
  80. {
  81. ExtTrace(extension,D_Ioctl, " ,PENDING");
  82. }
  83. else
  84. {
  85. ExtTrace1(extension,D_Ioctl,"Read Return Status:%d",Status);
  86. }
  87. return Status;
  88. }
  89. else
  90. {
  91. // Nothing to do, return success
  92. Irp->IoStatus.Status = STATUS_SUCCESS;
  93. SerialCompleteRequest(extension, Irp, 0);
  94. return STATUS_SUCCESS;
  95. }
  96. }
  97. /*************************************************************************
  98. Routine Description:
  99. This routine is used to start off any read. It initializes
  100. the Iostatus fields of the irp. It will set up any timers
  101. that are used to control the read. It will attempt to complete
  102. the read from data already in the interrupt buffer. If the
  103. read can be completed quickly it will start off another if
  104. necessary.
  105. Arguments:
  106. Extension - Simply a pointer to the serial device extension.
  107. Return Value:
  108. This routine will return the status of the first read
  109. irp. This is useful in that if we have a read that can
  110. complete right away (AND there had been nothing in the
  111. queue before it) the read could return SUCCESS and the
  112. application won't have to do a wait.
  113. *************************************************************************/
  114. NTSTATUS
  115. SerialStartRead(
  116. IN PSERIAL_DEVICE_EXTENSION Extension
  117. )
  118. {
  119. PIRP newIrp;
  120. KIRQL oldIrql;
  121. KIRQL controlIrql;
  122. BOOLEAN returnWithWhatsPresent;
  123. BOOLEAN os2ssreturn;
  124. BOOLEAN crunchDownToOne;
  125. BOOLEAN useTotalTimer;
  126. BOOLEAN useIntervalTimer;
  127. ULONG multiplierVal;
  128. ULONG constantVal;
  129. LARGE_INTEGER totalTime;
  130. SERIAL_TIMEOUTS timeoutsForIrp;
  131. BOOLEAN setFirstStatus = FALSE;
  132. NTSTATUS firstStatus;
  133. do
  134. {
  135. Extension->NumberNeededForRead =
  136. IoGetCurrentIrpStackLocation(Extension->CurrentReadIrp)
  137. ->Parameters.Read.Length;
  138. // Calculate the timeout value needed for the
  139. // request. Note that the values stored in the
  140. // timeout record are in milliseconds.
  141. useTotalTimer = FALSE;
  142. returnWithWhatsPresent = FALSE;
  143. os2ssreturn = FALSE;
  144. crunchDownToOne = FALSE;
  145. useIntervalTimer = FALSE;
  146. // Always initialize the timer objects so that the
  147. // completion code can tell when it attempts to
  148. // cancel the timers whether the timers had ever
  149. // been set.
  150. KeInitializeTimer(&Extension->ReadRequestTotalTimer);
  151. KeInitializeTimer(&Extension->ReadRequestIntervalTimer);
  152. // We get the *current* timeout values to use for timing
  153. // this read.
  154. KeAcquireSpinLock(&Extension->ControlLock, &controlIrql);
  155. timeoutsForIrp = Extension->Timeouts;
  156. KeReleaseSpinLock(&Extension->ControlLock, controlIrql);
  157. // Calculate the interval timeout for the read.
  158. if (timeoutsForIrp.ReadIntervalTimeout &&
  159. (timeoutsForIrp.ReadIntervalTimeout !=
  160. MAXULONG))
  161. {
  162. useIntervalTimer = TRUE;
  163. Extension->IntervalTime.QuadPart =
  164. UInt32x32To64(
  165. timeoutsForIrp.ReadIntervalTimeout,
  166. 10000
  167. );
  168. #ifdef S_VS
  169. // if they are using a per-character timeout of less
  170. // than 100ms, then change it to 100ms due to possible
  171. // network latencies.
  172. if (Extension->IntervalTime.QuadPart < (10000 * 100))
  173. {
  174. ExtTrace(Extension,D_Ioctl,"Adjust mintime");
  175. Extension->IntervalTime.QuadPart = (10000 * 100);
  176. }
  177. #endif
  178. if (Extension->IntervalTime.QuadPart >=
  179. Extension->CutOverAmount.QuadPart) {
  180. Extension->IntervalTimeToUse =
  181. &Extension->LongIntervalAmount;
  182. } else {
  183. Extension->IntervalTimeToUse =
  184. &Extension->ShortIntervalAmount;
  185. }
  186. }
  187. if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG)
  188. {
  189. // We need to do special return quickly stuff here.
  190. // 1) If both constant and multiplier are
  191. // 0 then we return immediately with whatever
  192. // we've got, even if it was zero.
  193. // 2) If constant and multiplier are not MAXULONG
  194. // then return immediately if any characters
  195. // are present, but if nothing is there, then
  196. // use the timeouts as specified.
  197. // 3) If multiplier is MAXULONG then do as in
  198. // "2" but return when the first character
  199. // arrives.
  200. if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
  201. !timeoutsForIrp.ReadTotalTimeoutMultiplier)
  202. {
  203. returnWithWhatsPresent = TRUE;
  204. }
  205. else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  206. &&
  207. (timeoutsForIrp.ReadTotalTimeoutMultiplier
  208. != MAXULONG))
  209. {
  210. useTotalTimer = TRUE;
  211. os2ssreturn = TRUE;
  212. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  213. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  214. }
  215. else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  216. &&
  217. (timeoutsForIrp.ReadTotalTimeoutMultiplier
  218. == MAXULONG))
  219. {
  220. useTotalTimer = TRUE;
  221. os2ssreturn = TRUE;
  222. crunchDownToOne = TRUE;
  223. multiplierVal = 0;
  224. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  225. }
  226. }
  227. else
  228. {
  229. // If both the multiplier and the constant are
  230. // zero then don't do any total timeout processing.
  231. if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
  232. timeoutsForIrp.ReadTotalTimeoutConstant) {
  233. // We have some timer values to calculate.
  234. useTotalTimer = TRUE;
  235. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  236. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  237. }
  238. }
  239. if (useTotalTimer)
  240. {
  241. totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
  242. Extension->NumberNeededForRead,
  243. multiplierVal
  244. )
  245. + constantVal))
  246. ;
  247. #ifdef S_VS
  248. if (totalTime.QuadPart < 50)
  249. {
  250. totalTime.QuadPart = 50; // limit to a minimum of 50ms timeout
  251. }
  252. #endif
  253. totalTime.QuadPart *= -10000;
  254. }
  255. // Move any data in the interrupt buffer to the user buffer.
  256. // Try to satisfy the current read irp.
  257. // Use spinlock so a purge will not cause problems.
  258. KeAcquireSpinLock(&Extension->ControlLock, &controlIrql);
  259. // Move the data from the host side buffer to the user buffer
  260. // This is the "first" move so assign CountOnLastRead
  261. Extension->CountOnLastRead = SerialGetCharsFromIntBuffer(Extension);
  262. // Init the timeout flag
  263. Extension->ReadByIsr = 0;
  264. // See if we have any cause to return immediately.
  265. if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
  266. (os2ssreturn && Extension->CurrentReadIrp->IoStatus.Information))
  267. {
  268. // We got all we needed for this read.
  269. KeReleaseSpinLock(&Extension->ControlLock, controlIrql);
  270. #ifdef TRACE_PORT
  271. if (Extension->TraceOptions)
  272. {
  273. if (Extension->TraceOptions & 1) // event tracing
  274. {
  275. ExtTrace1(Extension,D_Read,"Immed. Read Done, size:%d",
  276. Extension->CurrentReadIrp->IoStatus.Information);
  277. // dump data into the trace buffer in a hex or ascii dump format
  278. TraceDump(Extension,
  279. Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer,
  280. Extension->CurrentReadIrp->IoStatus.Information, 0);
  281. }
  282. else if (Extension->TraceOptions & 2) // trace input data
  283. {
  284. TracePut(
  285. Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer,
  286. Extension->CurrentReadIrp->IoStatus.Information);
  287. }
  288. }
  289. #endif
  290. Extension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
  291. if (!setFirstStatus)
  292. {
  293. firstStatus = STATUS_SUCCESS;
  294. setFirstStatus = TRUE;
  295. }
  296. }
  297. else // not return with what we have
  298. {
  299. MyKdPrint(D_Read,("Read Pending\n"))
  300. // The irp may go under control of the isr.
  301. // Initialize the reference count
  302. SERIAL_INIT_REFERENCE(Extension->CurrentReadIrp);
  303. IoAcquireCancelSpinLock(&oldIrql);
  304. // We need to see if this irp should be canceled.
  305. if (Extension->CurrentReadIrp->Cancel)
  306. {
  307. IoReleaseCancelSpinLock(oldIrql);
  308. KeReleaseSpinLock(&Extension->ControlLock, controlIrql);
  309. Extension->CurrentReadIrp->IoStatus.Status =
  310. STATUS_CANCELLED;
  311. Extension->CurrentReadIrp->IoStatus.Information = 0;
  312. if (!setFirstStatus)
  313. {
  314. firstStatus = STATUS_CANCELLED;
  315. setFirstStatus = TRUE;
  316. }
  317. }
  318. else
  319. {
  320. // If we are supposed to crunch the read down to
  321. // one character, then update the read length
  322. // in the irp and truncate the number needed for
  323. // read down to one. Note that if we are doing
  324. // this crunching, then the information must be
  325. // zero (or we would have completed above) and
  326. // the number needed for the read must still be
  327. // equal to the read length.
  328. //
  329. if (crunchDownToOne)
  330. {
  331. Extension->NumberNeededForRead = 1;
  332. IoGetCurrentIrpStackLocation(
  333. Extension->CurrentReadIrp
  334. )->Parameters.Read.Length = 1;
  335. }
  336. // Is this irp complete?
  337. if (Extension->NumberNeededForRead)
  338. {
  339. // The irp isn't complete, the ISR or timeout
  340. // will start the completion routines and
  341. // invoke this code again to finish.
  342. // Total supervisory read time.
  343. if (useTotalTimer)
  344. {
  345. SERIAL_SET_REFERENCE(
  346. Extension->CurrentReadIrp,
  347. SERIAL_REF_TOTAL_TIMER
  348. );
  349. // Start off the total timer
  350. KeSetTimer(
  351. &Extension->ReadRequestTotalTimer,
  352. totalTime,
  353. &Extension->TotalReadTimeoutDpc
  354. );
  355. }
  356. // Inter-character timer
  357. if(useIntervalTimer)
  358. {
  359. SERIAL_SET_REFERENCE(
  360. Extension->CurrentReadIrp,
  361. SERIAL_REF_INT_TIMER
  362. );
  363. KeQuerySystemTime(
  364. &Extension->LastReadTime
  365. );
  366. KeSetTimer(
  367. &Extension->ReadRequestIntervalTimer,
  368. *Extension->IntervalTimeToUse,
  369. &Extension->IntervalReadTimeoutDpc
  370. );
  371. }
  372. SERIAL_SET_REFERENCE(Extension->CurrentReadIrp,
  373. SERIAL_REF_CANCEL);
  374. IoMarkIrpPending(Extension->CurrentReadIrp);
  375. IoSetCancelRoutine(
  376. Extension->CurrentReadIrp,
  377. SerialCancelCurrentRead
  378. );
  379. SERIAL_SET_REFERENCE(Extension->CurrentReadIrp,
  380. SERIAL_REF_ISR);
  381. // tell ISR to complete it.
  382. Extension->ReadPending = TRUE;
  383. IoReleaseCancelSpinLock(oldIrql);
  384. KeReleaseSpinLock(&Extension->ControlLock, controlIrql);
  385. if (!setFirstStatus)
  386. {
  387. firstStatus = STATUS_PENDING;
  388. }
  389. return firstStatus;
  390. }
  391. else
  392. {
  393. IoReleaseCancelSpinLock(oldIrql);
  394. KeReleaseSpinLock(&Extension->ControlLock,controlIrql);
  395. Extension->CurrentReadIrp->IoStatus.Status =
  396. STATUS_SUCCESS;
  397. if (!setFirstStatus) {
  398. firstStatus = STATUS_SUCCESS;
  399. setFirstStatus = TRUE;
  400. }
  401. } // irp not complete
  402. } // not canceled
  403. } // not return with what we have
  404. // The current irp is complete, try to get another one.
  405. SerialGetNextIrp(
  406. &Extension->CurrentReadIrp,
  407. &Extension->ReadQueue,
  408. &newIrp,
  409. TRUE,
  410. Extension
  411. );
  412. } while (newIrp);
  413. return firstStatus;
  414. }
  415. /*------------------------------------------------------------------------
  416. trace_read_data - used to trace completion of read irp.
  417. |------------------------------------------------------------------------*/
  418. void trace_read_data(PSERIAL_DEVICE_EXTENSION extension)
  419. {
  420. if (extension->TraceOptions & 1) // event tracing
  421. {
  422. ExtTrace3(extension,D_Read,"Pend. Read Done, size:%d [%d %d]",
  423. extension->CurrentReadIrp->IoStatus.Information,
  424. extension->RxQ.QPut, extension->RxQ.QGet);
  425. // dump data into the trace buffer in a hex or ascii dump format
  426. TraceDump(extension,
  427. extension->CurrentReadIrp->AssociatedIrp.SystemBuffer,
  428. extension->CurrentReadIrp->IoStatus.Information, 0);
  429. }
  430. else if (extension->TraceOptions & 2) // trace input data
  431. {
  432. TracePut(
  433. extension->CurrentReadIrp->AssociatedIrp.SystemBuffer,
  434. extension->CurrentReadIrp->IoStatus.Information);
  435. }
  436. }
  437. /***************************************************************************
  438. Routine Description:
  439. This routine is merely used to complete any read that
  440. ended up being used by the Isr. It assumes that the
  441. status and the information fields of the irp are already
  442. correctly filled in.
  443. Arguments:
  444. Dpc - Not Used.
  445. DeferredContext - Really points to the device extension.
  446. SystemContext1 - Not Used.
  447. SystemContext2 - Not Used.
  448. Return Value:
  449. None.
  450. ***************************************************************************/
  451. VOID
  452. SerialCompleteRead(
  453. IN PKDPC Dpc,
  454. IN PVOID DeferredContext,
  455. IN PVOID SystemContext1,
  456. IN PVOID SystemContext2
  457. )
  458. {
  459. PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
  460. KIRQL oldIrql;
  461. UNREFERENCED_PARAMETER(Dpc);
  462. UNREFERENCED_PARAMETER(SystemContext1);
  463. UNREFERENCED_PARAMETER(SystemContext2);
  464. #ifdef TRACE_PORT
  465. if (extension->TraceOptions)
  466. {
  467. ExtTrace(extension,D_Read,"Read Complete");
  468. trace_read_data(extension);
  469. }
  470. #endif
  471. IoAcquireCancelSpinLock(&oldIrql);
  472. // check that we haven't been canceled by a timeout
  473. // fix for the semaphores
  474. if (extension->CurrentReadIrp != NULL)
  475. {
  476. // Don't allow the ISR to complete this IRP
  477. extension->ReadPending = FALSE;
  478. // Indicate to the interval timer that the read has completed.
  479. // The interval timer dpc can be lurking in some DPC queue.
  480. extension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
  481. SerialTryToCompleteCurrent(
  482. extension,
  483. NULL,
  484. oldIrql,
  485. STATUS_SUCCESS,
  486. &extension->CurrentReadIrp,
  487. &extension->ReadQueue,
  488. &extension->ReadRequestIntervalTimer,
  489. &extension->ReadRequestTotalTimer,
  490. SerialStartRead,
  491. SerialGetNextIrp,
  492. SERIAL_REF_ISR
  493. );
  494. }
  495. else
  496. {
  497. IoReleaseCancelSpinLock(oldIrql);
  498. }
  499. #ifdef TESTING_READ
  500. MyKdPrint(D_Read,("Complete Read!"))
  501. #endif
  502. }
  503. /****************************************************************************
  504. Routine Description:
  505. This routine is used to cancel the current read.
  506. Arguments:
  507. DeviceObject - Pointer to the device object for this device
  508. Irp - Pointer to the IRP to be canceled.
  509. Return Value:
  510. None.
  511. ****************************************************************************/
  512. VOID
  513. SerialCancelCurrentRead(
  514. PDEVICE_OBJECT DeviceObject,
  515. PIRP Irp
  516. )
  517. {
  518. PSERIAL_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
  519. // Indicate to the interval timer that the read has encountered a cancel.
  520. // The interval timer dpc can be lurking in some DPC queue.
  521. extension->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
  522. extension->ReadPending = FALSE;
  523. SERIAL_CLEAR_REFERENCE(extension->CurrentReadIrp, SERIAL_REF_ISR);
  524. if (extension->TraceOptions)
  525. {
  526. ExtTrace(extension,D_Read,"Cancel Read");
  527. trace_read_data(extension);
  528. }
  529. SerialTryToCompleteCurrent(
  530. extension,
  531. SerialGrabReadFromIsr,
  532. Irp->CancelIrql,
  533. STATUS_CANCELLED,
  534. &extension->CurrentReadIrp,
  535. &extension->ReadQueue,
  536. &extension->ReadRequestIntervalTimer,
  537. &extension->ReadRequestTotalTimer,
  538. SerialStartRead,
  539. SerialGetNextIrp,
  540. SERIAL_REF_CANCEL
  541. );
  542. }
  543. /*------------------------------------------------------------------
  544. Routine Description:
  545. This routine is used to complete a read because its total
  546. timer has expired.
  547. Arguments:
  548. Dpc - Not Used.
  549. DeferredContext - Really points to the device extension.
  550. SystemContext1 - Not Used.
  551. SystemContext2 - Not Used.
  552. Return Value:
  553. None.
  554. |------------------------------------------------------------------*/
  555. VOID
  556. SerialReadTimeout(
  557. IN PKDPC Dpc,
  558. IN PVOID DeferredContext,
  559. IN PVOID SystemContext1,
  560. IN PVOID SystemContext2
  561. )
  562. {
  563. PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
  564. KIRQL oldIrql;
  565. UNREFERENCED_PARAMETER(Dpc);
  566. UNREFERENCED_PARAMETER(SystemContext1);
  567. UNREFERENCED_PARAMETER(SystemContext2);
  568. #ifdef TESTING_READ
  569. MyKdPrint(D_Read,("\n[Read Timeout!]\n"))
  570. MyKdPrint(D_Read,("Read Wanted, len:%ld \n", extension->debug_ul1))
  571. MyKdPrint(D_Read,("Read, Got:%ld, Immed:%d readpen:%d\n",
  572. extension->CurrentReadIrp->IoStatus.Information,
  573. extension->debug_ul2,
  574. extension->ReadPending))
  575. MyKdPrint(D_Read,("Read Left, NNFR:%ld\n", extension->NumberNeededForRead))
  576. KdBreakPoint();
  577. #endif
  578. if (extension->TraceOptions)
  579. {
  580. ExtTrace(extension,D_Read,"Rd-Total Timeout");
  581. trace_read_data(extension);
  582. }
  583. IoAcquireCancelSpinLock(&oldIrql);
  584. // Indicate to the interval timer that the read has completed
  585. // due to total timeout.
  586. // The interval timer dpc can be lurking in some DPC queue.
  587. extension->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
  588. SerialTryToCompleteCurrent(
  589. extension,
  590. SerialGrabReadFromIsr,
  591. oldIrql,
  592. STATUS_TIMEOUT,
  593. &extension->CurrentReadIrp,
  594. &extension->ReadQueue,
  595. &extension->ReadRequestIntervalTimer,
  596. &extension->ReadRequestTotalTimer,
  597. SerialStartRead,
  598. SerialGetNextIrp,
  599. SERIAL_REF_TOTAL_TIMER
  600. );
  601. }
  602. /*------------------------------------------------------------------
  603. Routine Description:
  604. This routine is used timeout the request if the time between
  605. characters exceed the interval time. A global is kept in
  606. the device extension that records the count of characters read
  607. the last the last time this routine was invoked (This dpc
  608. will resubmit the timer if the count has changed). If the
  609. count has not changed then this routine will attempt to complete
  610. the irp. Note the special case of the last count being zero.
  611. The timer isn't really in effect until the first character is read.
  612. Arguments:
  613. Dpc - Not Used.
  614. DeferredContext - Really points to the device extension.
  615. SystemContext1 - Not Used.
  616. SystemContext2 - Not Used.
  617. Return Value:
  618. None.
  619. |------------------------------------------------------------------*/
  620. VOID
  621. SerialIntervalReadTimeout(
  622. IN PKDPC Dpc,
  623. IN PVOID DeferredContext,
  624. IN PVOID SystemContext1,
  625. IN PVOID SystemContext2
  626. )
  627. {
  628. PSERIAL_DEVICE_EXTENSION extension = DeferredContext;
  629. KIRQL oldIrql;
  630. #if 0
  631. KIRQL controlIrql;
  632. #endif
  633. UNREFERENCED_PARAMETER(Dpc);
  634. UNREFERENCED_PARAMETER(SystemContext1);
  635. UNREFERENCED_PARAMETER(SystemContext2);
  636. #ifdef TIMEOUT_TRACING
  637. ExtTrace3(extension,D_Read,"RIT, amnt:%d [%d %d]",
  638. extension->CurrentReadIrp->IoStatus.Information,
  639. extension->RxQ.QPut, extension->RxQ.QGet);
  640. #endif
  641. IoAcquireCancelSpinLock(&oldIrql);
  642. if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL)
  643. {
  644. #ifdef TIMEOUT_TRACING
  645. if (extension->TraceOptions)
  646. {
  647. ExtTrace(extension,D_Read,"Interv. Complete Total");
  648. trace_read_data(extension);
  649. }
  650. #endif
  651. // The total timer has fired, try to complete.
  652. SerialTryToCompleteCurrent(
  653. extension,
  654. SerialGrabReadFromIsr,
  655. oldIrql,
  656. STATUS_TIMEOUT,
  657. &extension->CurrentReadIrp,
  658. &extension->ReadQueue,
  659. &extension->ReadRequestIntervalTimer,
  660. &extension->ReadRequestTotalTimer,
  661. SerialStartRead,
  662. SerialGetNextIrp,
  663. SERIAL_REF_INT_TIMER
  664. );
  665. }
  666. else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE)
  667. {
  668. ExtTrace(extension,D_Read," Rd Timeout, Complete");
  669. #ifdef TRACE_PORT
  670. if (extension->TraceOptions)
  671. { trace_read_data(extension); }
  672. #endif
  673. // The regular completion routine has been called, try to complete.
  674. SerialTryToCompleteCurrent(
  675. extension,
  676. SerialGrabReadFromIsr,
  677. oldIrql,
  678. STATUS_SUCCESS,
  679. &extension->CurrentReadIrp,
  680. &extension->ReadQueue,
  681. &extension->ReadRequestIntervalTimer,
  682. &extension->ReadRequestTotalTimer,
  683. SerialStartRead,
  684. SerialGetNextIrp,
  685. SERIAL_REF_INT_TIMER
  686. );
  687. }
  688. else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL)
  689. {
  690. ExtTrace(extension,D_Read,"Rd Timeout, Cancel");
  691. #ifdef TRACE_PORT
  692. if (extension->TraceOptions)
  693. { trace_read_data(extension); }
  694. #endif
  695. // The cancel read routine has been called, try to complete.
  696. SerialTryToCompleteCurrent(
  697. extension,
  698. SerialGrabReadFromIsr,
  699. oldIrql,
  700. STATUS_CANCELLED,
  701. &extension->CurrentReadIrp,
  702. &extension->ReadQueue,
  703. &extension->ReadRequestIntervalTimer,
  704. &extension->ReadRequestTotalTimer,
  705. SerialStartRead,
  706. SerialGetNextIrp,
  707. SERIAL_REF_INT_TIMER
  708. );
  709. }
  710. else if (extension->CountOnLastRead || extension->ReadByIsr)
  711. {
  712. //
  713. // Check on Interval Timeouts.
  714. //
  715. // As we come back to this routine we will compare the current time
  716. // to the "last" time. If the difference is larger than the
  717. // interval requested by the user, time out the request.
  718. // If the ISR has read in any more characters, resubmit the timer.
  719. if(extension->ReadByIsr)
  720. {
  721. // Something was placed in the system side buffer by the ISR
  722. // Init for resubmitted timeout
  723. extension->ReadByIsr = 0;
  724. #if 0
  725. /*----------
  726. This is bad news, the ISR moves data from the que to the user IRP buffer,
  727. if we do it here we have a nasty time-consuming contention issue.
  728. There is no good reason to do the move here, take it out.
  729. ----------*/
  730. KeAcquireSpinLock(&extension->ControlLock,&controlIrql);
  731. // Move the chars to the user buffer
  732. /*----------
  733. isr code calls this routine also, the extension->ReadPending is the
  734. mechanism to control access(Two SerialGetCharsFromIntBuffer() calls
  735. at same time.) kpb
  736. ----------*/
  737. extension->CountOnLastRead |=
  738. SerialGetCharsFromIntBuffer(extension);
  739. KeReleaseSpinLock(&extension->ControlLock,controlIrql);
  740. #endif
  741. // Save off the "last" time something was read.
  742. KeQuerySystemTime(
  743. &extension->LastReadTime
  744. );
  745. #ifdef TRACE_TICK_DEBUG
  746. ExtTrace(extension,D_Read," Resubmit(new chars)");
  747. #endif
  748. // Resubmit the timer
  749. KeSetTimer(
  750. &extension->ReadRequestIntervalTimer,
  751. *extension->IntervalTimeToUse,
  752. &extension->IntervalReadTimeoutDpc
  753. );
  754. IoReleaseCancelSpinLock(oldIrql);
  755. // Allow the ISR to complete this IRP
  756. }
  757. else
  758. {
  759. // The timer fired but nothing was in the interrupt buffer.
  760. // Characters have been read previously, so check time interval
  761. LARGE_INTEGER currentTime;
  762. KeQuerySystemTime(
  763. &currentTime
  764. );
  765. if ((currentTime.QuadPart - extension->LastReadTime.QuadPart) >=
  766. extension->IntervalTime.QuadPart)
  767. {
  768. ExtTrace(extension,D_Read,"RIT Timeout");
  769. #ifdef TRACE_PORT
  770. if (extension->TraceOptions)
  771. { trace_read_data(extension); }
  772. #endif
  773. // No characters read in the interval time, kill this read.
  774. SerialTryToCompleteCurrent(
  775. extension,
  776. SerialGrabReadFromIsr,
  777. oldIrql,
  778. STATUS_TIMEOUT,
  779. &extension->CurrentReadIrp,
  780. &extension->ReadQueue,
  781. &extension->ReadRequestIntervalTimer,
  782. &extension->ReadRequestTotalTimer,
  783. SerialStartRead,
  784. SerialGetNextIrp,
  785. SERIAL_REF_INT_TIMER
  786. );
  787. }
  788. else
  789. {
  790. #ifdef TRACE_TICK_DEBUG
  791. // The timer fired but the interval time has not
  792. // been exceeded, resubmit the timer
  793. ExtTrace(extension,D_Read," Resubmit");
  794. #endif
  795. KeSetTimer(
  796. &extension->ReadRequestIntervalTimer,
  797. *extension->IntervalTimeToUse,
  798. &extension->IntervalReadTimeoutDpc
  799. );
  800. IoReleaseCancelSpinLock(oldIrql);
  801. #ifdef TIMEOUT_TRACING
  802. ExtTrace(extension,D_Read," No data, Resubmit.");
  803. #endif
  804. // Allow the ISR to complete this IRP
  805. }
  806. }
  807. }
  808. else
  809. {
  810. // No characters have been read yet, so just resubmit the timeout.
  811. KeSetTimer(
  812. &extension->ReadRequestIntervalTimer,
  813. *extension->IntervalTimeToUse,
  814. &extension->IntervalReadTimeoutDpc
  815. );
  816. IoReleaseCancelSpinLock(oldIrql);
  817. #ifdef TIMEOUT_TRACING
  818. ExtTrace(extension,D_Read," No data A, Resubmit.");
  819. #endif
  820. }
  821. }
  822. /*------------------------------------------------------------------
  823. SerialGrabReadFromIsr - Take back the read packet from the ISR by
  824. reseting ReadPending flag in extension. Need to use a sync with
  825. isr/timer routine to avoid contention in multiprocessor environments.
  826. Called from sync routine or with timer spinlock held.
  827. App - Can set ReadPending to give read-irp handling to the ISR without
  828. syncing to ISR.
  829. ISR - Can reset ReadPending to give read-irp handling back to app-time.
  830. If App wants to grab control of read-irp handling back from ISR, then
  831. it must sync-up with the isr/timer routine which has control.
  832. |-------------------------------------------------------------------*/
  833. BOOLEAN SerialGrabReadFromIsr(PSERIAL_DEVICE_EXTENSION Extension)
  834. {
  835. Extension->ReadPending = FALSE;
  836. SERIAL_CLEAR_REFERENCE(Extension->CurrentReadIrp, SERIAL_REF_ISR);
  837. return FALSE;
  838. }
  839. /*------------------------------------------------------------------
  840. Routine Description:
  841. This routine is used to copy any characters out of the interrupt
  842. buffer into the users buffer. It will be reading values that
  843. are updated with the ISR but this is safe since this value is
  844. only decremented by synchronization routines.
  845. Arguments:
  846. Extension - A pointer to the device extension.
  847. Return Value:
  848. The number of characters that were copied into the user
  849. buffer.
  850. |-------------------------------------------------------------------*/
  851. ULONG SerialGetCharsFromIntBuffer(PSERIAL_DEVICE_EXTENSION Extension)
  852. {
  853. LONG RxCount;
  854. LONG WrapCount = 0L;
  855. // See how much data we have in RxBuf (host-side buffer)
  856. // RxCount signed here for buffer wrap testing
  857. RxCount = q_count(&Extension->RxQ);
  858. // Check for a zero count in RxBuf
  859. if (RxCount == 0)
  860. return 0L;
  861. // Send back only as much as the application asked for...
  862. // RxCount unsigned here (will always be positive at this point)
  863. if (Extension->NumberNeededForRead < (ULONG)RxCount)
  864. RxCount = Extension->NumberNeededForRead;
  865. // Check for a buffer wrap
  866. WrapCount = q_room_get_till_wrap(&Extension->RxQ);
  867. if (RxCount > WrapCount) // wrap is required
  868. {
  869. // RtlMoveMemory(
  870. memcpy(
  871. (PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer) +
  872. Extension->CurrentReadIrp->IoStatus.Information,
  873. Extension->RxQ.QBase + Extension->RxQ.QGet,
  874. WrapCount);
  875. // RtlMoveMemory(
  876. memcpy(
  877. (PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer) +
  878. Extension->CurrentReadIrp->IoStatus.Information + WrapCount,
  879. Extension->RxQ.QBase,
  880. RxCount - WrapCount);
  881. }
  882. else //--- single move ok
  883. {
  884. // RtlMoveMemory(
  885. memcpy(
  886. (PUCHAR)(Extension->CurrentReadIrp->AssociatedIrp.SystemBuffer) +
  887. Extension->CurrentReadIrp->IoStatus.Information,
  888. Extension->RxQ.QBase + Extension->RxQ.QGet,
  889. RxCount);
  890. }
  891. // Update host side buffer ptrs
  892. Extension->RxQ.QGet = (Extension->RxQ.QGet + RxCount) % Extension->RxQ.QSize;
  893. Extension->CurrentReadIrp->IoStatus.Information += RxCount;
  894. Extension->NumberNeededForRead -= RxCount;
  895. return RxCount;
  896. }