Source code of Windows XP (NT5)
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.

1044 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. InterlockedDecrement(&pingPong->weAreCancelling);
  217. //
  218. // Ordering of the next two instructions is crucial, since
  219. // CancelPingPongs will exit after pumpDoneEvent is set, and the
  220. // pingPongs could be deleted after that.
  221. //
  222. DBGVERBOSE(("Pingpong %x cancelled in submit before sending\n", pingPong))
  223. KeSetEvent (&pingPong->sentEvent, 0, FALSE);
  224. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  225. status = STATUS_CANCELLED;
  226. break;
  227. } else {
  228. fdoExt->outstandingRequests++;
  229. DBGVERBOSE(("Sending pingpong %x from Submit\n", pingPong))
  230. status = HidpCallDriver(fdoExt->fdo, irp);
  231. KeSetEvent (&pingPong->sentEvent, 0, FALSE);
  232. *irpSent = TRUE;
  233. }
  234. if (PINGPONG_IMMEDIATE_READ != InterlockedExchange(&pingPong->ReadInterlock,
  235. PINGPONG_END_READ)) {
  236. //
  237. // The read is asynch, will call SubmitInterruptRead from the
  238. // completion routine
  239. //
  240. DBGVERBOSE(("read is pending\n"))
  241. break;
  242. } else {
  243. //
  244. // The read was synchronous (probably bytes in the buffer). The
  245. // completion routine will not call SubmitInterruptRead, so we
  246. // just loop here. This is to prevent us from running out of stack
  247. // space if always call StartRead from the completion routine
  248. //
  249. status = irp->IoStatus.Status;
  250. DBGVERBOSE(("read is looping with status %x\n", status))
  251. }
  252. } else {
  253. if (pingPong->weAreCancelling ){
  254. // We are stopping the read pump.
  255. // set this event and stop resending the pingpong IRP.
  256. DBGVERBOSE(("We are cancelling bit set for pingpong %x\n", pingPong))
  257. InterlockedDecrement(&pingPong->weAreCancelling);
  258. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  259. } else {
  260. /*
  261. * The device returned error.
  262. * In order to support slightly-broken devices which
  263. * "hiccup" occasionally, we implement a back-off timer
  264. * algorithm; this way, the device gets a second chance,
  265. * but if it spits back error each time, this doesn't
  266. * eat up all the available CPU.
  267. */
  268. DBGVERBOSE(("Queuing backoff timer on pingpong %x\n", pingPong))
  269. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  270. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  271. KeSetTimer( &pingPong->backoffTimer,
  272. pingPong->backoffTimerPeriod,
  273. &pingPong->backoffTimerDPC);
  274. }
  275. break;
  276. }
  277. }
  278. DBGSUCCESS(status, FALSE)
  279. return status;
  280. }
  281. /*
  282. ********************************************************************************
  283. * HidpProcessInterruptReport
  284. ********************************************************************************
  285. *
  286. * Take the new interrupt read report and either:
  287. * 1. If there is a pending read IRP, use it to satisfy that read IRP
  288. * and complete the read IRP
  289. *
  290. * or
  291. *
  292. * 2. If there is no pending read IRP,
  293. * queue the report for a future read.
  294. *
  295. */
  296. NTSTATUS HidpProcessInterruptReport(
  297. PHIDCLASS_COLLECTION collection,
  298. PHIDCLASS_FILE_EXTENSION FileExtension,
  299. PUCHAR Report,
  300. ULONG ReportLength,
  301. PIRP *irpToComplete
  302. )
  303. {
  304. KIRQL oldIrql;
  305. NTSTATUS result;
  306. PIRP readIrpToSatisfy;
  307. BOOLEAN calledBlueScreenFunc = FALSE;
  308. LockFileExtension(FileExtension, &oldIrql);
  309. if (FileExtension->BlueScreenData.BluescreenFunction &&
  310. *(FileExtension->BlueScreenData.IsBluescreenTime) ) {
  311. (*FileExtension->BlueScreenData.BluescreenFunction)(
  312. FileExtension->BlueScreenData.Context,
  313. Report
  314. );
  315. calledBlueScreenFunc = TRUE;
  316. readIrpToSatisfy = NULL;
  317. result = STATUS_SUCCESS;
  318. }
  319. if (!calledBlueScreenFunc){
  320. /*
  321. * Dequeue the next interrupt read.
  322. */
  323. readIrpToSatisfy = DequeueInterruptReadIrp(collection, FileExtension);
  324. if (readIrpToSatisfy){
  325. /*
  326. * We have dequeued a pended read IRP
  327. * which we will complete with this report.
  328. */
  329. ULONG userReportLength;
  330. PCHAR pDest;
  331. PIO_STACK_LOCATION irpSp;
  332. NTSTATUS status;
  333. ASSERT(IsListEmpty(&FileExtension->ReportList));
  334. irpSp = IoGetCurrentIrpStackLocation(readIrpToSatisfy);
  335. pDest = HidpGetSystemAddressForMdlSafe(readIrpToSatisfy->MdlAddress);
  336. if(pDest) {
  337. userReportLength = irpSp->Parameters.Read.Length;
  338. status = HidpCopyInputReportToUser( FileExtension,
  339. Report,
  340. &userReportLength,
  341. pDest);
  342. DBGASSERT(NT_SUCCESS(status),
  343. ("HidpCopyInputReportToUser returned status = %x", status),
  344. TRUE)
  345. readIrpToSatisfy->IoStatus.Status = status;
  346. readIrpToSatisfy->IoStatus.Information = userReportLength;
  347. DBG_RECORD_READ(readIrpToSatisfy, userReportLength, (ULONG)Report[0], TRUE)
  348. result = status;
  349. }
  350. else {
  351. result = STATUS_INVALID_USER_BUFFER;
  352. }
  353. }
  354. else {
  355. /*
  356. * We don't have any pending read IRPs.
  357. * So queue this report for the next read.
  358. */
  359. PHIDCLASS_REPORT report;
  360. ULONG reportSize;
  361. reportSize = FIELD_OFFSET(HIDCLASS_REPORT, UnparsedReport) + ReportLength;
  362. report = ALLOCATEPOOL(NonPagedPool, reportSize);
  363. if (report){
  364. report->reportLength = ReportLength;
  365. RtlCopyMemory(report->UnparsedReport, Report, ReportLength);
  366. EnqueueInterruptReport(FileExtension, report);
  367. result = STATUS_PENDING;
  368. }
  369. else {
  370. result = STATUS_INSUFFICIENT_RESOURCES;
  371. }
  372. }
  373. }
  374. UnlockFileExtension(FileExtension, oldIrql);
  375. /*
  376. * This function is called with the fileExtensionsList spinlock held.
  377. * So we can't complete the IRP here. Pass it back to the caller and it'll
  378. * be completed as soon as we drop all the spinlocks.
  379. */
  380. *irpToComplete = readIrpToSatisfy;
  381. DBGSUCCESS(result, TRUE)
  382. return result;
  383. }
  384. /*
  385. ********************************************************************************
  386. * HidpDistributeInterruptReport
  387. ********************************************************************************
  388. *
  389. *
  390. */
  391. VOID HidpDistributeInterruptReport(
  392. IN PHIDCLASS_COLLECTION hidclassCollection,
  393. PUCHAR Report,
  394. ULONG ReportLength
  395. )
  396. {
  397. PLIST_ENTRY listEntry;
  398. KIRQL oldIrql;
  399. LIST_ENTRY irpsToComplete;
  400. ULONG secureReadMode;
  401. #if DBG
  402. ULONG numRecipients = 0;
  403. ULONG numPending = 0;
  404. ULONG numFailed = 0;
  405. #endif
  406. InitializeListHead(&irpsToComplete);
  407. KeAcquireSpinLock(&hidclassCollection->FileExtensionListSpinLock, &oldIrql);
  408. listEntry = &hidclassCollection->FileExtensionList;
  409. secureReadMode = hidclassCollection->secureReadMode;
  410. while ((listEntry = listEntry->Flink) != &hidclassCollection->FileExtensionList){
  411. PIRP irpToComplete;
  412. PHIDCLASS_FILE_EXTENSION fileExtension = CONTAINING_RECORD(listEntry, HIDCLASS_FILE_EXTENSION, FileList);
  413. NTSTATUS status;
  414. //
  415. // This is to enforce security for devices such as a digitizer on a
  416. // tablet PC at the logon screen
  417. //
  418. if (secureReadMode && !fileExtension->isSecureOpen) {
  419. continue;
  420. }
  421. #if DBG
  422. status =
  423. #endif
  424. HidpProcessInterruptReport(hidclassCollection, fileExtension, Report, ReportLength, &irpToComplete);
  425. if (irpToComplete){
  426. InsertTailList(&irpsToComplete, &irpToComplete->Tail.Overlay.ListEntry);
  427. }
  428. #if DBG
  429. if (status == STATUS_SUCCESS){
  430. }
  431. else if (status == STATUS_PENDING){
  432. numPending++;
  433. }
  434. else {
  435. DBGSUCCESS(status, FALSE)
  436. numFailed++;
  437. }
  438. numRecipients++;
  439. #endif
  440. }
  441. DBG_LOG_REPORT(hidclassCollection->CollectionNumber, numRecipients, numPending, numFailed, Report, ReportLength)
  442. KeReleaseSpinLock(&hidclassCollection->FileExtensionListSpinLock, oldIrql);
  443. /*
  444. * Now that we've dropped all the spinlocks, complete all the dequeued read IRPs.
  445. */
  446. while (!IsListEmpty(&irpsToComplete)){
  447. PIRP irp;
  448. PLIST_ENTRY listEntry = RemoveHeadList(&irpsToComplete);
  449. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  450. IoCompleteRequest(irp, IO_KEYBOARD_INCREMENT);
  451. }
  452. }
  453. /*
  454. ********************************************************************************
  455. * GetPingPongFromIrp
  456. ********************************************************************************
  457. *
  458. *
  459. */
  460. HIDCLASS_PINGPONG *GetPingPongFromIrp(FDO_EXTENSION *fdoExt, PIRP irp)
  461. {
  462. HIDCLASS_PINGPONG *pingPong = NULL;
  463. ULONG i;
  464. for (i = 0; i < fdoExt->numPingPongs; i++){
  465. if (fdoExt->pingPongs[i].irp == irp){
  466. pingPong = &fdoExt->pingPongs[i];
  467. break;
  468. }
  469. }
  470. ASSERT(pingPong);
  471. return pingPong;
  472. }
  473. /*
  474. ********************************************************************************
  475. * HidpInterruptReadComplete
  476. ********************************************************************************
  477. *
  478. *
  479. */
  480. NTSTATUS HidpInterruptReadComplete(
  481. IN PDEVICE_OBJECT DeviceObject,
  482. IN PIRP Irp,
  483. IN PVOID Context
  484. )
  485. {
  486. FDO_EXTENSION *fdoExt = (FDO_EXTENSION *)Context;
  487. HIDCLASS_PINGPONG *pingPong;
  488. KIRQL oldIrql;
  489. BOOLEAN startRead;
  490. DBG_COMMON_ENTRY()
  491. DBGLOG_INTSTART()
  492. //
  493. // Track the number of outstanding requests to this device.
  494. //
  495. ASSERT(fdoExt->outstandingRequests > 0 );
  496. fdoExt->outstandingRequests--;
  497. pingPong = GetPingPongFromIrp(fdoExt, Irp);
  498. if (!pingPong) {
  499. //
  500. // Something is terribly wrong, but do nothing. Hopefully
  501. // just exiting will clear up this pimple.
  502. //
  503. DBGERR(("A pingPong structure could not be found!!! Have this looked at!"))
  504. goto InterruptReadCompleteExit;
  505. }
  506. //
  507. // If ReadInterlock is == START_READ, this func has been completed
  508. // synchronously. Place IMMEDIATE_READ into the interlock to signify this
  509. // situation; this will notify StartRead to loop when IoCallDriver returns.
  510. // Otherwise, we have been completed async and it is safe to call StartRead()
  511. //
  512. startRead =
  513. (PINGPONG_START_READ !=
  514. InterlockedCompareExchange(&pingPong->ReadInterlock,
  515. PINGPONG_IMMEDIATE_READ,
  516. PINGPONG_START_READ));
  517. /*
  518. * Take appropriate action based on the completion code of this pingpong irp.
  519. */
  520. if (Irp->IoStatus.Status == STATUS_SUCCESS){
  521. /*
  522. * We've read one or more input reports.
  523. * They are sitting consecutively in Irp->UserBuffer.
  524. */
  525. PUCHAR reportStart = Irp->UserBuffer;
  526. LONG bytesRemaining = (LONG)Irp->IoStatus.Information;
  527. DBGASSERT(bytesRemaining > 0, ("BAD HARDWARE. Device returned zero bytes. If this happens repeatedly, remove device."), FALSE);
  528. /*
  529. * Deliver each report separately.
  530. */
  531. while (bytesRemaining > 0){
  532. UCHAR reportId;
  533. PHIDP_REPORT_IDS reportIdentifier;
  534. /*
  535. * If the first report ID is 0, then there is only one report id
  536. * and it is known implicitly by the device, so it is not included
  537. * in the reports sent to or from the device.
  538. * Otherwise, there are multiple report ids and the report id is the
  539. * first byte of the report.
  540. */
  541. if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0){
  542. /*
  543. * This device has only a single input report ID, so call it report id 0;
  544. */
  545. reportId = 0;
  546. }
  547. else {
  548. /*
  549. * This device has multiple input report IDs, so each report
  550. * begins with a UCHAR report ID.
  551. */
  552. reportId = *reportStart;
  553. DBGASSERT(reportId,
  554. ("Bad Hardware. Not returning a report id although it has multiple ids."),
  555. FALSE) // Bad hardware, bug 354829.
  556. reportStart += sizeof(UCHAR);
  557. bytesRemaining--;
  558. }
  559. /*
  560. * Extract the report identifier with the given id from the HID device extension.
  561. */
  562. reportIdentifier = GetReportIdentifier(fdoExt, reportId);
  563. if (reportIdentifier){
  564. LONG reportDataLen = (reportId ?
  565. reportIdentifier->InputLength-1 :
  566. reportIdentifier->InputLength);
  567. if ((reportDataLen > 0) && (reportDataLen <= bytesRemaining)){
  568. PHIDCLASS_COLLECTION collection;
  569. PHIDP_COLLECTION_DESC hidCollectionDesc;
  570. /*
  571. * This report represents the state of some collection on the device.
  572. * Find that collection.
  573. */
  574. collection = GetHidclassCollection( fdoExt,
  575. reportIdentifier->CollectionNumber);
  576. hidCollectionDesc = GetCollectionDesc( fdoExt,
  577. reportIdentifier->CollectionNumber);
  578. if (collection && hidCollectionDesc){
  579. PDO_EXTENSION *pdoExt;
  580. /*
  581. * The collection's inputLength is the size of the
  582. * largest report (including report id); so it should
  583. * be at least as big as this one.
  584. */
  585. ASSERT(hidCollectionDesc->InputLength >= reportDataLen+1);
  586. /*
  587. * Make sure that the PDO for this collection has gotten
  588. * START_DEVICE before returning anything for it.
  589. * (collection-PDOs can get REMOVE_DEVICE/START_DEVICE intermittently).
  590. */
  591. pdoExt = &fdoExt->collectionPdoExtensions[collection->CollectionIndex]->pdoExt;
  592. ASSERT(ISPTR(pdoExt));
  593. if (pdoExt->state == COLLECTION_STATE_RUNNING){
  594. /*
  595. * "Cook" the report
  596. * (if it doesn't already have a report id byte, add one).
  597. */
  598. ASSERT(ISPTR(collection->cookedInterruptReportBuf));
  599. collection->cookedInterruptReportBuf[0] = reportId;
  600. RtlCopyMemory( collection->cookedInterruptReportBuf+1,
  601. reportStart,
  602. reportDataLen);
  603. /*
  604. * If this report contains a power-button event, alert this system.
  605. */
  606. CheckReportPowerEvent( fdoExt,
  607. collection,
  608. collection->cookedInterruptReportBuf,
  609. hidCollectionDesc->InputLength);
  610. /*
  611. * Distribute the report to all of the open file objects on this collection.
  612. */
  613. HidpDistributeInterruptReport(collection,
  614. collection->cookedInterruptReportBuf,
  615. hidCollectionDesc->InputLength);
  616. }
  617. else {
  618. DBGVERBOSE(("Report dropped because collection-PDO not started (pdoExt->state = %d).", pdoExt->state))
  619. }
  620. }
  621. else {
  622. // PDO hasn't been initialized yet. Throw away data.
  623. DBGVERBOSE(("Report dropped because collection-PDO not initialized."))
  624. // TRAP;
  625. break;
  626. }
  627. }
  628. else {
  629. DBGASSERT(reportDataLen > 0, ("Device returning report id with zero-length input report as part of input data."), FALSE)
  630. if (reportDataLen > bytesRemaining) {
  631. DBGVERBOSE(("Device has corrupt input report"));
  632. }
  633. break;
  634. }
  635. /*
  636. * Move to the next report in the buffer.
  637. */
  638. bytesRemaining -= reportDataLen;
  639. reportStart += reportDataLen;
  640. }
  641. else {
  642. //
  643. // We have thrown away data because we couldn't find a report
  644. // identifier corresponding to this data that we've been
  645. // returned. Bad hardware, bug 354829.
  646. //
  647. break;
  648. }
  649. }
  650. /*
  651. * The read succeeded.
  652. * Reset the backoff timer stuff (for when reads fail)
  653. * and re-submit this ping-pong IRP.
  654. */
  655. pingPong->backoffTimerPeriod.HighPart = -1;
  656. pingPong->backoffTimerPeriod.LowPart = -10000000;
  657. }
  658. //
  659. // Business as usual.
  660. //
  661. if (startRead) {
  662. if (pingPong->weAreCancelling ){
  663. // We are stopping the read pump.
  664. // Set this event and stop resending the pingpong IRP.
  665. DBGVERBOSE(("We are cancelling bit set for pingpong %x\n", pingPong))
  666. InterlockedDecrement(&pingPong->weAreCancelling);
  667. KeSetEvent(&pingPong->pumpDoneEvent, 0, FALSE);
  668. } else {
  669. if (Irp->IoStatus.Status == STATUS_SUCCESS){
  670. BOOLEAN irpSent;
  671. DBGVERBOSE(("Submitting pingpong %x from completion routine\n", pingPong))
  672. HidpSubmitInterruptRead(fdoExt, pingPong, &irpSent);
  673. } else {
  674. /*
  675. * The device returned error.
  676. * In order to support slightly-broken devices which
  677. * "hiccup" occasionally, we implement a back-off timer
  678. * algorithm; this way, the device gets a second chance,
  679. * but if it spits back error each time, this doesn't
  680. * eat up all the available CPU.
  681. */
  682. #if DBG
  683. if (dbgTrapOnHiccup){
  684. DBGERR(("Device 'hiccuped' (status=%xh); setting backoff timer (fdoExt=%ph)...", Irp->IoStatus.Status, fdoExt))
  685. }
  686. #endif
  687. DBGVERBOSE(("Device returned error %x on pingpong %x\n", Irp->IoStatus.Status, pingPong))
  688. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  689. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  690. KeSetTimer( &pingPong->backoffTimer,
  691. pingPong->backoffTimerPeriod,
  692. &pingPong->backoffTimerDPC);
  693. }
  694. }
  695. }
  696. InterruptReadCompleteExit:
  697. DBGLOG_INTEND()
  698. DBG_COMMON_EXIT()
  699. /*
  700. * ALWAYS return STATUS_MORE_PROCESSING_REQUIRED;
  701. * otherwise, the irp is required to have a thread.
  702. */
  703. return STATUS_MORE_PROCESSING_REQUIRED;
  704. }
  705. /*
  706. ********************************************************************************
  707. * HidpStartAllPingPongs
  708. ********************************************************************************
  709. *
  710. *
  711. */
  712. NTSTATUS HidpStartAllPingPongs(FDO_EXTENSION *fdoExt)
  713. {
  714. NTSTATUS status = STATUS_SUCCESS;
  715. ULONG i;
  716. ASSERT(fdoExt->numPingPongs > 0);
  717. for (i = 0; i < fdoExt->numPingPongs; i++){
  718. BOOLEAN irpSent;
  719. // Different threads may be trying to start this pump at the
  720. // same time due to idle notification. Must only start once.
  721. if (fdoExt->pingPongs[i].pumpDoneEvent.Header.SignalState) {
  722. fdoExt->pingPongs[i].ReadInterlock = PINGPONG_END_READ;
  723. KeResetEvent(&fdoExt->pingPongs[i].pumpDoneEvent);
  724. DBGVERBOSE(("Starting pingpong %x from HidpStartAllPingPongs\n", &fdoExt->pingPongs[i]))
  725. status = HidpSubmitInterruptRead(fdoExt, &fdoExt->pingPongs[i], &irpSent);
  726. if (!NT_SUCCESS(status)){
  727. if (irpSent){
  728. DBGWARN(("Initial read failed with status %xh.", status))
  729. #if DBG
  730. if (dbgTrapOnHiccup){
  731. DBGERR(("Device 'hiccuped' ?? (fdoExt=%ph).", fdoExt))
  732. }
  733. #endif
  734. /*
  735. * We'll let the back-off logic in the completion
  736. * routine deal with this.
  737. */
  738. status = STATUS_SUCCESS;
  739. }
  740. else {
  741. DBGERR(("Initial read failed, irp not sent, status = %xh.", status))
  742. break;
  743. }
  744. }
  745. }
  746. }
  747. if (status == STATUS_PENDING){
  748. status = STATUS_SUCCESS;
  749. }
  750. DBGSUCCESS(status, TRUE)
  751. return status;
  752. }
  753. /*
  754. ********************************************************************************
  755. * CancelAllPingPongIrps
  756. ********************************************************************************
  757. *
  758. *
  759. */
  760. VOID CancelAllPingPongIrps(FDO_EXTENSION *fdoExt)
  761. {
  762. ULONG i;
  763. for (i = 0; i < fdoExt->numPingPongs; i++){
  764. HIDCLASS_PINGPONG *pingPong = &fdoExt->pingPongs[i];
  765. DBGVERBOSE(("Cancelling pingpong %x\n", pingPong))
  766. ASSERT(pingPong->sig == PINGPONG_SIG);
  767. ASSERT(!pingPong->weAreCancelling);
  768. //
  769. // The order of the following instructions is crucial. We must set
  770. // the weAreCancelling bit before waiting on the sentEvent, and the
  771. // last thing that we should wait on is the pumpDoneEvent, which
  772. // indicates that the read loop has finished all reads and will never
  773. // run again.
  774. //
  775. // Note that we don't need spinlocks to guard since we only have two
  776. // threads touching pingpong structures; the read pump thread and the
  777. // pnp thread. PNP irps are synchronous, so those are safe. Using the
  778. // weAreCancelling bit and the two events, sentEvent and pumpDoneEvent,
  779. // the pnp irps are synchronized with the pnp routines. This insures
  780. // that this cancel routine doesn't exit until the read pump has
  781. // signalled the pumpDoneEvent and exited, hence the pingpong
  782. // structures aren't ripped out from underneath it.
  783. //
  784. // If we have a backoff timer queued, it will eventually fire and
  785. // call the submitinterruptread routine to restart reads. This will
  786. // exit eventually, because we have set the weAreCancelling bit.
  787. //
  788. InterlockedIncrement(&pingPong->weAreCancelling);
  789. {
  790. /*
  791. * Synchronize with the irp's completion routine.
  792. */
  793. #if DBG
  794. UCHAR beforeIrql = KeGetCurrentIrql();
  795. UCHAR afterIrql;
  796. PVOID cancelRoutine = (PVOID)pingPong->irp->CancelRoutine;
  797. #endif
  798. KeWaitForSingleObject(&pingPong->sentEvent,
  799. Executive, // wait reason
  800. KernelMode,
  801. FALSE, // not alertable
  802. NULL ); // no timeout
  803. DBGVERBOSE(("Pingpong sent event set for pingpong %x\n", pingPong))
  804. IoCancelIrp(pingPong->irp);
  805. #if DBG
  806. afterIrql = KeGetCurrentIrql();
  807. if (afterIrql != beforeIrql){
  808. DBGERR(("CancelAllPingPongIrps: cancel routine at %ph changed irql from %d to %d.", cancelRoutine, beforeIrql, afterIrql))
  809. }
  810. #endif
  811. }
  812. /*
  813. * Cancelling the IRP causes a lower driver to
  814. * complete it (either in a cancel routine or when
  815. * the driver checks Irp->Cancel just before queueing it).
  816. * Wait for the IRP to actually get cancelled.
  817. */
  818. KeWaitForSingleObject( &pingPong->pumpDoneEvent,
  819. Executive, // wait reason
  820. KernelMode,
  821. FALSE, // not alertable
  822. NULL ); // no timeout
  823. DBGVERBOSE(("Pingpong pump done event set for %x\n", pingPong))
  824. }
  825. }
  826. /*
  827. ********************************************************************************
  828. * DestroyPingPongs
  829. ********************************************************************************
  830. *
  831. *
  832. */
  833. VOID DestroyPingPongs(FDO_EXTENSION *fdoExt)
  834. {
  835. if (ISPTR(fdoExt->pingPongs)){
  836. ULONG i;
  837. CancelAllPingPongIrps(fdoExt);
  838. for (i = 0; i < fdoExt->numPingPongs; i++){
  839. IoFreeIrp(fdoExt->pingPongs[i].irp);
  840. ExFreePool(fdoExt->pingPongs[i].reportBuffer);
  841. #if DBG
  842. fdoExt->pingPongs[i].sig = 0xDEADBEEF;
  843. #endif
  844. }
  845. ExFreePool(fdoExt->pingPongs);
  846. fdoExt->pingPongs = BAD_POINTER;
  847. }
  848. }
  849. /*
  850. ********************************************************************************
  851. * HidpPingpongBackoffTimerDpc
  852. ********************************************************************************
  853. *
  854. *
  855. *
  856. */
  857. VOID HidpPingpongBackoffTimerDpc(
  858. IN PKDPC Dpc,
  859. IN PVOID DeferredContext,
  860. IN PVOID SystemArgument1,
  861. IN PVOID SystemArgument2
  862. )
  863. {
  864. HIDCLASS_PINGPONG *pingPong = (HIDCLASS_PINGPONG *)DeferredContext;
  865. BOOLEAN irpSent;
  866. ASSERT(pingPong->sig == PINGPONG_SIG);
  867. /*
  868. * Increase the back-off time by 1 second, up to a max of 5 secs
  869. * (in negative 100-nanosecond units).
  870. */
  871. ASSERT((LONG)pingPong->backoffTimerPeriod.HighPart == -1);
  872. ASSERT((LONG)pingPong->backoffTimerPeriod.LowPart < 0);
  873. if ((LONG)pingPong->backoffTimerPeriod.LowPart > -50000000){
  874. (LONG)pingPong->backoffTimerPeriod.LowPart -= 10000000;
  875. }
  876. DBGVERBOSE(("Submitting Pingpong %x from backoff\n", pingPong))
  877. //
  878. // If we are being removed, or the CancelAllPingPongIrps has been called,
  879. // this call will take care of things.
  880. //
  881. HidpSubmitInterruptRead(pingPong->myFdoExt, pingPong, &irpSent);
  882. }