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.

530 lines
20 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. read.c
  5. Abstract
  6. Read handling routines
  7. Author:
  8. Ervin P.
  9. Environment:
  10. Kernel mode only
  11. Revision History:
  12. --*/
  13. #include "pch.h"
  14. /*
  15. ********************************************************************************
  16. * HidpCancelReadIrp
  17. ********************************************************************************
  18. *
  19. * If a queued read Irp gets cancelled by the user,
  20. * this function removes it from our pending-read list.
  21. *
  22. */
  23. VOID HidpCancelReadIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  24. {
  25. PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  26. FDO_EXTENSION *fdoExt;
  27. PHIDCLASS_COLLECTION collection;
  28. ULONG collectionIndex;
  29. KIRQL oldIrql;
  30. PIO_STACK_LOCATION irpSp;
  31. PHIDCLASS_FILE_EXTENSION fileExtension;
  32. ASSERT(hidDeviceExtension->Signature == HID_DEVICE_EXTENSION_SIG);
  33. ASSERT(hidDeviceExtension->isClientPdo);
  34. fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt;
  35. collectionIndex = hidDeviceExtension->pdoExt.collectionIndex;
  36. collection = &fdoExt->classCollectionArray[collectionIndex];
  37. irpSp = IoGetCurrentIrpStackLocation(Irp);
  38. ASSERT(irpSp->FileObject->Type == IO_TYPE_FILE);
  39. fileExtension = (PHIDCLASS_FILE_EXTENSION)irpSp->FileObject->FsContext;
  40. IoReleaseCancelSpinLock(Irp->CancelIrql);
  41. LockFileExtension(fileExtension, &oldIrql);
  42. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  43. DBG_RECORD_READ(Irp, 0, 0, TRUE);
  44. ASSERT(collection->numPendingReads > 0);
  45. collection->numPendingReads--;
  46. UnlockFileExtension(fileExtension, oldIrql);
  47. Irp->IoStatus.Status = STATUS_CANCELLED;
  48. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  49. }
  50. NTSTATUS EnqueueInterruptReadIrp( PHIDCLASS_COLLECTION collection,
  51. PHIDCLASS_FILE_EXTENSION fileExtension,
  52. PIRP Irp)
  53. {
  54. NTSTATUS status;
  55. PDRIVER_CANCEL oldCancelRoutine;
  56. RUNNING_DISPATCH();
  57. /*
  58. * Must set a cancel routine before
  59. * checking the Cancel flag.
  60. */
  61. oldCancelRoutine = IoSetCancelRoutine(Irp, HidpCancelReadIrp);
  62. ASSERT(!oldCancelRoutine);
  63. /*
  64. * Make sure this Irp wasn't just cancelled.
  65. * Note that there is NO RACE CONDITION here
  66. * because we are holding the fileExtension lock.
  67. */
  68. if (Irp->Cancel){
  69. /*
  70. * This IRP was cancelled.
  71. */
  72. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  73. if (oldCancelRoutine){
  74. /*
  75. * The cancel routine was NOT called.
  76. * Return error so that caller completes the IRP.
  77. */
  78. ASSERT(oldCancelRoutine == HidpCancelReadIrp);
  79. status = STATUS_CANCELLED;
  80. }
  81. else {
  82. /*
  83. * The cancel routine was called.
  84. * As soon as we drop the spinlock it will dequeue
  85. * and complete the IRP.
  86. * Initialize the IRP's listEntry so that the dequeue
  87. * doesn't cause corruption.
  88. * Then don't touch the irp.
  89. */
  90. InitializeListHead(&Irp->Tail.Overlay.ListEntry);
  91. collection->numPendingReads++; // because cancel routine will decrement
  92. IoMarkIrpPending(Irp);
  93. status = Irp->IoStatus.Status = STATUS_PENDING;
  94. }
  95. }
  96. else {
  97. DBG_RECORD_READ(Irp, IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length, 0, FALSE)
  98. /*
  99. * There are no reports waiting.
  100. * Queue this irp onto the file extension's list of pending irps.
  101. */
  102. InsertTailList(&fileExtension->PendingIrpList, &Irp->Tail.Overlay.ListEntry);
  103. collection->numPendingReads++;
  104. IoMarkIrpPending(Irp);
  105. status = Irp->IoStatus.Status = STATUS_PENDING;
  106. }
  107. return status;
  108. }
  109. PIRP DequeueInterruptReadIrp( PHIDCLASS_COLLECTION collection,
  110. PHIDCLASS_FILE_EXTENSION fileExtension)
  111. {
  112. PIRP irp = NULL;
  113. RUNNING_DISPATCH();
  114. while (!irp && !IsListEmpty(&fileExtension->PendingIrpList)){
  115. PDRIVER_CANCEL oldCancelRoutine;
  116. PLIST_ENTRY listEntry = RemoveHeadList(&fileExtension->PendingIrpList);
  117. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  118. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  119. if (oldCancelRoutine){
  120. ASSERT(oldCancelRoutine == HidpCancelReadIrp);
  121. ASSERT(collection->numPendingReads > 0);
  122. collection->numPendingReads--;
  123. }
  124. else {
  125. /*
  126. * IRP was cancelled and cancel routine was called.
  127. * As soon as we drop the spinlock,
  128. * the cancel routine will dequeue and complete this IRP.
  129. * Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
  130. * Then, don't touch the IRP.
  131. */
  132. ASSERT(irp->Cancel);
  133. InitializeListHead(&irp->Tail.Overlay.ListEntry);
  134. irp = NULL;
  135. }
  136. }
  137. return irp;
  138. }
  139. /*
  140. ********************************************************************************
  141. * HidpIrpMajorRead
  142. ********************************************************************************
  143. *
  144. * Note: this function should not be pageable because
  145. * reads can come in at dispatch level.
  146. *
  147. */
  148. NTSTATUS HidpIrpMajorRead(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp)
  149. {
  150. NTSTATUS status = STATUS_SUCCESS;
  151. FDO_EXTENSION *fdoExt;
  152. PDO_EXTENSION *pdoExt;
  153. PIO_STACK_LOCATION irpSp;
  154. PHIDCLASS_FILE_EXTENSION fileExtension;
  155. KIRQL oldIrql;
  156. ASSERT(HidDeviceExtension->isClientPdo);
  157. pdoExt = &HidDeviceExtension->pdoExt;
  158. fdoExt = &pdoExt->deviceFdoExt->fdoExt;
  159. irpSp = IoGetCurrentIrpStackLocation(Irp);
  160. /*
  161. * Get our file extension.
  162. */
  163. if (!irpSp->FileObject ||
  164. (irpSp->FileObject &&
  165. !irpSp->FileObject->FsContext)) {
  166. DBGWARN(("Attempted read with no file extension"))
  167. Irp->IoStatus.Status = status = STATUS_PRIVILEGE_NOT_HELD;
  168. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  169. return status;
  170. }
  171. ASSERT(irpSp->FileObject->Type == IO_TYPE_FILE);
  172. fileExtension = (PHIDCLASS_FILE_EXTENSION)irpSp->FileObject->FsContext;
  173. ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
  174. /*
  175. * Check security.
  176. * The open must have been succeeded BY THIS DRIVER and
  177. * (if this is a read on a keyboard or mouse)
  178. * the client must be a kernel driver.
  179. */
  180. if (fileExtension->SecurityCheck && fileExtension->haveReadPrivilege){
  181. if (((fdoExt->state == DEVICE_STATE_START_SUCCESS) ||
  182. (fdoExt->state == DEVICE_STATE_STOPPING) ||
  183. (fdoExt->state == DEVICE_STATE_STOPPED)) &&
  184. ((pdoExt->state == COLLECTION_STATE_RUNNING) ||
  185. (pdoExt->state == COLLECTION_STATE_STOPPING) ||
  186. (pdoExt->state == COLLECTION_STATE_STOPPED))){
  187. ULONG collectionNum;
  188. PHIDCLASS_COLLECTION classCollection;
  189. PHIDP_COLLECTION_DESC collectionDesc;
  190. //
  191. // ISSUE: Is this safe to stop a polled collection like this?
  192. // interrupt driver collections have a restore read pump at power up
  193. // to D0, but I don't see any for polled collections...?
  194. //
  195. BOOLEAN isStopped = ((fdoExt->state == DEVICE_STATE_STOPPED) ||
  196. (fdoExt->state == DEVICE_STATE_STOPPING) ||
  197. (pdoExt->state == COLLECTION_STATE_STOPPING) ||
  198. (pdoExt->state == COLLECTION_STATE_STOPPED));
  199. Irp->IoStatus.Information = 0;
  200. /*
  201. * Get our collection and collection description.
  202. */
  203. collectionNum = HidDeviceExtension->pdoExt.collectionNum;
  204. classCollection = GetHidclassCollection(fdoExt, collectionNum);
  205. collectionDesc = GetCollectionDesc(fdoExt, collectionNum);
  206. if (classCollection && collectionDesc){
  207. /*
  208. * Make sure the caller's read buffer is large enough to read at least one report.
  209. */
  210. if (irpSp->Parameters.Read.Length >= collectionDesc->InputLength){
  211. /*
  212. * We know we're going to try to transfer something into the caller's
  213. * buffer, so get the global address. This will also serve to create
  214. * a mapped system address in the MDL if necessary.
  215. */
  216. if (classCollection->hidCollectionInfo.Polled){
  217. /*
  218. * This is a POLLED collection.
  219. */
  220. #if DBG
  221. if (fileExtension->isOpportunisticPolledDeviceReader &&
  222. fileExtension->nowCompletingIrpForOpportunisticReader){
  223. DBGWARN(("'Opportunistic' reader issuing read in completion routine"))
  224. }
  225. #endif
  226. if (isStopped){
  227. status = EnqueuePolledReadIrp(classCollection, Irp);
  228. }
  229. else if (fileExtension->isOpportunisticPolledDeviceReader &&
  230. !classCollection->polledDataIsStale &&
  231. !fileExtension->nowCompletingIrpForOpportunisticReader &&
  232. (irpSp->Parameters.Read.Length >= classCollection->savedPolledReportLen)){
  233. PUCHAR callersBuffer;
  234. callersBuffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
  235. if (callersBuffer) {
  236. ULONG userReportLength;
  237. /*
  238. * Use the polledDeviceReadQueueSpinLock to protect
  239. * the savedPolledReportBuf.
  240. */
  241. KeAcquireSpinLock(&classCollection->polledDeviceReadQueueSpinLock, &oldIrql);
  242. /*
  243. * This is an "opportunistic" reader who
  244. * wants a result right away.
  245. * We have a recent report,
  246. * so just copy the last saved report.
  247. */
  248. RtlCopyMemory( callersBuffer,
  249. classCollection->savedPolledReportBuf,
  250. classCollection->savedPolledReportLen);
  251. Irp->IoStatus.Information = userReportLength = classCollection->savedPolledReportLen;
  252. KeReleaseSpinLock(&classCollection->polledDeviceReadQueueSpinLock, oldIrql);
  253. DBG_RECORD_READ(Irp, userReportLength, (ULONG)callersBuffer[0], TRUE)
  254. status = STATUS_SUCCESS;
  255. }
  256. else {
  257. status = STATUS_INVALID_USER_BUFFER;
  258. }
  259. }
  260. else {
  261. status = EnqueuePolledReadIrp(classCollection, Irp);
  262. /*
  263. * If this is an "opportunistic" polled
  264. * device reader, and we queued the irp,
  265. * make the read happen right away.
  266. * Make sure ALL SPINLOCKS ARE RELEASED
  267. * before we call out of the driver.
  268. */
  269. if (NT_SUCCESS(status) && fileExtension->isOpportunisticPolledDeviceReader){
  270. ReadPolledDevice(pdoExt, FALSE);
  271. }
  272. }
  273. }
  274. else {
  275. /*
  276. * This is an ordinary NON-POLLED collection.
  277. * We either:
  278. * 1. Satisfy this read with a queued report
  279. * or
  280. * 2. Queue this read IRP and satisfy it in the future
  281. * when a report comes in (on one of the ping-pong IRPs).
  282. */
  283. //
  284. // We only stop interrupt devices when we power down.
  285. //
  286. if (fdoExt->devicePowerState != PowerDeviceD0) {
  287. DBGINFO(("read report received in low power"));
  288. }
  289. isStopped |= (fdoExt->devicePowerState != PowerDeviceD0);
  290. LockFileExtension(fileExtension, &oldIrql);
  291. if (isStopped){
  292. status = EnqueueInterruptReadIrp(classCollection, fileExtension, Irp);
  293. } else {
  294. ULONG userBufferRemaining = irpSp->Parameters.Read.Length;
  295. PUCHAR callersBuffer;
  296. callersBuffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
  297. if (callersBuffer) {
  298. PUCHAR nextReportBuffer = callersBuffer;
  299. /*
  300. * There are some reports waiting.
  301. *
  302. * Spin in this loop, filling up the caller's buffer with reports,
  303. * until either the buffer fills up or we run out of reports.
  304. */
  305. ULONG reportsReturned = 0;
  306. status = STATUS_SUCCESS;
  307. while (userBufferRemaining > 0){
  308. PHIDCLASS_REPORT reportExtension;
  309. ULONG reportSize = userBufferRemaining;
  310. reportExtension = DequeueInterruptReport(fileExtension, userBufferRemaining);
  311. if (reportExtension){
  312. status = HidpCopyInputReportToUser( fileExtension,
  313. reportExtension->UnparsedReport,
  314. &reportSize,
  315. nextReportBuffer);
  316. /*
  317. * Whether we succeeded or failed, free this report.
  318. * (If we failed, there may be something wrong with
  319. * the report, so we'll just throw it away).
  320. */
  321. ExFreePool(reportExtension);
  322. if (NT_SUCCESS(status)){
  323. reportsReturned++;
  324. nextReportBuffer += reportSize;
  325. ASSERT(reportSize <= userBufferRemaining);
  326. userBufferRemaining -= reportSize;
  327. } else {
  328. DBGSUCCESS(status, TRUE)
  329. break;
  330. }
  331. } else {
  332. break;
  333. }
  334. }
  335. if (NT_SUCCESS(status)){
  336. if (!reportsReturned) {
  337. /*
  338. * No reports are ready. So queue the read IRP.
  339. */
  340. status = EnqueueInterruptReadIrp(classCollection, fileExtension, Irp);
  341. } else {
  342. /*
  343. * We've succesfully copied something into the user's buffer,
  344. * calculate how much we've copied and return in the irp.
  345. */
  346. Irp->IoStatus.Information = (ULONG)(nextReportBuffer - callersBuffer);
  347. DBG_RECORD_READ(Irp, (ULONG)Irp->IoStatus.Information, (ULONG)callersBuffer[0], TRUE)
  348. }
  349. }
  350. }
  351. else {
  352. status = STATUS_INVALID_USER_BUFFER;
  353. }
  354. }
  355. UnlockFileExtension(fileExtension, oldIrql);
  356. }
  357. } else {
  358. status = STATUS_INVALID_BUFFER_SIZE;
  359. }
  360. }
  361. else {
  362. status = STATUS_DEVICE_NOT_CONNECTED;
  363. }
  364. DBGSUCCESS(status, TRUE)
  365. }
  366. else {
  367. /*
  368. * This can legitimately happen.
  369. * The device was disconnected between the client's open and read;
  370. * or between a read-complete and the next read.
  371. */
  372. status = STATUS_DEVICE_NOT_CONNECTED;
  373. }
  374. }
  375. else {
  376. DBGWARN(("HidpIrpMajorRead: user-mode client does not have read privilege"))
  377. status = STATUS_PRIVILEGE_NOT_HELD;
  378. }
  379. /*
  380. * If we satisfied the read Irp (did not queue it),
  381. * then complete it here.
  382. */
  383. if (status != STATUS_PENDING){
  384. ULONG insideReadCompleteCount;
  385. Irp->IoStatus.Status = status;
  386. insideReadCompleteCount = InterlockedIncrement(&fileExtension->insideReadCompleteCount);
  387. if (insideReadCompleteCount <= INSIDE_READCOMPLETE_MAX){
  388. IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
  389. }
  390. else {
  391. /*
  392. * All these nested reads are _probably_ occuring on the same thread,
  393. * and we are going to run out of stack and crash if we keep completing
  394. * synchronously. So return pending for this IRP and schedule a workItem
  395. * to complete it asynchronously, just to give the stack a chance to unwind.
  396. */
  397. ASYNC_COMPLETE_CONTEXT *asyncCompleteContext = ALLOCATEPOOL(NonPagedPool, sizeof(ASYNC_COMPLETE_CONTEXT));
  398. if (asyncCompleteContext){
  399. ASSERT(!Irp->CancelRoutine);
  400. DBGWARN(("HidpIrpMajorRead: CLIENT IS LOOPING ON READ COMPLETION -- scheduling workItem to complete IRP %ph (status=%xh) asynchronously", Irp, status))
  401. ExInitializeWorkItem( &asyncCompleteContext->workItem,
  402. WorkItemCallback_CompleteIrpAsynchronously,
  403. asyncCompleteContext);
  404. asyncCompleteContext->sig = ASYNC_COMPLETE_CONTEXT_SIG;
  405. asyncCompleteContext->irp = Irp;
  406. asyncCompleteContext->devObj = pdoExt->pdo;
  407. ObReferenceObject(pdoExt->pdo);
  408. ExQueueWorkItem(&asyncCompleteContext->workItem, DelayedWorkQueue);
  409. status = STATUS_PENDING;
  410. }
  411. else {
  412. DBGERR(("HidpIrpMajorRead: completeIrpWorkItem alloc failed"))
  413. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  414. }
  415. }
  416. InterlockedDecrement(&fileExtension->insideReadCompleteCount);
  417. }
  418. DBGSUCCESS(status, FALSE)
  419. return status;
  420. }
  421. VOID WorkItemCallback_CompleteIrpAsynchronously(PVOID context)
  422. {
  423. ASYNC_COMPLETE_CONTEXT *asyncCompleteContext = context;
  424. ASSERT(asyncCompleteContext->sig == ASYNC_COMPLETE_CONTEXT_SIG);
  425. DBGVERBOSE(("WorkItemCallback_CompleteIrpAsynchronously: completing irp %ph with status %xh.", asyncCompleteContext->irp, asyncCompleteContext->irp->IoStatus.Status))
  426. /*
  427. * Indicate that the irp may be completing
  428. */
  429. IoMarkIrpPending(asyncCompleteContext->irp);
  430. IoCompleteRequest(asyncCompleteContext->irp, IO_NO_INCREMENT);
  431. ObDereferenceObject(asyncCompleteContext->devObj);
  432. ExFreePool(asyncCompleteContext);
  433. }