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.

502 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. } else {
  81. /*
  82. * The cancel routine was called.
  83. * As soon as we drop the spinlock it will dequeue
  84. * and complete the IRP.
  85. * Initialize the IRP's listEntry so that the dequeue
  86. * doesn't cause corruption.
  87. * Then don't touch the irp.
  88. */
  89. InitializeListHead(&Irp->Tail.Overlay.ListEntry);
  90. collection->numPendingReads++; // because cancel routine will decrement
  91. IoMarkIrpPending(Irp);
  92. status = STATUS_PENDING;
  93. }
  94. } else {
  95. DBG_RECORD_READ(Irp, IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length, 0, FALSE)
  96. /*
  97. * There are no reports waiting.
  98. * Queue this irp onto the file extension's list of pending irps.
  99. */
  100. InsertTailList(&fileExtension->PendingIrpList, &Irp->Tail.Overlay.ListEntry);
  101. collection->numPendingReads++;
  102. IoMarkIrpPending(Irp);
  103. status = STATUS_PENDING;
  104. }
  105. return status;
  106. }
  107. PIRP DequeueInterruptReadIrp( PHIDCLASS_COLLECTION collection,
  108. PHIDCLASS_FILE_EXTENSION fileExtension)
  109. {
  110. PIRP irp = NULL;
  111. RUNNING_DISPATCH();
  112. while (!irp && !IsListEmpty(&fileExtension->PendingIrpList)) {
  113. PDRIVER_CANCEL oldCancelRoutine;
  114. PLIST_ENTRY listEntry = RemoveHeadList(&fileExtension->PendingIrpList);
  115. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  116. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  117. if (oldCancelRoutine) {
  118. ASSERT(oldCancelRoutine == HidpCancelReadIrp);
  119. ASSERT(collection->numPendingReads > 0);
  120. collection->numPendingReads--;
  121. } else {
  122. /*
  123. * IRP was cancelled and cancel routine was called.
  124. * As soon as we drop the spinlock,
  125. * the cancel routine will dequeue and complete this IRP.
  126. * Initialize the IRP's listEntry so that the dequeue doesn't cause corruption.
  127. * Then, don't touch the IRP.
  128. */
  129. ASSERT(irp->Cancel);
  130. InitializeListHead(&irp->Tail.Overlay.ListEntry);
  131. irp = NULL;
  132. }
  133. }
  134. return irp;
  135. }
  136. /*
  137. ********************************************************************************
  138. * HidpIrpMajorRead
  139. ********************************************************************************
  140. *
  141. * Note: this function should not be pageable because
  142. * reads can come in at dispatch level.
  143. *
  144. */
  145. NTSTATUS HidpIrpMajorRead(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp)
  146. {
  147. NTSTATUS status = STATUS_SUCCESS;
  148. FDO_EXTENSION *fdoExt;
  149. PDO_EXTENSION *pdoExt;
  150. PIO_STACK_LOCATION irpSp;
  151. PHIDCLASS_FILE_EXTENSION fileExtension;
  152. KIRQL oldIrql;
  153. ASSERT(HidDeviceExtension->isClientPdo);
  154. pdoExt = &HidDeviceExtension->pdoExt;
  155. fdoExt = &pdoExt->deviceFdoExt->fdoExt;
  156. irpSp = IoGetCurrentIrpStackLocation(Irp);
  157. /*
  158. * Get our file extension.
  159. */
  160. if (!irpSp->FileObject ||
  161. (irpSp->FileObject &&
  162. !irpSp->FileObject->FsContext)) {
  163. DBGWARN(("Attempted read with no file extension"))
  164. Irp->IoStatus.Status = status = STATUS_PRIVILEGE_NOT_HELD;
  165. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  166. return status;
  167. }
  168. ASSERT(irpSp->FileObject->Type == IO_TYPE_FILE);
  169. fileExtension = (PHIDCLASS_FILE_EXTENSION)irpSp->FileObject->FsContext;
  170. ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG);
  171. if (((fdoExt->state == DEVICE_STATE_START_SUCCESS) ||
  172. (fdoExt->state == DEVICE_STATE_STOPPING) ||
  173. (fdoExt->state == DEVICE_STATE_STOPPED)) &&
  174. ((pdoExt->state == COLLECTION_STATE_RUNNING) ||
  175. (pdoExt->state == COLLECTION_STATE_STOPPING) ||
  176. (pdoExt->state == COLLECTION_STATE_STOPPED))) {
  177. ULONG collectionNum;
  178. PHIDCLASS_COLLECTION classCollection;
  179. PHIDP_COLLECTION_DESC collectionDesc;
  180. //
  181. // ISSUE: Is this safe to stop a polled collection like this?
  182. // interrupt driver collections have a restore read pump at power up
  183. // to D0, but I don't see any for polled collections...?
  184. //
  185. BOOLEAN isStopped = ((fdoExt->state == DEVICE_STATE_STOPPED) ||
  186. (fdoExt->state == DEVICE_STATE_STOPPING) ||
  187. (pdoExt->state == COLLECTION_STATE_STOPPING) ||
  188. (pdoExt->state == COLLECTION_STATE_STOPPED));
  189. Irp->IoStatus.Information = 0;
  190. /*
  191. * Get our collection and collection description.
  192. */
  193. collectionNum = HidDeviceExtension->pdoExt.collectionNum;
  194. classCollection = GetHidclassCollection(fdoExt, collectionNum);
  195. collectionDesc = GetCollectionDesc(fdoExt, collectionNum);
  196. if (classCollection && collectionDesc) {
  197. /*
  198. * Make sure the caller's read buffer is large enough to read at least one report.
  199. */
  200. if (irpSp->Parameters.Read.Length >= collectionDesc->InputLength) {
  201. /*
  202. * We know we're going to try to transfer something into the caller's
  203. * buffer, so get the global address. This will also serve to create
  204. * a mapped system address in the MDL if necessary.
  205. */
  206. if (classCollection->hidCollectionInfo.Polled) {
  207. /*
  208. * This is a POLLED collection.
  209. */
  210. if (isStopped) {
  211. status = EnqueuePolledReadIrp(classCollection, Irp);
  212. } else if (fileExtension->isOpportunisticPolledDeviceReader &&
  213. !classCollection->polledDataIsStale &&
  214. (irpSp->Parameters.Read.Length >= classCollection->savedPolledReportLen)) {
  215. PUCHAR callersBuffer;
  216. callersBuffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
  217. if (callersBuffer) {
  218. ULONG userReportLength;
  219. /*
  220. * Use the polledDeviceReadQueueSpinLock to protect
  221. * the savedPolledReportBuf.
  222. */
  223. KeAcquireSpinLock(&classCollection->polledDeviceReadQueueSpinLock, &oldIrql);
  224. /*
  225. * This is an "opportunistic" reader who
  226. * wants a result right away.
  227. * We have a recent report,
  228. * so just copy the last saved report.
  229. */
  230. RtlCopyMemory( callersBuffer,
  231. classCollection->savedPolledReportBuf,
  232. classCollection->savedPolledReportLen);
  233. Irp->IoStatus.Information = userReportLength = classCollection->savedPolledReportLen;
  234. KeReleaseSpinLock(&classCollection->polledDeviceReadQueueSpinLock, oldIrql);
  235. DBG_RECORD_READ(Irp, userReportLength, (ULONG)callersBuffer[0], TRUE)
  236. status = STATUS_SUCCESS;
  237. } else {
  238. status = STATUS_INVALID_USER_BUFFER;
  239. }
  240. } else {
  241. status = EnqueuePolledReadIrp(classCollection, Irp);
  242. /*
  243. * If this is an "opportunistic" polled
  244. * device reader, and we queued the irp,
  245. * make the read happen right away.
  246. * Make sure ALL SPINLOCKS ARE RELEASED
  247. * before we call out of the driver.
  248. */
  249. if (NT_SUCCESS(status) && fileExtension->isOpportunisticPolledDeviceReader) {
  250. ReadPolledDevice(pdoExt, FALSE);
  251. }
  252. }
  253. } else {
  254. /*
  255. * This is an ordinary NON-POLLED collection.
  256. * We either:
  257. * 1. Satisfy this read with a queued report
  258. * or
  259. * 2. Queue this read IRP and satisfy it in the future
  260. * when a report comes in (on one of the ping-pong IRPs).
  261. */
  262. //
  263. // We only stop interrupt devices when we power down.
  264. //
  265. if (fdoExt->devicePowerState != PowerDeviceD0) {
  266. DBGINFO(("read report received in low power"));
  267. }
  268. isStopped |= (fdoExt->devicePowerState != PowerDeviceD0);
  269. LockFileExtension(fileExtension, &oldIrql);
  270. if (isStopped) {
  271. status = EnqueueInterruptReadIrp(classCollection, fileExtension, Irp);
  272. } else {
  273. ULONG userBufferRemaining = irpSp->Parameters.Read.Length;
  274. PUCHAR callersBuffer;
  275. callersBuffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress);
  276. if (callersBuffer) {
  277. PUCHAR nextReportBuffer = callersBuffer;
  278. /*
  279. * There are some reports waiting.
  280. *
  281. * Spin in this loop, filling up the caller's buffer with reports,
  282. * until either the buffer fills up or we run out of reports.
  283. */
  284. ULONG reportsReturned = 0;
  285. status = STATUS_SUCCESS;
  286. while (userBufferRemaining > 0) {
  287. PHIDCLASS_REPORT reportExtension;
  288. ULONG reportSize = userBufferRemaining;
  289. reportExtension = DequeueInterruptReport(fileExtension, userBufferRemaining);
  290. if (reportExtension) {
  291. status = HidpCopyInputReportToUser( fileExtension,
  292. reportExtension->UnparsedReport,
  293. &reportSize,
  294. nextReportBuffer);
  295. /*
  296. * Whether we succeeded or failed, free this report.
  297. * (If we failed, there may be something wrong with
  298. * the report, so we'll just throw it away).
  299. */
  300. ExFreePool(reportExtension);
  301. if (NT_SUCCESS(status)) {
  302. reportsReturned++;
  303. nextReportBuffer += reportSize;
  304. ASSERT(reportSize <= userBufferRemaining);
  305. userBufferRemaining -= reportSize;
  306. } else {
  307. DBGSUCCESS(status, TRUE)
  308. break;
  309. }
  310. } else {
  311. break;
  312. }
  313. }
  314. if (NT_SUCCESS(status)) {
  315. if (!reportsReturned) {
  316. /*
  317. * No reports are ready. So queue the read IRP.
  318. */
  319. status = EnqueueInterruptReadIrp(classCollection, fileExtension, Irp);
  320. } else {
  321. /*
  322. * We've succesfully copied something into the user's buffer,
  323. * calculate how much we've copied and return in the irp.
  324. */
  325. Irp->IoStatus.Information = (ULONG)(nextReportBuffer - callersBuffer);
  326. DBG_RECORD_READ(Irp, (ULONG)Irp->IoStatus.Information, (ULONG)callersBuffer[0], TRUE)
  327. }
  328. }
  329. } else {
  330. status = STATUS_INVALID_USER_BUFFER;
  331. }
  332. }
  333. UnlockFileExtension(fileExtension, oldIrql);
  334. }
  335. } else {
  336. status = STATUS_INVALID_BUFFER_SIZE;
  337. }
  338. } else {
  339. status = STATUS_DEVICE_NOT_CONNECTED;
  340. }
  341. DBGSUCCESS(status, FALSE)
  342. } else {
  343. /*
  344. * This can legitimately happen.
  345. * The device was disconnected between the client's open and read;
  346. * or between a read-complete and the next read.
  347. */
  348. status = STATUS_DEVICE_NOT_CONNECTED;
  349. }
  350. /*
  351. * If we satisfied the read Irp (did not queue it),
  352. * then complete it here.
  353. */
  354. if (status != STATUS_PENDING) {
  355. ULONG insideReadCompleteCount;
  356. Irp->IoStatus.Status = status;
  357. insideReadCompleteCount = InterlockedIncrement(&fileExtension->insideReadCompleteCount);
  358. if (insideReadCompleteCount <= INSIDE_READCOMPLETE_MAX) {
  359. IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
  360. } else {
  361. /*
  362. * All these nested reads are _probably_ occuring on the same thread,
  363. * and we are going to run out of stack and crash if we keep completing
  364. * synchronously. So return pending for this IRP and schedule a workItem
  365. * to complete it asynchronously, just to give the stack a chance to unwind.
  366. */
  367. ASYNC_COMPLETE_CONTEXT *asyncCompleteContext = ALLOCATEPOOL(NonPagedPool, sizeof(ASYNC_COMPLETE_CONTEXT));
  368. if (asyncCompleteContext) {
  369. ASSERT(!Irp->CancelRoutine);
  370. DBGWARN(("HidpIrpMajorRead: CLIENT IS LOOPING ON READ COMPLETION -- scheduling workItem to complete IRP %ph (status=%xh) asynchronously", Irp, status))
  371. asyncCompleteContext->workItem = IoAllocateWorkItem(pdoExt->pdo);
  372. asyncCompleteContext->sig = ASYNC_COMPLETE_CONTEXT_SIG;
  373. asyncCompleteContext->irp = Irp;
  374. /*
  375. * Indicate that the irp has been queued
  376. */
  377. IoMarkIrpPending(asyncCompleteContext->irp);
  378. IoQueueWorkItem(asyncCompleteContext->workItem,
  379. WorkItemCallback_CompleteIrpAsynchronously,
  380. DelayedWorkQueue,
  381. asyncCompleteContext);
  382. status = STATUS_PENDING;
  383. } else {
  384. DBGERR(("HidpIrpMajorRead: completeIrpWorkItem alloc failed"))
  385. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  386. }
  387. }
  388. InterlockedDecrement(&fileExtension->insideReadCompleteCount);
  389. }
  390. DBGSUCCESS(status, FALSE)
  391. return status;
  392. }
  393. VOID
  394. WorkItemCallback_CompleteIrpAsynchronously(PDEVICE_OBJECT DevObj,
  395. PVOID context)
  396. {
  397. ASYNC_COMPLETE_CONTEXT *asyncCompleteContext = context;
  398. ASSERT(asyncCompleteContext->sig == ASYNC_COMPLETE_CONTEXT_SIG);
  399. DBGVERBOSE(("WorkItemCallback_CompleteIrpAsynchronously: completing irp %ph with status %xh.", asyncCompleteContext->irp, asyncCompleteContext->irp->IoStatus.Status))
  400. IoCompleteRequest(asyncCompleteContext->irp, IO_NO_INCREMENT);
  401. IoFreeWorkItem(asyncCompleteContext->workItem);
  402. ExFreePool(asyncCompleteContext);
  403. }