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.

1156 lines
37 KiB

  1. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. Copyright (c) 1991, 1992, 1993 Microsoft Corporation
  3. Module Name:
  4. read.c
  5. Abstract:
  6. This module contains the code that is very specific to read
  7. operations in the serial driver
  8. Author:
  9. Anthony V. Ercolano 26-Sep-1991
  10. Environment:
  11. Kernel mode
  12. Revision History :
  13. -----------------------------------------------------------------------------*/
  14. #include "precomp.h"
  15. // Prototypes
  16. VOID SerialCancelCurrentRead(PDEVICE_OBJECT DeviceObject, PIRP Irp);
  17. BOOLEAN SerialGrabReadFromIsr(IN PVOID Context);
  18. BOOLEAN SerialUpdateReadByIsr(IN PVOID Context);
  19. BOOLEAN ReadDataFromIntBuffer(IN PVOID Context);
  20. BOOLEAN UpdateAndWaitForMoreData(IN PVOID Context);
  21. NTSTATUS SerialResizeBuffer(IN PPORT_DEVICE_EXTENSION pPort);
  22. BOOLEAN SerialUpdateAndSwitchToNew(IN PVOID Context);
  23. // End of Prototypes
  24. #ifdef ALLOC_PRAGMA
  25. #endif
  26. NTSTATUS
  27. SerialRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
  28. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  29. Routine Description:
  30. This is the dispatch routine for reading. It validates the parameters
  31. for the read request and if all is ok then it places the request
  32. on the work queue.
  33. Arguments:
  34. DeviceObject - Pointer to the device object for this device
  35. Irp - Pointer to the IRP for the current request
  36. Return Value:
  37. If the io is zero length then it will return STATUS_SUCCESS,
  38. otherwise this routine will return the status returned by
  39. the actual start read routine.
  40. -----------------------------------------------------------------------------*/
  41. {
  42. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  43. SpxDbgMsg(SPX_TRACE_CALLS,("%s: Read Irp dispatch entry for Irp: %x\n", PRODUCT_NAME, Irp));
  44. SpxIRPCounter(pPort, Irp, IRP_SUBMITTED); // Increment counter for performance stats.
  45. if(SerialCompleteIfError(DeviceObject, Irp) != STATUS_SUCCESS)
  46. return STATUS_CANCELLED;
  47. Irp->IoStatus.Information = 0L;
  48. //
  49. // Quick check for a zero length read. If it is zero length then we are already done!
  50. //
  51. if(IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length)
  52. {
  53. //
  54. // Well it looks like we actually have to do some work.
  55. // Put the read on the queue so that we can process it when our previous reads are done.
  56. //
  57. return SerialStartOrQueue(pPort, Irp, &pPort->ReadQueue, &pPort->CurrentReadIrp, SerialStartRead);
  58. }
  59. else
  60. {
  61. Irp->IoStatus.Status = STATUS_SUCCESS;
  62. SpxDbgMsg(SPX_TRACE_CALLS,("%s: Complete Read for Irp: %x\n", PRODUCT_NAME, Irp));
  63. SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
  64. IoCompleteRequest(Irp, 0);
  65. return STATUS_SUCCESS;
  66. }
  67. }
  68. NTSTATUS
  69. SerialStartRead(IN PPORT_DEVICE_EXTENSION pPort)
  70. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  71. Routine Description:
  72. This routine is used to start off any read. It initializes
  73. the Iostatus fields of the irp. It will set up any timers
  74. that are used to control the read. It will attempt to complete
  75. the read from data already in the interrupt buffer. If the
  76. read can be completed quickly it will start off another if
  77. necessary.
  78. Arguments:
  79. Extension - Simply a pointer to the serial device extension.
  80. Return Value:
  81. This routine will return the status of the first read
  82. irp. This is useful in that if we have a read that can
  83. complete right away (AND there had been nothing in the
  84. queue before it) the read could return SUCCESS and the
  85. application won't have to do a wait.
  86. -----------------------------------------------------------------------------*/
  87. {
  88. SERIAL_UPDATE_CHAR updateChar;
  89. PIRP newIrp;
  90. KIRQL oldIrql;
  91. KIRQL controlIrql;
  92. BOOLEAN returnWithWhatsPresent;
  93. BOOLEAN os2ssreturn;
  94. BOOLEAN crunchDownToOne;
  95. BOOLEAN useTotalTimer;
  96. BOOLEAN useIntervalTimer;
  97. ULONG multiplierVal;
  98. ULONG constantVal;
  99. LARGE_INTEGER totalTime;
  100. SERIAL_TIMEOUTS timeoutsForIrp;
  101. BOOLEAN setFirstStatus = FALSE;
  102. NTSTATUS firstStatus;
  103. updateChar.pPort = pPort;
  104. SpxDbgMsg(SPX_TRACE_CALLS,("SerialStartRead - Irp: %x\n", pPort->CurrentReadIrp));
  105. do
  106. {
  107. //
  108. // Check to see if this is a resize request. If it is then go to a routine that specializes in that.
  109. //
  110. if(IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->MajorFunction != IRP_MJ_READ)
  111. {
  112. NTSTATUS localStatus = SerialResizeBuffer(pPort);
  113. if(!setFirstStatus)
  114. {
  115. firstStatus = localStatus;
  116. setFirstStatus = TRUE;
  117. }
  118. }
  119. else
  120. {
  121. //
  122. // The irp might go under control of the isr.
  123. // It won't hurt to initialize the reference count right now.
  124. //
  125. SERIAL_INIT_REFERENCE(pPort->CurrentReadIrp);
  126. pPort->NumberNeededForRead = IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length;
  127. // Calculate the timeout value needed for the request.
  128. // Note that the values stored in the timeout record are in milliseconds.
  129. useTotalTimer = FALSE;
  130. returnWithWhatsPresent = FALSE;
  131. os2ssreturn = FALSE;
  132. crunchDownToOne = FALSE;
  133. useIntervalTimer = FALSE;
  134. // Always initialize the timer objects so that the completion code can tell when it
  135. // attempts to cancel the timers whether the timers had ever been Set.
  136. KeInitializeTimer(&pPort->ReadRequestTotalTimer);
  137. KeInitializeTimer(&pPort->ReadRequestIntervalTimer);
  138. // We get the *current* timeout values to use for timing this read.
  139. KeAcquireSpinLock(&pPort->ControlLock, &controlIrql);
  140. timeoutsForIrp = pPort->Timeouts;
  141. KeReleaseSpinLock(&pPort->ControlLock, controlIrql);
  142. // Calculate the interval timeout for the read.
  143. if(timeoutsForIrp.ReadIntervalTimeout && (timeoutsForIrp.ReadIntervalTimeout != MAXULONG))
  144. {
  145. useIntervalTimer = TRUE;
  146. pPort->IntervalTime.QuadPart = UInt32x32To64(timeoutsForIrp.ReadIntervalTimeout, 10000);
  147. if(pPort->IntervalTime.QuadPart >= pPort->CutOverAmount.QuadPart)
  148. pPort->IntervalTimeToUse = &pPort->LongIntervalAmount;
  149. else
  150. pPort->IntervalTimeToUse = &pPort->ShortIntervalAmount;
  151. }
  152. if(timeoutsForIrp.ReadIntervalTimeout == MAXULONG)
  153. {
  154. //
  155. // We need to do special return quickly stuff here.
  156. //
  157. // 1) If both constant and multiplier are
  158. // 0 then we return immediately with whatever
  159. // we've got, even if it was zero.
  160. //
  161. // 2) If constant and multiplier are not MAXULONG
  162. // then return immediately if any characters
  163. // are present, but if nothing is there, then
  164. // use the timeouts as specified.
  165. //
  166. // 3) If multiplier is MAXULONG then do as in
  167. // "2" but return when the first character
  168. // arrives.
  169. //
  170. if(!timeoutsForIrp.ReadTotalTimeoutConstant && !timeoutsForIrp.ReadTotalTimeoutMultiplier)
  171. {
  172. returnWithWhatsPresent = TRUE;
  173. }
  174. else if((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  175. && (timeoutsForIrp.ReadTotalTimeoutMultiplier != MAXULONG))
  176. {
  177. useTotalTimer = TRUE;
  178. os2ssreturn = TRUE;
  179. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  180. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  181. }
  182. else if((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
  183. && (timeoutsForIrp.ReadTotalTimeoutMultiplier == MAXULONG))
  184. {
  185. useTotalTimer = TRUE;
  186. os2ssreturn = TRUE;
  187. crunchDownToOne = TRUE;
  188. multiplierVal = 0;
  189. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  190. }
  191. }
  192. else
  193. {
  194. //
  195. // If both the multiplier and the constant are
  196. // zero then don't do any total timeout processing.
  197. //
  198. if(timeoutsForIrp.ReadTotalTimeoutMultiplier || timeoutsForIrp.ReadTotalTimeoutConstant)
  199. {
  200. // We have some timer values to calculate.
  201. useTotalTimer = TRUE;
  202. multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
  203. constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
  204. }
  205. }
  206. if(useTotalTimer)
  207. {
  208. totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(pPort->NumberNeededForRead, multiplierVal)
  209. + constantVal)) * -10000;
  210. }
  211. //
  212. // If we are supposed to crunch the read down to one character, then update
  213. // the number needed for read down to one.
  214. //
  215. if(crunchDownToOne)
  216. pPort->NumberNeededForRead = 1;
  217. //
  218. // We do this copy in the hope of getting most (if not
  219. // all) of the characters out of the interrupt buffer.
  220. //
  221. // Note that we need to protect this operation with a
  222. // spinlock since we don't want a purge to hose us.
  223. //
  224. KeAcquireSpinLock(&pPort->ControlLock, &controlIrql);
  225. KeSynchronizeExecution(pPort->Interrupt, ReadDataFromIntBuffer, pPort);
  226. //
  227. // See if we have any cause to return immediately.
  228. //
  229. if(returnWithWhatsPresent || (!pPort->NumberNeededForRead)
  230. || (os2ssreturn && pPort->CurrentReadIrp->IoStatus.Information))
  231. {
  232. // We got all we needed for this read.
  233. KeReleaseSpinLock(&pPort->ControlLock, controlIrql);
  234. pPort->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
  235. if(!setFirstStatus)
  236. {
  237. firstStatus = STATUS_SUCCESS;
  238. setFirstStatus = TRUE;
  239. }
  240. }
  241. else
  242. {
  243. IoAcquireCancelSpinLock(&oldIrql);
  244. //
  245. // We need to see if this irp should be canceled.
  246. //
  247. if(pPort->CurrentReadIrp->Cancel)
  248. {
  249. IoReleaseCancelSpinLock(oldIrql);
  250. KeReleaseSpinLock(&pPort->ControlLock, controlIrql);
  251. pPort->CurrentReadIrp->IoStatus.Status = STATUS_CANCELLED;
  252. pPort->CurrentReadIrp->IoStatus.Information = 0;
  253. if(!setFirstStatus)
  254. {
  255. firstStatus = STATUS_CANCELLED;
  256. setFirstStatus = TRUE;
  257. }
  258. }
  259. else
  260. {
  261. //
  262. // We still need to get more characters for this read.
  263. // synchronize with the isr so that we can update the
  264. // number of characters and if necessary it will have the
  265. // isr switch to copying into the users buffer.
  266. //
  267. KeSynchronizeExecution(pPort->Interrupt, UpdateAndWaitForMoreData, pPort);
  268. //
  269. // The irp still isn't complete. The
  270. // completion routines will end up reinvoking
  271. // this routine. So we simply leave.
  272. //
  273. // First thought we should start off the total
  274. // timer for the read and increment the reference
  275. // count that the total timer has on the current
  276. // irp. Note that this is safe, because even if
  277. // the io has been satisfied by the isr it can't
  278. // complete yet because we still own the cancel
  279. // spinlock.
  280. //
  281. if(useTotalTimer)
  282. {
  283. SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_TOTAL_TIMER);
  284. KeSetTimer(&pPort->ReadRequestTotalTimer, totalTime, &pPort->TotalReadTimeoutDpc);
  285. }
  286. if(useIntervalTimer)
  287. {
  288. SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_INT_TIMER);
  289. KeQuerySystemTime(&pPort->LastReadTime);
  290. KeSetTimer(&pPort->ReadRequestIntervalTimer,
  291. *pPort->IntervalTimeToUse,
  292. &pPort->IntervalReadTimeoutDpc);
  293. }
  294. IoMarkIrpPending(pPort->CurrentReadIrp);
  295. IoReleaseCancelSpinLock(oldIrql);
  296. KeReleaseSpinLock(&pPort->ControlLock, controlIrql);
  297. if(!setFirstStatus)
  298. firstStatus = STATUS_PENDING;
  299. if(firstStatus == STATUS_PENDING)
  300. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead - STATUS_PENDING - Irp: %x\n", pPort->CurrentReadIrp));
  301. if(firstStatus == STATUS_SUCCESS)
  302. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead - STATUS_SUCCESS - Irp: %x\n", pPort->CurrentReadIrp));
  303. if(firstStatus == STATUS_CANCELLED)
  304. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead - STATUS_CANCELLED - Irp: %x\n", pPort->CurrentReadIrp));
  305. return firstStatus;
  306. }
  307. }
  308. }
  309. //
  310. // Well the operation is complete.
  311. //
  312. SerialGetNextIrp(pPort, &pPort->CurrentReadIrp, &pPort->ReadQueue, &newIrp, TRUE);
  313. } while (newIrp);
  314. if(firstStatus == STATUS_PENDING)
  315. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead 2 - STATUS_PENDING - Irp: %x\n", pPort->CurrentReadIrp));
  316. if(firstStatus == STATUS_SUCCESS)
  317. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead 2 - STATUS_SUCCESS - Irp: %x\n", pPort->CurrentReadIrp));
  318. if(firstStatus == STATUS_CANCELLED)
  319. SpxDbgMsg(SPX_TRACE_CALLS,("End SerialStartRead 2 - STATUS_CANCELLED - Irp: %x\n", pPort->CurrentReadIrp));
  320. return firstStatus;
  321. }
  322. VOID
  323. SerialCompleteRead(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemContext1, IN PVOID SystemContext2)
  324. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  325. Routine Description:
  326. This routine is merely used to complete any read that
  327. ended up being used by the Isr. It assumes that the
  328. status and the information fields of the irp are already
  329. correctly filled in.
  330. Arguments:
  331. Dpc - Not Used.
  332. DeferredContext - Really points to the device extension.
  333. SystemContext1 - Not Used.
  334. SystemContext2 - Not Used.
  335. Return Value:
  336. None.
  337. -----------------------------------------------------------------------------*/
  338. {
  339. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  340. KIRQL oldIrql;
  341. UNREFERENCED_PARAMETER(Dpc);
  342. UNREFERENCED_PARAMETER(SystemContext1);
  343. UNREFERENCED_PARAMETER(SystemContext2);
  344. SpxDbgMsg(SPX_TRACE_CALLS,("SerialCompleteRead - Irp: %x\n", pPort->CurrentReadIrp));
  345. IoAcquireCancelSpinLock(&oldIrql);
  346. // We set this to indicate to the interval timer that the read has completed.
  347. // Recall that the interval timer dpc can be lurking in some DPC queue.
  348. pPort->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
  349. // Clear the normal complete reference.
  350. SERIAL_CLEAR_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_COMPLETING);
  351. // Clear reference to ISR on completion
  352. SerialTryToCompleteCurrent( pPort,
  353. NULL,
  354. oldIrql,
  355. STATUS_SUCCESS,
  356. &pPort->CurrentReadIrp,
  357. &pPort->ReadQueue,
  358. &pPort->ReadRequestIntervalTimer,
  359. &pPort->ReadRequestTotalTimer,
  360. SerialStartRead,
  361. SerialGetNextIrp,
  362. SERIAL_REF_ISR);
  363. }
  364. VOID
  365. SerialCancelCurrentRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  366. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  367. Routine Description:
  368. This routine is used to cancel the current read.
  369. Arguments:
  370. DeviceObject - Pointer to the device object for this device
  371. Irp - Pointer to the IRP to be canceled.
  372. Return Value:
  373. None.
  374. -----------------------------------------------------------------------------*/
  375. {
  376. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  377. SpxDbgMsg(SPX_TRACE_CALLS,("SerialCancelCurrentRead - Irp: %x\n", pPort->CurrentReadIrp));
  378. // We set this to indicate to the interval timer that the read has encountered a cancel.
  379. // Recall that the interval timer dpc can be lurking in some DPC queue.
  380. pPort->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
  381. SerialTryToCompleteCurrent( pPort,
  382. SerialGrabReadFromIsr,
  383. Irp->CancelIrql,
  384. STATUS_CANCELLED,
  385. &pPort->CurrentReadIrp,
  386. &pPort->ReadQueue,
  387. &pPort->ReadRequestIntervalTimer,
  388. &pPort->ReadRequestTotalTimer,
  389. SerialStartRead,
  390. SerialGetNextIrp,
  391. SERIAL_REF_CANCEL);
  392. }
  393. BOOLEAN
  394. SerialGrabReadFromIsr(IN PVOID Context)
  395. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  396. Routine Description:
  397. This routine is used to grab (if possible) the irp from the
  398. isr. If it finds that the isr still owns the irp it grabs
  399. the irp away (updating the number of characters copied into the
  400. users buffer). If it grabs it away it also decrements the
  401. reference count on the irp since it no longer belongs to the
  402. isr (and the dpc that would complete it).
  403. NOTE: This routine assumes that if the current buffer that the
  404. ISR is copying characters into is the interrupt buffer then
  405. the dpc has already been queued.
  406. NOTE: This routine is being called from KeSynchronizeExecution.
  407. NOTE: This routine assumes that it is called with the cancel spin
  408. lock held.
  409. Arguments:
  410. Context - Really a pointer to the device extension.
  411. Return Value:
  412. Always false.
  413. -----------------------------------------------------------------------------*/
  414. {
  415. PPORT_DEVICE_EXTENSION pPort = Context;
  416. SpxDbgMsg(SPX_TRACE_CALLS,("SerialGrabReadFromIsr - Irp: %x\n", pPort->CurrentReadIrp));
  417. ReadDataFromIntBuffer(pPort);
  418. SERIAL_CLEAR_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_ISR);
  419. return FALSE;
  420. }
  421. VOID
  422. SerialReadTimeout(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemContext1, IN PVOID SystemContext2)
  423. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  424. Routine Description:
  425. This routine is used to complete a read because its total
  426. timer has expired.
  427. Arguments:
  428. Dpc - Not Used.
  429. DeferredContext - Really points to the device extension.
  430. SystemContext1 - Not Used.
  431. SystemContext2 - Not Used.
  432. Return Value:
  433. None.
  434. -----------------------------------------------------------------------------*/
  435. {
  436. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  437. KIRQL oldIrql;
  438. UNREFERENCED_PARAMETER(Dpc);
  439. UNREFERENCED_PARAMETER(SystemContext1);
  440. UNREFERENCED_PARAMETER(SystemContext2);
  441. SpxDbgMsg(SPX_TRACE_CALLS,("SerialReadTimeout - Irp: %x\n", pPort->CurrentReadIrp));
  442. IoAcquireCancelSpinLock(&oldIrql);
  443. // We set this to indicate to the interval timer that the read has completed due to total timeout.
  444. // Recall that the interval timer dpc can be lurking in some DPC queue.
  445. pPort->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
  446. SerialTryToCompleteCurrent( pPort,
  447. SerialGrabReadFromIsr,
  448. oldIrql,
  449. STATUS_TIMEOUT,
  450. &pPort->CurrentReadIrp,
  451. &pPort->ReadQueue,
  452. &pPort->ReadRequestIntervalTimer,
  453. &pPort->ReadRequestTotalTimer,
  454. SerialStartRead,
  455. SerialGetNextIrp,
  456. SERIAL_REF_TOTAL_TIMER);
  457. }
  458. BOOLEAN
  459. SerialUpdateReadByIsr(IN PVOID Context)
  460. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  461. Routine Description:
  462. This routine is used to update the count of characters read
  463. by the isr since the last interval timer experation.
  464. NOTE: This routine is being called from KeSynchronizeExecution.
  465. NOTE: This routine assumes that it is called with the cancel spin
  466. lock held.
  467. Arguments:
  468. Context - Really a pointer to the device extension.
  469. Return Value:
  470. Always false.
  471. -----------------------------------------------------------------------------*/
  472. {
  473. PPORT_DEVICE_EXTENSION pPort = Context;
  474. SpxDbgMsg(SPX_TRACE_CALLS,("SerialUpdateReadByIsr - Irp: %x\n", pPort->CurrentReadIrp));
  475. pPort->CountOnLastRead = pPort->ReadByIsr;
  476. pPort->ReadByIsr = 0;
  477. return FALSE;
  478. }
  479. VOID
  480. SerialIntervalReadTimeout(IN PKDPC Dpc,
  481. IN PVOID DeferredContext,
  482. IN PVOID SystemContext1,
  483. IN PVOID SystemContext2)
  484. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  485. Routine Description:
  486. This routine is used timeout the request if the time between
  487. characters exceed the interval time. A global is kept in
  488. the device extension that records the count of characters read
  489. the last the last time this routine was invoked (This dpc
  490. will resubmit the timer if the count has changed). If the
  491. count has not changed then this routine will attempt to complete
  492. the irp. Note the special case of the last count being zero.
  493. The timer isn't really in effect until the first character is
  494. read.
  495. Arguments:
  496. Dpc - Not Used.
  497. DeferredContext - Really points to the device extension.
  498. SystemContext1 - Not Used.
  499. SystemContext2 - Not Used.
  500. Return Value:
  501. None.
  502. -----------------------------------------------------------------------------*/
  503. {
  504. PPORT_DEVICE_EXTENSION pPort = DeferredContext;
  505. KIRQL oldIrql;
  506. UNREFERENCED_PARAMETER(Dpc);
  507. UNREFERENCED_PARAMETER(SystemContext1);
  508. UNREFERENCED_PARAMETER(SystemContext2);
  509. SpxDbgMsg(SPX_TRACE_CALLS,("SerialIntervalReadTimeout - Irp: %x\n", pPort->CurrentReadIrp));
  510. IoAcquireCancelSpinLock(&oldIrql);
  511. if(pPort->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL)
  512. {
  513. // This value is only set by the total timer to indicate that it has fired.
  514. // If so, then we should simply try to complete.
  515. SerialTryToCompleteCurrent( pPort,
  516. SerialGrabReadFromIsr,
  517. oldIrql,
  518. STATUS_TIMEOUT,
  519. &pPort->CurrentReadIrp,
  520. &pPort->ReadQueue,
  521. &pPort->ReadRequestIntervalTimer,
  522. &pPort->ReadRequestTotalTimer,
  523. SerialStartRead,
  524. SerialGetNextIrp,
  525. SERIAL_REF_INT_TIMER);
  526. }
  527. else if(pPort->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE)
  528. {
  529. // This value is only set by the regular completion routine.
  530. // If so, then we should simply try to complete.
  531. SerialTryToCompleteCurrent( pPort,
  532. SerialGrabReadFromIsr,
  533. oldIrql,
  534. STATUS_SUCCESS,
  535. &pPort->CurrentReadIrp,
  536. &pPort->ReadQueue,
  537. &pPort->ReadRequestIntervalTimer,
  538. &pPort->ReadRequestTotalTimer,
  539. SerialStartRead,
  540. SerialGetNextIrp,
  541. SERIAL_REF_INT_TIMER);
  542. }
  543. else if(pPort->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL)
  544. {
  545. // This value is only set by the cancel read routine.
  546. // If so, then we should simply try to complete.
  547. SerialTryToCompleteCurrent( pPort,
  548. SerialGrabReadFromIsr,
  549. oldIrql,
  550. STATUS_CANCELLED,
  551. &pPort->CurrentReadIrp,
  552. &pPort->ReadQueue,
  553. &pPort->ReadRequestIntervalTimer,
  554. &pPort->ReadRequestTotalTimer,
  555. SerialStartRead,
  556. SerialGetNextIrp,
  557. SERIAL_REF_INT_TIMER);
  558. }
  559. else if(pPort->CountOnLastRead || pPort->ReadByIsr)
  560. {
  561. //
  562. // Something has happened since we last came here. We
  563. // check to see if the ISR has read in any more characters.
  564. // If it did then we should update the isr's read count
  565. // and resubmit the timer.
  566. //
  567. if(pPort->ReadByIsr)
  568. {
  569. KeSynchronizeExecution(pPort->Interrupt, SerialUpdateReadByIsr, pPort);
  570. //
  571. // Save off the "last" time something was read.
  572. // As we come back to this routine we will compare
  573. // the current time to the "last" time. If the
  574. // difference is ever larger then the interval
  575. // requested by the user, then time out the request.
  576. //
  577. KeQuerySystemTime(&pPort->LastReadTime);
  578. KeSetTimer( &pPort->ReadRequestIntervalTimer,
  579. *pPort->IntervalTimeToUse,
  580. &pPort->IntervalReadTimeoutDpc);
  581. IoReleaseCancelSpinLock(oldIrql);
  582. }
  583. else
  584. {
  585. //
  586. // Take the difference between the current time
  587. // and the last time we had characters and
  588. // see if it is greater then the interval time.
  589. // if it is, then time out the request. Otherwise
  590. // go away again for a while.
  591. //
  592. // No characters read in the interval time. Kill this read.
  593. LARGE_INTEGER currentTime;
  594. KeQuerySystemTime(&currentTime);
  595. if((currentTime.QuadPart - pPort->LastReadTime.QuadPart) >= pPort->IntervalTime.QuadPart)
  596. {
  597. SerialTryToCompleteCurrent( pPort,
  598. SerialGrabReadFromIsr,
  599. oldIrql,
  600. STATUS_TIMEOUT,
  601. &pPort->CurrentReadIrp,
  602. &pPort->ReadQueue,
  603. &pPort->ReadRequestIntervalTimer,
  604. &pPort->ReadRequestTotalTimer,
  605. SerialStartRead,
  606. SerialGetNextIrp,
  607. SERIAL_REF_INT_TIMER);
  608. }
  609. else
  610. {
  611. KeSetTimer( &pPort->ReadRequestIntervalTimer,
  612. *pPort->IntervalTimeToUse,
  613. &pPort->IntervalReadTimeoutDpc);
  614. IoReleaseCancelSpinLock(oldIrql);
  615. }
  616. }
  617. }
  618. else
  619. {
  620. //
  621. // Timer doesn't really start until the first character.
  622. // So we should simply resubmit ourselves.
  623. //
  624. KeSetTimer(&pPort->ReadRequestIntervalTimer, *pPort->IntervalTimeToUse, &pPort->IntervalReadTimeoutDpc);
  625. IoReleaseCancelSpinLock(oldIrql);
  626. }
  627. }
  628. BOOLEAN
  629. ReadDataFromIntBuffer(IN PVOID Context)
  630. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  631. Routine Description:
  632. This routine is used to copy any characters out of the interrupt
  633. buffer into the users buffer. It will be reading values that
  634. are updated with the ISR but this is safe since this value is
  635. only decremented by synchronization routines. This routine will
  636. return the number of characters copied so some other routine
  637. can call a synchronization routine to update what is seen at
  638. interrupt level.
  639. Arguments:
  640. Extension - A pointer to the device extension.
  641. Return Value:
  642. The number of characters that were copied into the user
  643. buffer.
  644. -----------------------------------------------------------------------------*/
  645. {
  646. PPORT_DEVICE_EXTENSION pPort = Context;
  647. ULONG NumberOfBytes = 0;
  648. SpxDbgMsg(SPX_TRACE_CALLS,("ReadDataFromIntBuffer - Irp: %x\n", pPort->CurrentReadIrp));
  649. NumberOfBytes = pPort->pUartLib->UL_ReadData_XXXX(pPort->pUart,
  650. (PUCHAR)(pPort->CurrentReadIrp->AssociatedIrp.SystemBuffer)
  651. + pPort->CurrentReadIrp->IoStatus.Information,
  652. IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length
  653. - pPort->CurrentReadIrp->IoStatus.Information);
  654. if(NumberOfBytes)
  655. {
  656. if(NumberOfBytes > pPort->NumberNeededForRead)
  657. pPort->NumberNeededForRead = 0;
  658. else
  659. pPort->NumberNeededForRead -= NumberOfBytes;
  660. pPort->CurrentReadIrp->IoStatus.Information += NumberOfBytes;
  661. // Deal with flow control if necessary.
  662. SerialHandleReducedIntBuffer(pPort);
  663. return TRUE;
  664. }
  665. return FALSE;
  666. }
  667. BOOLEAN
  668. UpdateAndWaitForMoreData(IN PVOID Context)
  669. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  670. Routine Description:
  671. This routine gets the (hopefully) few characters that
  672. remain in the interrupt buffer after the first time we tried
  673. to get them out. If we still don't have enough characters
  674. to satisfy the read it will then we set things up so that the
  675. ISR uses the user buffer copy into.
  676. This routine is also used to update a count that is maintained
  677. by the ISR to keep track of the number of characters in its buffer.
  678. NOTE: This is called by KeSynchronizeExecution.
  679. Arguments:
  680. Context - Points to a structure that contains a pointer to the
  681. device extension, a count of the number of characters
  682. that we previously copied into the users buffer, and
  683. a boolean that we will set that defines whether we
  684. switched the ISR to copy into the users buffer.
  685. Return Value:
  686. -----------------------------------------------------------------------------*/
  687. {
  688. PPORT_DEVICE_EXTENSION pPort = Context;
  689. SpxDbgMsg(SPX_TRACE_CALLS,("UpdateAndWaitForMoreData - Irp: %x\n", pPort->CurrentReadIrp));
  690. // There are more characters to get to satisfy this read.
  691. // Copy any characters that have arrived since we got the last batch.
  692. ReadDataFromIntBuffer(pPort);
  693. //
  694. // No more new characters will be "received" until we exit this routine.
  695. // We again check to make sure that we haven't satisfied this read,
  696. // and if we haven't, we wait for the ISR to get some more data.
  697. //
  698. if(pPort->NumberNeededForRead)
  699. {
  700. pPort->CountOnLastRead = pPort->CurrentReadIrp->IoStatus.Information;
  701. pPort->ReadByIsr = 0;
  702. // Mark the irp as being in a cancelable state.
  703. IoSetCancelRoutine(pPort->CurrentReadIrp, SerialCancelCurrentRead);
  704. // Increment the reference count twice.
  705. // Once for the Isr owning the irp and once because the cancel routine has a reference to it.
  706. SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_ISR);
  707. SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_CANCEL);
  708. return FALSE;
  709. }
  710. return TRUE;
  711. }
  712. //
  713. // We use this structure only to communicate to the synchronization
  714. // routine when we are switching to the resized buffer.
  715. //
  716. typedef struct _SERIAL_RESIZE_PARAMS
  717. {
  718. PPORT_DEVICE_EXTENSION pPort;
  719. PUCHAR OldBuffer;
  720. PUCHAR NewBuffer;
  721. ULONG NewBufferSize;
  722. ULONG NumberMoved;
  723. } SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
  724. NTSTATUS
  725. SerialResizeBuffer(IN PPORT_DEVICE_EXTENSION pPort)
  726. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  727. Routine Description:
  728. This routine will process the resize buffer request.
  729. If size requested for the RX buffer is smaller than
  730. the current buffer then we will simply return
  731. STATUS_SUCCESS. (We don't want to make buffers smaller.
  732. If we did that then we all of a sudden have "overrun"
  733. problems to deal with as well as flow control to deal
  734. with - very painful.) We ignore the TX buffer size
  735. request since we don't use a TX buffer.
  736. Arguments:
  737. Extension - Pointer to the device extension for the port.
  738. Return Value:
  739. STATUS_SUCCESS if everything worked out ok.
  740. STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
  741. memory for the buffer.
  742. -----------------------------------------------------------------------------*/
  743. {
  744. PSERIAL_QUEUE_SIZE rs = pPort->CurrentReadIrp->AssociatedIrp.SystemBuffer;
  745. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp);
  746. PVOID newBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
  747. irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  748. pPort->CurrentReadIrp->IoStatus.Information = 0L;
  749. pPort->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
  750. if(rs->InSize <= pPort->BufferSize)
  751. {
  752. //
  753. // Nothing to do. We don't make buffers smaller. Just
  754. // agree with the user. We must deallocate the memory
  755. // that was already allocated in the ioctl dispatch routine.
  756. //
  757. ExFreePool(newBuffer);
  758. }
  759. else
  760. {
  761. SERIAL_RESIZE_PARAMS rp;
  762. KIRQL controlIrql;
  763. //
  764. // Hmmm, looks like we actually have to go
  765. // through with this. We need to move all the
  766. // data that is in the current buffer into this
  767. // new buffer. We'll do this in two steps.
  768. //
  769. // First we go up to dispatch level and try to
  770. // move as much as we can without stopping the
  771. // ISR from running. We go up to dispatch level
  772. // by acquiring the control lock. We do it at
  773. // dispatch using the control lock so that:
  774. //
  775. // 1) We can't be context switched in the middle
  776. // of the move. Our pointers into the buffer
  777. // could be *VERY* stale by the time we got back.
  778. //
  779. // 2) We use the control lock since we don't want
  780. // some pesky purge irp to come along while
  781. // we are trying to move.
  782. //
  783. // After the move, but while we still hold the control
  784. // lock, we synch with the ISR and get those last
  785. // (hopefully) few characters that have come in since
  786. // we started the copy. We switch all of our pointers,
  787. // counters, and such to point to this new buffer. NOTE:
  788. // we need to be careful. If the buffer we were using
  789. // was not the default one created when we initialized
  790. // the device (i.e. it was created via a previous IRP of
  791. // this type), we should deallocate it.
  792. //
  793. rp.pPort = pPort;
  794. rp.OldBuffer = NULL;
  795. rp.NewBuffer = newBuffer;
  796. rp.NewBufferSize = rs->InSize;
  797. KeAcquireSpinLock(&pPort->ControlLock, &controlIrql);
  798. KeSynchronizeExecution(pPort->Interrupt, SerialUpdateAndSwitchToNew, &rp);
  799. KeReleaseSpinLock( &pPort->ControlLock, controlIrql);
  800. // Free up the memory that the old buffer consumed.
  801. ExFreePool(rp.OldBuffer);
  802. }
  803. return STATUS_SUCCESS;
  804. }
  805. BOOLEAN
  806. SerialUpdateAndSwitchToNew(IN PVOID Context)
  807. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  808. Routine Description:
  809. This routine gets the (hopefully) few characters that
  810. remain in the interrupt buffer after the first time we tried
  811. to get them out.
  812. NOTE: This is called by KeSynchronizeExecution.
  813. Arguments:
  814. Context - Points to a structure that contains a pointer to the
  815. device extension, a pointer to the buffer we are moving
  816. to, and a count of the number of characters
  817. that we previously copied into the new buffer, and the
  818. actual size of the new buffer.
  819. Return Value:
  820. Always FALSE.
  821. -----------------------------------------------------------------------------*/
  822. {
  823. PSERIAL_RESIZE_PARAMS params = Context;
  824. PPORT_DEVICE_EXTENSION pPort = params->pPort;
  825. pPort->BufferSizes.pINBuffer = params->NewBuffer;
  826. pPort->BufferSizes.INBufferSize = params->NewBufferSize;
  827. pPort->pUartLib->UL_BufferControl_XXXX(pPort->pUart, &pPort->BufferSizes, UL_BC_OP_SET, UL_BC_BUFFER | UL_BC_IN);
  828. params->OldBuffer = pPort->BufferSizes.pINBuffer;
  829. pPort->BufferSize = params->NewBufferSize;
  830. // We set up the default xon/xoff limits.
  831. pPort->HandFlow.XoffLimit = pPort->BufferSize >> 3;
  832. pPort->HandFlow.XonLimit = pPort->BufferSize >> 1;
  833. pPort->BufferSizePt8 = ((3*(pPort->BufferSize>>2)) + (pPort->BufferSize>>4));
  834. #ifdef WMI_SUPPORT
  835. UPDATE_WMI_XMIT_THRESHOLDS(pPort->WmiCommData, pPort->HandFlow);
  836. #endif
  837. // Since we (essentially) reduced the percentage of the interrupt
  838. // buffer being full, we need to handle any flow control.
  839. SerialHandleReducedIntBuffer(pPort);
  840. return FALSE;
  841. }