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.

1032 lines
36 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. pingpong.c
  5. Abstract
  6. Interrupt style collections like to always have a read pending in case
  7. something happens. This file contains routines to keep IRPs down
  8. in the miniport, and to complete client reads (if a client read IRP is
  9. pending) or queue them (if not).
  10. Author:
  11. Ervin P.
  12. Environment:
  13. Kernel mode only
  14. Revision History:
  15. --*/
  16. #include "pch.h"
  17. #ifdef ALLOC_PRAGMA
  18. #pragma alloc_text(PAGE, HidpInitializePingPongIrps)
  19. #pragma alloc_text(PAGE, HidpReallocPingPongIrps)
  20. #endif
  21. /*
  22. ********************************************************************************
  23. * HidpInitializePingPongIrps
  24. ********************************************************************************
  25. *
  26. *
  27. */
  28. NTSTATUS HidpInitializePingPongIrps(FDO_EXTENSION *fdoExtension)
  29. {
  30. NTSTATUS result = STATUS_SUCCESS;
  31. ULONG i;
  32. CCHAR numIrpStackLocations;
  33. PAGED_CODE();
  34. /*
  35. * Note that our functional device object normally requires FDO->StackSize stack
  36. * locations; but these IRPs will only be sent to the minidriver, so we need one less.
  37. *
  38. * THIS MEANS THAT WE SHOULD NEVER TOUCH OUR OWN STACK LOCATION (we don't have one!)
  39. */
  40. numIrpStackLocations = fdoExtension->fdo->StackSize - 1;
  41. //
  42. // Next determine the size of each input HID report. There
  43. // must be at least one collection of type interrupt, or we wouldn't
  44. // need the ping-pong stuff at all and therefore wouldn't be here.
  45. //
  46. ASSERT(fdoExtension->maxReportSize > 0);
  47. ASSERT(fdoExtension->numPingPongs > 0);
  48. fdoExtension->pingPongs = ALLOCATEPOOL(NonPagedPool, fdoExtension->numPingPongs*sizeof(HIDCLASS_PINGPONG));
  49. if (fdoExtension->pingPongs){
  50. ULONG reportBufferSize = fdoExtension->maxReportSize;
  51. RtlZeroMemory(fdoExtension->pingPongs, fdoExtension->numPingPongs*sizeof(HIDCLASS_PINGPONG));
  52. #if DBG
  53. // reserve space for guard word
  54. reportBufferSize += sizeof(ULONG);
  55. #endif
  56. for (i = 0; i < fdoExtension->numPingPongs; i++){
  57. fdoExtension->pingPongs[i].myFdoExt = fdoExtension;
  58. fdoExtension->pingPongs[i].weAreCancelling = 0;
  59. fdoExtension->pingPongs[i].sig = PINGPONG_SIG;
  60. /*
  61. * Initialize backoff timeout to 1 second (in neg 100-nsec units)
  62. */
  63. fdoExtension->pingPongs[i].backoffTimerPeriod.HighPart = -1;
  64. fdoExtension->pingPongs[i].backoffTimerPeriod.LowPart = -10000000;
  65. KeInitializeTimer(&fdoExtension->pingPongs[i].backoffTimer);
  66. KeInitializeDpc(&fdoExtension->pingPongs[i].backoffTimerDPC,
  67. HidpPingpongBackoffTimerDpc,
  68. &fdoExtension->pingPongs[i]);
  69. fdoExtension->pingPongs[i].reportBuffer = ALLOCATEPOOL(NonPagedPool, reportBufferSize);
  70. if (fdoExtension->pingPongs[i].reportBuffer){
  71. PIRP irp;
  72. #if DBG
  73. #ifdef _X86_
  74. // this sets off alignment problems on Alpha
  75. // place guard word
  76. *(PULONG)(&fdoExtension->pingPongs[i].reportBuffer[fdoExtension->maxReportSize]) = HIDCLASS_REPORT_BUFFER_GUARD;
  77. #endif
  78. #endif
  79. irp = IoAllocateIrp(numIrpStackLocations, FALSE);
  80. if (irp){
  81. /*
  82. * Point the ping-pong IRP's UserBuffer to the corresponding
  83. * ping-pong object's report buffer.
  84. */
  85. irp->UserBuffer = fdoExtension->pingPongs[i].reportBuffer;
  86. fdoExtension->pingPongs[i].irp = irp;
  87. KeInitializeEvent(&fdoExtension->pingPongs[i].sentEvent,
  88. NotificationEvent,
  89. TRUE); // Set to signaled
  90. KeInitializeEvent(&fdoExtension->pingPongs[i].pumpDoneEvent,
  91. NotificationEvent,
  92. TRUE); // Set to signaled
  93. }
  94. else {
  95. result = STATUS_INSUFFICIENT_RESOURCES;
  96. break;
  97. }
  98. }
  99. else {
  100. result = STATUS_INSUFFICIENT_RESOURCES;
  101. break;
  102. }
  103. }
  104. }
  105. else {
  106. result = STATUS_INSUFFICIENT_RESOURCES;
  107. }
  108. DBGSUCCESS(result, TRUE)
  109. return result;
  110. }
  111. /*
  112. ********************************************************************************
  113. * HidpReallocPingPongIrps
  114. ********************************************************************************
  115. *
  116. *
  117. */
  118. NTSTATUS HidpReallocPingPongIrps(FDO_EXTENSION *fdoExtension, ULONG newNumBufs)
  119. {
  120. NTSTATUS status = STATUS_SUCCESS;
  121. PAGED_CODE();
  122. if (fdoExtension->driverExt->DevicesArePolled){
  123. /*
  124. * Polled devices don't _HAVE_ ping-pong IRPs.
  125. */
  126. DBGERR(("Minidriver devices polled fdo %x.", fdoExtension))
  127. fdoExtension->numPingPongs = 0;
  128. fdoExtension->pingPongs = BAD_POINTER;
  129. status = STATUS_SUCCESS;
  130. }
  131. else if (newNumBufs < MIN_PINGPONG_IRPS){
  132. DBGERR(("newNumBufs < MIN_PINGPONG_IRPS!"))
  133. status = STATUS_INVALID_DEVICE_REQUEST;
  134. }
  135. else {
  136. DestroyPingPongs(fdoExtension);
  137. if (HidpSetMaxReportSize(fdoExtension)){
  138. /*
  139. * Initialize and restart the new ping-pong IRPs.
  140. * If we can't allocate the desired number of buffers,
  141. * keep reducing until we get some.
  142. */
  143. do {
  144. fdoExtension->numPingPongs = newNumBufs;
  145. status = HidpInitializePingPongIrps(fdoExtension);
  146. newNumBufs /= 2;
  147. } while (!NT_SUCCESS(status) && (newNumBufs >= MIN_PINGPONG_IRPS));
  148. if (!NT_SUCCESS(status)) {
  149. /*
  150. * The device will no longer function !!!
  151. */
  152. TRAP;
  153. fdoExtension->numPingPongs = 0;
  154. }
  155. }
  156. }
  157. DBGSUCCESS(status, TRUE)
  158. return status;
  159. }
  160. /*
  161. ********************************************************************************
  162. * HidpSubmitInterruptRead
  163. ********************************************************************************
  164. *
  165. *
  166. */
  167. NTSTATUS HidpSubmitInterruptRead(
  168. IN FDO_EXTENSION *fdoExt,
  169. HIDCLASS_PINGPONG *pingPong,
  170. BOOLEAN *irpSent)
  171. {
  172. NTSTATUS status = STATUS_SUCCESS;
  173. PIO_STACK_LOCATION irpSp;
  174. KIRQL oldIrql;
  175. BOOLEAN proceed;
  176. LONG oldInterlock;
  177. PIRP irp = pingPong->irp;
  178. ASSERT(irp);
  179. *irpSent = FALSE;
  180. while (1) {
  181. if (NT_SUCCESS(status)) {
  182. HidpSetDeviceBusy(fdoExt);
  183. oldInterlock = InterlockedExchange(&pingPong->ReadInterlock,
  184. PINGPONG_START_READ);
  185. ASSERT(oldInterlock == PINGPONG_END_READ);
  186. irp->Cancel = FALSE;
  187. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  188. irpSp = IoGetNextIrpStackLocation(irp);
  189. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  190. irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_READ_REPORT;
  191. irpSp->Parameters.DeviceIoControl.OutputBufferLength = fdoExt->maxReportSize;
  192. /*
  193. * Indicate interrupt collection (default).
  194. * We use .InputBufferLength for this
  195. */
  196. irpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
  197. ASSERT(irp->UserBuffer == pingPong->reportBuffer);
  198. #ifdef _X86_
  199. // this sets off alignment problems on Alpha
  200. ASSERT(*(PULONG)(&pingPong->reportBuffer[fdoExt->maxReportSize]) == HIDCLASS_REPORT_BUFFER_GUARD);
  201. #endif
  202. /*
  203. * Set the completion, passing the FDO extension as context.
  204. */
  205. IoSetCompletionRoutine( irp,
  206. HidpInterruptReadComplete,
  207. (PVOID)fdoExt,
  208. TRUE,
  209. TRUE,
  210. TRUE );
  211. /*
  212. * Send down the read IRP.
  213. */
  214. KeResetEvent(&pingPong->sentEvent);
  215. if (pingPong->weAreCancelling) {
  216. //
  217. // Ordering of the next two instructions is crucial, since
  218. // CancelPingPongs will exit after pumpDoneEvent is set, and the
  219. // pingPongs could be deleted after that.
  220. //
  221. DBGVERBOSE(("Pingpong %x cancelled in submit before sending\n", pingPong))
  222. KeSetEvent (&pingPong->sentEvent, 0, FALSE);
  223. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  224. status = STATUS_CANCELLED;
  225. break;
  226. } else {
  227. InterlockedIncrement(&fdoExt->outstandingRequests);
  228. DBGVERBOSE(("Sending pingpong %x from Submit\n", pingPong))
  229. status = HidpCallDriver(fdoExt->fdo, irp);
  230. KeSetEvent (&pingPong->sentEvent, 0, FALSE);
  231. *irpSent = TRUE;
  232. }
  233. if (PINGPONG_IMMEDIATE_READ != InterlockedExchange(&pingPong->ReadInterlock,
  234. PINGPONG_END_READ)) {
  235. //
  236. // The read is asynch, will call SubmitInterruptRead from the
  237. // completion routine
  238. //
  239. DBGVERBOSE(("read is pending\n"))
  240. break;
  241. } else {
  242. //
  243. // The read was synchronous (probably bytes in the buffer). The
  244. // completion routine will not call SubmitInterruptRead, so we
  245. // just loop here. This is to prevent us from running out of stack
  246. // space if always call StartRead from the completion routine
  247. //
  248. status = irp->IoStatus.Status;
  249. DBGVERBOSE(("read is looping with status %x\n", status))
  250. }
  251. } else {
  252. if (pingPong->weAreCancelling ){
  253. // We are stopping the read pump.
  254. // set this event and stop resending the pingpong IRP.
  255. DBGVERBOSE(("We are cancelling bit set for pingpong %x\n", pingPong))
  256. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  257. } else {
  258. /*
  259. * The device returned error.
  260. * In order to support slightly-broken devices which
  261. * "hiccup" occasionally, we implement a back-off timer
  262. * algorithm; this way, the device gets a second chance,
  263. * but if it spits back error each time, this doesn't
  264. * eat up all the available CPU.
  265. */
  266. DBGVERBOSE(("Queuing backoff timer on pingpong %x\n", pingPong))
  267. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  268. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  269. KeSetTimer( &pingPong->backoffTimer,
  270. pingPong->backoffTimerPeriod,
  271. &pingPong->backoffTimerDPC);
  272. }
  273. break;
  274. }
  275. }
  276. DBGSUCCESS(status, FALSE)
  277. return status;
  278. }
  279. /*
  280. ********************************************************************************
  281. * HidpProcessInterruptReport
  282. ********************************************************************************
  283. *
  284. * Take the new interrupt read report and either:
  285. * 1. If there is a pending read IRP, use it to satisfy that read IRP
  286. * and complete the read IRP
  287. *
  288. * or
  289. *
  290. * 2. If there is no pending read IRP,
  291. * queue the report for a future read.
  292. *
  293. */
  294. NTSTATUS HidpProcessInterruptReport(
  295. PHIDCLASS_COLLECTION collection,
  296. PHIDCLASS_FILE_EXTENSION FileExtension,
  297. PUCHAR Report,
  298. ULONG ReportLength,
  299. PIRP *irpToComplete
  300. )
  301. {
  302. KIRQL oldIrql;
  303. NTSTATUS result;
  304. PIRP readIrpToSatisfy;
  305. LockFileExtension(FileExtension, &oldIrql);
  306. /*
  307. * Dequeue the next interrupt read.
  308. */
  309. readIrpToSatisfy = DequeueInterruptReadIrp(collection, FileExtension);
  310. if (readIrpToSatisfy){
  311. /*
  312. * We have dequeued a pended read IRP
  313. * which we will complete with this report.
  314. */
  315. ULONG userReportLength;
  316. PCHAR pDest;
  317. PIO_STACK_LOCATION irpSp;
  318. NTSTATUS status;
  319. ASSERT(IsListEmpty(&FileExtension->ReportList));
  320. irpSp = IoGetCurrentIrpStackLocation(readIrpToSatisfy);
  321. pDest = HidpGetSystemAddressForMdlSafe(readIrpToSatisfy->MdlAddress);
  322. if(pDest) {
  323. userReportLength = irpSp->Parameters.Read.Length;
  324. status = HidpCopyInputReportToUser( FileExtension,
  325. Report,
  326. &userReportLength,
  327. pDest);
  328. DBGASSERT(NT_SUCCESS(status),
  329. ("HidpCopyInputReportToUser returned status = %x", status),
  330. TRUE)
  331. readIrpToSatisfy->IoStatus.Status = status;
  332. readIrpToSatisfy->IoStatus.Information = userReportLength;
  333. DBG_RECORD_READ(readIrpToSatisfy, userReportLength, (ULONG)Report[0], TRUE)
  334. result = status;
  335. } else {
  336. result = STATUS_INVALID_USER_BUFFER;
  337. readIrpToSatisfy->IoStatus.Status = result;
  338. }
  339. } else {
  340. /*
  341. * We don't have any pending read IRPs.
  342. * So queue this report for the next read.
  343. */
  344. PHIDCLASS_REPORT report;
  345. ULONG reportSize;
  346. reportSize = FIELD_OFFSET(HIDCLASS_REPORT, UnparsedReport) + ReportLength;
  347. report = ALLOCATEPOOL(NonPagedPool, reportSize);
  348. if (report){
  349. report->reportLength = ReportLength;
  350. RtlCopyMemory(report->UnparsedReport, Report, ReportLength);
  351. EnqueueInterruptReport(FileExtension, report);
  352. result = STATUS_PENDING;
  353. } else {
  354. result = STATUS_INSUFFICIENT_RESOURCES;
  355. }
  356. }
  357. UnlockFileExtension(FileExtension, oldIrql);
  358. /*
  359. * This function is called with the fileExtensionsList spinlock held.
  360. * So we can't complete the IRP here. Pass it back to the caller and it'll
  361. * be completed as soon as we drop all the spinlocks.
  362. */
  363. *irpToComplete = readIrpToSatisfy;
  364. DBGSUCCESS(result, FALSE)
  365. return result;
  366. }
  367. /*
  368. ********************************************************************************
  369. * HidpDistributeInterruptReport
  370. ********************************************************************************
  371. *
  372. *
  373. */
  374. VOID HidpDistributeInterruptReport(
  375. IN PHIDCLASS_COLLECTION hidclassCollection,
  376. PUCHAR Report,
  377. ULONG ReportLength
  378. )
  379. {
  380. PLIST_ENTRY listEntry;
  381. KIRQL oldIrql;
  382. LIST_ENTRY irpsToComplete;
  383. ULONG secureReadMode;
  384. #if DBG
  385. ULONG numRecipients = 0;
  386. ULONG numPending = 0;
  387. ULONG numFailed = 0;
  388. #endif
  389. InitializeListHead(&irpsToComplete);
  390. KeAcquireSpinLock(&hidclassCollection->FileExtensionListSpinLock, &oldIrql);
  391. listEntry = &hidclassCollection->FileExtensionList;
  392. secureReadMode = hidclassCollection->secureReadMode;
  393. while ((listEntry = listEntry->Flink) != &hidclassCollection->FileExtensionList){
  394. PIRP irpToComplete;
  395. PHIDCLASS_FILE_EXTENSION fileExtension = CONTAINING_RECORD(listEntry, HIDCLASS_FILE_EXTENSION, FileList);
  396. NTSTATUS status;
  397. //
  398. // This is to enforce security for devices such as a digitizer on a
  399. // tablet PC at the logon screen
  400. //
  401. if (secureReadMode && !fileExtension->isSecureOpen) {
  402. continue;
  403. }
  404. #if DBG
  405. status =
  406. #endif
  407. HidpProcessInterruptReport(hidclassCollection, fileExtension, Report, ReportLength, &irpToComplete);
  408. if (irpToComplete){
  409. InsertTailList(&irpsToComplete, &irpToComplete->Tail.Overlay.ListEntry);
  410. }
  411. #if DBG
  412. if (status == STATUS_SUCCESS){
  413. }
  414. else if (status == STATUS_PENDING){
  415. numPending++;
  416. }
  417. else {
  418. DBGSUCCESS(status, FALSE)
  419. numFailed++;
  420. }
  421. numRecipients++;
  422. #endif
  423. }
  424. DBG_LOG_REPORT(hidclassCollection->CollectionNumber, numRecipients, numPending, numFailed, Report, ReportLength)
  425. KeReleaseSpinLock(&hidclassCollection->FileExtensionListSpinLock, oldIrql);
  426. /*
  427. * Now that we've dropped all the spinlocks, complete all the dequeued read IRPs.
  428. */
  429. while (!IsListEmpty(&irpsToComplete)){
  430. PIRP irp;
  431. PLIST_ENTRY listEntry = RemoveHeadList(&irpsToComplete);
  432. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  433. IoCompleteRequest(irp, IO_KEYBOARD_INCREMENT);
  434. }
  435. }
  436. /*
  437. ********************************************************************************
  438. * GetPingPongFromIrp
  439. ********************************************************************************
  440. *
  441. *
  442. */
  443. HIDCLASS_PINGPONG *GetPingPongFromIrp(FDO_EXTENSION *fdoExt, PIRP irp)
  444. {
  445. HIDCLASS_PINGPONG *pingPong = NULL;
  446. ULONG i;
  447. for (i = 0; i < fdoExt->numPingPongs; i++){
  448. if (fdoExt->pingPongs[i].irp == irp){
  449. pingPong = &fdoExt->pingPongs[i];
  450. break;
  451. }
  452. }
  453. ASSERT(pingPong);
  454. return pingPong;
  455. }
  456. /*
  457. ********************************************************************************
  458. * HidpInterruptReadComplete
  459. ********************************************************************************
  460. *
  461. *
  462. */
  463. NTSTATUS HidpInterruptReadComplete(
  464. IN PDEVICE_OBJECT DeviceObject,
  465. IN PIRP Irp,
  466. IN PVOID Context
  467. )
  468. {
  469. FDO_EXTENSION *fdoExt = (FDO_EXTENSION *)Context;
  470. HIDCLASS_PINGPONG *pingPong;
  471. KIRQL irql;
  472. BOOLEAN startRead;
  473. DBG_COMMON_ENTRY()
  474. DBGLOG_INTSTART()
  475. //
  476. // Track the number of outstanding requests to this device.
  477. //
  478. ASSERT(fdoExt->outstandingRequests > 0 );
  479. InterlockedDecrement(&fdoExt->outstandingRequests);
  480. pingPong = GetPingPongFromIrp(fdoExt, Irp);
  481. if (!pingPong) {
  482. //
  483. // Something is terribly wrong, but do nothing. Hopefully
  484. // just exiting will clear up this pimple.
  485. //
  486. DBGERR(("A pingPong structure could not be found!!! Have this looked at!"))
  487. goto InterruptReadCompleteExit;
  488. }
  489. //
  490. // If ReadInterlock is == START_READ, this func has been completed
  491. // synchronously. Place IMMEDIATE_READ into the interlock to signify this
  492. // situation; this will notify StartRead to loop when IoCallDriver returns.
  493. // Otherwise, we have been completed async and it is safe to call StartRead()
  494. //
  495. startRead =
  496. (PINGPONG_START_READ !=
  497. InterlockedCompareExchange(&pingPong->ReadInterlock,
  498. PINGPONG_IMMEDIATE_READ,
  499. PINGPONG_START_READ));
  500. /*
  501. * Take appropriate action based on the completion code of this pingpong irp.
  502. */
  503. if (NT_SUCCESS(Irp->IoStatus.Status)){
  504. /*
  505. * We've read one or more input reports.
  506. * They are sitting consecutively in Irp->UserBuffer.
  507. */
  508. PUCHAR reportStart = Irp->UserBuffer;
  509. LONG bytesRemaining = (LONG)Irp->IoStatus.Information;
  510. DBGASSERT(bytesRemaining > 0, ("BAD HARDWARE. Device returned zero bytes. If this happens repeatedly, remove device."), FALSE);
  511. /*
  512. * Deliver each report separately.
  513. */
  514. while (bytesRemaining > 0){
  515. UCHAR reportId;
  516. PHIDP_REPORT_IDS reportIdentifier;
  517. /*
  518. * If the first report ID is 0, then there is only one report id
  519. * and it is known implicitly by the device, so it is not included
  520. * in the reports sent to or from the device.
  521. * Otherwise, there are multiple report ids and the report id is the
  522. * first byte of the report.
  523. */
  524. if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0){
  525. /*
  526. * This device has only a single input report ID, so call it report id 0;
  527. */
  528. reportId = 0;
  529. }
  530. else {
  531. /*
  532. * This device has multiple input report IDs, so each report
  533. * begins with a UCHAR report ID.
  534. */
  535. reportId = *reportStart;
  536. DBGASSERT(reportId,
  537. ("Bad Hardware. Not returning a report id although it has multiple ids."),
  538. FALSE) // Bad hardware, bug 354829.
  539. reportStart += sizeof(UCHAR);
  540. bytesRemaining--;
  541. }
  542. /*
  543. * Extract the report identifier with the given id from the HID device extension.
  544. */
  545. reportIdentifier = GetReportIdentifier(fdoExt, reportId);
  546. if (reportIdentifier){
  547. LONG reportDataLen = (reportId ?
  548. reportIdentifier->InputLength-1 :
  549. reportIdentifier->InputLength);
  550. if ((reportDataLen > 0) && (reportDataLen <= bytesRemaining)){
  551. PHIDCLASS_COLLECTION collection;
  552. PHIDP_COLLECTION_DESC hidCollectionDesc;
  553. /*
  554. * This report represents the state of some collection on the device.
  555. * Find that collection.
  556. */
  557. collection = GetHidclassCollection( fdoExt,
  558. reportIdentifier->CollectionNumber);
  559. hidCollectionDesc = GetCollectionDesc( fdoExt,
  560. reportIdentifier->CollectionNumber);
  561. if (collection && hidCollectionDesc){
  562. PDO_EXTENSION *pdoExt;
  563. /*
  564. * The collection's inputLength is the size of the
  565. * largest report (including report id); so it should
  566. * be at least as big as this one.
  567. */
  568. ASSERT(hidCollectionDesc->InputLength >= reportDataLen+1);
  569. /*
  570. * Make sure that the PDO for this collection has gotten
  571. * START_DEVICE before returning anything for it.
  572. * (collection-PDOs can get REMOVE_DEVICE/START_DEVICE intermittently).
  573. */
  574. if (ISPTR(fdoExt->collectionPdoExtensions)
  575. && ISPTR(fdoExt->collectionPdoExtensions[collection->CollectionIndex])) {
  576. pdoExt = &fdoExt->collectionPdoExtensions[collection->CollectionIndex]->pdoExt;
  577. ASSERT(ISPTR(pdoExt));
  578. if (pdoExt->state == COLLECTION_STATE_RUNNING){
  579. /*
  580. * "Cook" the report
  581. * (if it doesn't already have a report id byte, add one).
  582. */
  583. ASSERT(ISPTR(collection->cookedInterruptReportBuf));
  584. collection->cookedInterruptReportBuf[0] = reportId;
  585. RtlCopyMemory( collection->cookedInterruptReportBuf+1,
  586. reportStart,
  587. reportDataLen);
  588. /*
  589. * If this report contains a power-button event, alert this system.
  590. */
  591. CheckReportPowerEvent( fdoExt,
  592. collection,
  593. collection->cookedInterruptReportBuf,
  594. hidCollectionDesc->InputLength);
  595. /*
  596. * Distribute the report to all of the open file objects on this collection.
  597. */
  598. HidpDistributeInterruptReport(collection,
  599. collection->cookedInterruptReportBuf,
  600. hidCollectionDesc->InputLength);
  601. }
  602. else {
  603. DBGVERBOSE(("Report dropped because collection-PDO not started (pdoExt->state = %d).", pdoExt->state))
  604. }
  605. }
  606. else {
  607. DBGVERBOSE(("Report dropped because collection-PDO doesn't exist"))
  608. }
  609. }
  610. else {
  611. // PDO hasn't been initialized yet. Throw away data.
  612. DBGVERBOSE(("Report dropped because collection-PDO not initialized."))
  613. // TRAP;
  614. break;
  615. }
  616. }
  617. else {
  618. DBGASSERT(reportDataLen > 0, ("Device returning report id with zero-length input report as part of input data."), FALSE)
  619. if (reportDataLen > bytesRemaining) {
  620. DBGVERBOSE(("Device has corrupt input report"));
  621. }
  622. break;
  623. }
  624. /*
  625. * Move to the next report in the buffer.
  626. */
  627. bytesRemaining -= reportDataLen;
  628. reportStart += reportDataLen;
  629. }
  630. else {
  631. //
  632. // We have thrown away data because we couldn't find a report
  633. // identifier corresponding to this data that we've been
  634. // returned. Bad hardware, bug 354829.
  635. //
  636. break;
  637. }
  638. }
  639. /*
  640. * The read succeeded.
  641. * Reset the backoff timer stuff (for when reads fail)
  642. * and re-submit this ping-pong IRP.
  643. */
  644. pingPong->backoffTimerPeriod.HighPart = -1;
  645. pingPong->backoffTimerPeriod.LowPart = -10000000;
  646. }
  647. //
  648. // Business as usual.
  649. //
  650. if (startRead) {
  651. if (pingPong->weAreCancelling ){
  652. // We are stopping the read pump.
  653. // Set this event and stop resending the pingpong IRP.
  654. DBGVERBOSE(("We are cancelling bit set for pingpong %x\n", pingPong))
  655. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  656. } else {
  657. if (NT_SUCCESS(Irp->IoStatus.Status)){
  658. BOOLEAN irpSent;
  659. DBGVERBOSE(("Submitting pingpong %x from completion routine\n", pingPong))
  660. HidpSubmitInterruptRead(fdoExt, pingPong, &irpSent);
  661. } else {
  662. /*
  663. * The device returned error.
  664. * In order to support slightly-broken devices which
  665. * "hiccup" occasionally, we implement a back-off timer
  666. * algorithm; this way, the device gets a second chance,
  667. * but if it spits back error each time, this doesn't
  668. * eat up all the available CPU.
  669. */
  670. #if DBG
  671. if (dbgTrapOnHiccup){
  672. DBGERR(("Device 'hiccuped' (status=%xh); setting backoff timer (fdoExt=%ph)...", Irp->IoStatus.Status, fdoExt))
  673. }
  674. #endif
  675. DBGVERBOSE(("Device returned error %x on pingpong %x\n", Irp->IoStatus.Status, pingPong))
  676. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  677. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  678. KeSetTimer( &pingPong->backoffTimer,
  679. pingPong->backoffTimerPeriod,
  680. &pingPong->backoffTimerDPC);
  681. }
  682. }
  683. }
  684. InterruptReadCompleteExit:
  685. DBGLOG_INTEND()
  686. DBG_COMMON_EXIT()
  687. /*
  688. * ALWAYS return STATUS_MORE_PROCESSING_REQUIRED;
  689. * otherwise, the irp is required to have a thread.
  690. */
  691. return STATUS_MORE_PROCESSING_REQUIRED;
  692. }
  693. /*
  694. ********************************************************************************
  695. * HidpStartAllPingPongs
  696. ********************************************************************************
  697. *
  698. *
  699. */
  700. NTSTATUS HidpStartAllPingPongs(FDO_EXTENSION *fdoExt)
  701. {
  702. NTSTATUS status = STATUS_SUCCESS;
  703. ULONG i;
  704. ASSERT(fdoExt->numPingPongs > 0);
  705. for (i = 0; i < fdoExt->numPingPongs; i++){
  706. BOOLEAN irpSent;
  707. // Different threads may be trying to start this pump at the
  708. // same time due to idle notification. Must only start once.
  709. if (fdoExt->pingPongs[i].pumpDoneEvent.Header.SignalState) {
  710. fdoExt->pingPongs[i].ReadInterlock = PINGPONG_END_READ;
  711. KeResetEvent(&fdoExt->pingPongs[i].pumpDoneEvent);
  712. DBGVERBOSE(("Starting pingpong %x from HidpStartAllPingPongs\n", &fdoExt->pingPongs[i]))
  713. status = HidpSubmitInterruptRead(fdoExt, &fdoExt->pingPongs[i], &irpSent);
  714. if (!NT_SUCCESS(status)){
  715. if (irpSent){
  716. DBGWARN(("Initial read failed with status %xh.", status))
  717. #if DBG
  718. if (dbgTrapOnHiccup){
  719. DBGERR(("Device 'hiccuped' ?? (fdoExt=%ph).", fdoExt))
  720. }
  721. #endif
  722. /*
  723. * We'll let the back-off logic in the completion
  724. * routine deal with this.
  725. */
  726. status = STATUS_SUCCESS;
  727. }
  728. else {
  729. DBGERR(("Initial read failed, irp not sent, status = %xh.", status))
  730. break;
  731. }
  732. }
  733. }
  734. }
  735. if (status == STATUS_PENDING){
  736. status = STATUS_SUCCESS;
  737. }
  738. DBGSUCCESS(status, TRUE)
  739. return status;
  740. }
  741. /*
  742. ********************************************************************************
  743. * CancelAllPingPongIrps
  744. ********************************************************************************
  745. *
  746. *
  747. */
  748. VOID CancelAllPingPongIrps(FDO_EXTENSION *fdoExt)
  749. {
  750. ULONG i;
  751. for (i = 0; i < fdoExt->numPingPongs; i++){
  752. HIDCLASS_PINGPONG *pingPong = &fdoExt->pingPongs[i];
  753. DBGVERBOSE(("Cancelling pingpong %x\n", pingPong))
  754. ASSERT(pingPong->sig == PINGPONG_SIG);
  755. ASSERT(!pingPong->weAreCancelling);
  756. //
  757. // The order of the following instructions is crucial. We must set
  758. // the weAreCancelling bit before waiting on the sentEvent, and the
  759. // last thing that we should wait on is the pumpDoneEvent, which
  760. // indicates that the read loop has finished all reads and will never
  761. // run again.
  762. //
  763. // Note that we don't need spinlocks to guard since we only have two
  764. // threads touching pingpong structures; the read pump thread and the
  765. // pnp thread. PNP irps are synchronous, so those are safe. Using the
  766. // weAreCancelling bit and the two events, sentEvent and pumpDoneEvent,
  767. // the pnp irps are synchronized with the pnp routines. This insures
  768. // that this cancel routine doesn't exit until the read pump has
  769. // signalled the pumpDoneEvent and exited, hence the pingpong
  770. // structures aren't ripped out from underneath it.
  771. //
  772. // If we have a backoff timer queued, it will eventually fire and
  773. // call the submitinterruptread routine to restart reads. This will
  774. // exit eventually, because we have set the weAreCancelling bit.
  775. //
  776. InterlockedIncrement(&pingPong->weAreCancelling);
  777. {
  778. /*
  779. * Synchronize with the irp's completion routine.
  780. */
  781. #if DBG
  782. UCHAR beforeIrql = KeGetCurrentIrql();
  783. UCHAR afterIrql;
  784. PVOID cancelRoutine = (PVOID)pingPong->irp->CancelRoutine;
  785. #endif
  786. KeWaitForSingleObject(&pingPong->sentEvent,
  787. Executive, // wait reason
  788. KernelMode,
  789. FALSE, // not alertable
  790. NULL ); // no timeout
  791. DBGVERBOSE(("Pingpong sent event set for pingpong %x\n", pingPong))
  792. IoCancelIrp(pingPong->irp);
  793. #if DBG
  794. afterIrql = KeGetCurrentIrql();
  795. if (afterIrql != beforeIrql){
  796. DBGERR(("CancelAllPingPongIrps: cancel routine at %ph changed irql from %d to %d.", cancelRoutine, beforeIrql, afterIrql))
  797. }
  798. #endif
  799. }
  800. /*
  801. * Cancelling the IRP causes a lower driver to
  802. * complete it (either in a cancel routine or when
  803. * the driver checks Irp->Cancel just before queueing it).
  804. * Wait for the IRP to actually get cancelled.
  805. */
  806. KeWaitForSingleObject( &pingPong->pumpDoneEvent,
  807. Executive, // wait reason
  808. KernelMode,
  809. FALSE, // not alertable
  810. NULL ); // no timeout
  811. InterlockedDecrement(&pingPong->weAreCancelling);
  812. DBGVERBOSE(("Pingpong pump done event set for %x\n", pingPong))
  813. }
  814. }
  815. /*
  816. ********************************************************************************
  817. * DestroyPingPongs
  818. ********************************************************************************
  819. *
  820. *
  821. */
  822. VOID DestroyPingPongs(FDO_EXTENSION *fdoExt)
  823. {
  824. if (ISPTR(fdoExt->pingPongs)){
  825. ULONG i;
  826. CancelAllPingPongIrps(fdoExt);
  827. for (i = 0; i < fdoExt->numPingPongs; i++){
  828. IoFreeIrp(fdoExt->pingPongs[i].irp);
  829. ExFreePool(fdoExt->pingPongs[i].reportBuffer);
  830. #if DBG
  831. fdoExt->pingPongs[i].sig = 0xDEADBEEF;
  832. #endif
  833. }
  834. ExFreePool(fdoExt->pingPongs);
  835. fdoExt->pingPongs = BAD_POINTER;
  836. }
  837. }
  838. /*
  839. ********************************************************************************
  840. * HidpPingpongBackoffTimerDpc
  841. ********************************************************************************
  842. *
  843. *
  844. *
  845. */
  846. VOID HidpPingpongBackoffTimerDpc(
  847. IN PKDPC Dpc,
  848. IN PVOID DeferredContext,
  849. IN PVOID SystemArgument1,
  850. IN PVOID SystemArgument2
  851. )
  852. {
  853. HIDCLASS_PINGPONG *pingPong = (HIDCLASS_PINGPONG *)DeferredContext;
  854. BOOLEAN irpSent;
  855. ASSERT(pingPong->sig == PINGPONG_SIG);
  856. /*
  857. * Increase the back-off time by 1 second, up to a max of 5 secs
  858. * (in negative 100-nanosecond units).
  859. */
  860. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  861. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  862. if ((LONG)pingPong->backoffTimerPeriod.LowPart > -50000000){
  863. (LONG)pingPong->backoffTimerPeriod.LowPart -= 10000000;
  864. }
  865. DBGVERBOSE(("Submitting Pingpong %x from backoff\n", pingPong))
  866. //
  867. // If we are being removed, or the CancelAllPingPongIrps has been called,
  868. // this call will take care of things.
  869. //
  870. HidpSubmitInterruptRead(pingPong->myFdoExt, pingPong, &irpSent);
  871. }