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.

567 lines
15 KiB

  1. /* ++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. UTILS.C
  5. Abstract:
  6. Utility functions
  7. Environment:
  8. kernel mode only
  9. Revision History:
  10. 07-15-99 : created
  11. Author:
  12. Jeff Midkiff (jeffmi)
  13. Notes:
  14. -- */
  15. #include <wdm.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <usbdi.h>
  19. #include <usbdlib.h>
  20. #include <ntddser.h>
  21. #include "wceusbsh.h"
  22. __inline
  23. VOID
  24. ReuseIrp (
  25. PIRP Irp,
  26. NTSTATUS Status
  27. );
  28. __inline
  29. VOID
  30. RundownIrpRefs(
  31. IN PIRP *PpCurrentOpIrp,
  32. IN PKTIMER IntervalTimer OPTIONAL,
  33. IN PKTIMER TotalTimer OPTIONAL,
  34. IN PDEVICE_EXTENSION PDevExt
  35. );
  36. VOID
  37. TryToCompleteCurrentIrp(
  38. IN PDEVICE_EXTENSION PDevExt,
  39. IN NTSTATUS ReturnStatus,
  40. IN PIRP *PpCurrentIrp,
  41. IN PLIST_ENTRY PIrpQueue OPTIONAL,
  42. IN PKTIMER PIntervalTimer OPTIONAL,
  43. IN PKTIMER PTotalTimer OPTIONAL,
  44. IN PSTART_ROUTINE PStartNextIrpRoutine OPTIONAL,
  45. IN PGET_NEXT_ROUTINE PGetNextIrpRoutine OPTIONAL,
  46. IN LONG ReferenceType,
  47. IN BOOLEAN CompleteRequest,
  48. IN KIRQL IrqlForRelease
  49. )
  50. /*++
  51. Routine Description:
  52. This routine attempts to rundown all of the reasons there are
  53. references on the current Irp. If everything can be killed
  54. then it will complete this Irp, and then try to start another.
  55. Similiar to StartIo.
  56. NOTE: This routine assumes that it is called with the control lock held.
  57. Arguments:
  58. Extension - Simply a pointer to the device extension.
  59. SynchRoutine - A routine that will synchronize with the isr
  60. and attempt to remove the knowledge of the
  61. current irp from the isr. NOTE: This pointer
  62. can be null.
  63. IrqlForRelease - This routine is called with the control lock held.
  64. This is the irql that was current when it was acquired.
  65. ReturnStatus - The irp's status field will be set to this value, if
  66. this routine can complete the irp.
  67. Return Value:
  68. None.
  69. --*/
  70. {
  71. PERF_ENTRY( PERF_TryToCompleteCurrentIrp );
  72. if ( !PDevExt || !PpCurrentIrp || !(*PpCurrentIrp) ) {
  73. DbgDump(DBG_ERR, ("TryToCompleteCurrentIrp: INVALID PARAMETER\n"));
  74. KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
  75. PERF_EXIT( PERF_TryToCompleteCurrentIrp );
  76. TEST_TRAP();
  77. return;
  78. }
  79. DbgDump(DBG_IRP|DBG_TRACE, (">TryToCompleteCurrentIrp(%p, 0x%x)\n", *PpCurrentIrp, ReturnStatus));
  80. //
  81. // We can decrement the reference to "remove" the fact
  82. // that the caller no longer will be accessing this irp.
  83. //
  84. IRP_CLEAR_REFERENCE(*PpCurrentIrp, ReferenceType);
  85. //
  86. // Try to run down all other references (i.e., Timers) to this irp.
  87. //
  88. RundownIrpRefs(PpCurrentIrp, PIntervalTimer, PTotalTimer, PDevExt);
  89. //
  90. // See if the ref count is zero after trying to kill everybody else.
  91. //
  92. if (!IRP_REFERENCE_COUNT(*PpCurrentIrp)) {
  93. //
  94. // The ref count was zero so we should complete this request.
  95. //
  96. PIRP pNewIrp;
  97. DbgDump( DBG_IRP, ("!IRP_REFERENCE_COUNT\n"));
  98. // set Irp's return status
  99. (*PpCurrentIrp)->IoStatus.Status = ReturnStatus;
  100. if (ReturnStatus == STATUS_CANCELLED) {
  101. (*PpCurrentIrp)->IoStatus.Information = 0;
  102. }
  103. if (PGetNextIrpRoutine) {
  104. //
  105. // Get the next Irp off the specified Irp queue
  106. //
  107. KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
  108. DbgDump( DBG_IRP, ("<< Current IRQL(1)\n"));
  109. DbgDump( DBG_IRP, ("Calling GetNextUserIrp\n"));
  110. (*PGetNextIrpRoutine)(PpCurrentIrp, PIrpQueue, &pNewIrp, CompleteRequest, PDevExt);
  111. if (pNewIrp) {
  112. //
  113. // There was an Irp in the queue
  114. //
  115. DbgDump( DBG_IRP, ("Calling StartNextIrpRoutine\n"));
  116. //
  117. // kick-start the next Irp
  118. //
  119. PStartNextIrpRoutine(PDevExt);
  120. }
  121. } else {
  122. PIRP pOldIrp = *PpCurrentIrp;
  123. //
  124. // There was no GetNextIrpRoutine.
  125. // We will simply complete the Irp.
  126. //
  127. DbgDump( DBG_IRP, ("No GetNextIrpRoutine\n"));
  128. *PpCurrentIrp = NULL;
  129. KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
  130. DbgDump( DBG_IRP, ("<< Current IRQL(2)\n"));
  131. if (CompleteRequest) {
  132. //
  133. // complete the Irp
  134. //
  135. DbgDump(DBG_IRP|DBG_READ|DBG_READ_LENGTH|DBG_TRACE, ("IoCompleteRequest(2, %p) Status: 0x%x Btyes: %d\n", pOldIrp, pOldIrp->IoStatus.Status, pOldIrp->IoStatus.Information ));
  136. ReleaseRemoveLock(&PDevExt->RemoveLock, pOldIrp);
  137. IoCompleteRequest( pOldIrp, IO_NO_INCREMENT );
  138. }
  139. }
  140. } else {
  141. //
  142. // Irp still has outstanding references
  143. //
  144. DbgDump(DBG_WRN|DBG_IRP|DBG_TRACE, ("Current IRP %p still has reference of %x\n", *PpCurrentIrp,
  145. ((UINT_PTR)((IoGetCurrentIrpStackLocation((*PpCurrentIrp))->
  146. Parameters.Others.Argument4)))));
  147. KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
  148. DbgDump( DBG_IRP, ("<< Current IRQL(3)\n"));
  149. }
  150. DbgDump( DBG_IRP|DBG_TRACE, ("<TryToCompleteCurrentIrp\n"));
  151. PERF_EXIT( PERF_TryToCompleteCurrentIrp );
  152. return;
  153. }
  154. VOID
  155. RundownIrpRefs(
  156. IN PIRP *PpCurrentIrp,
  157. IN PKTIMER IntervalTimer OPTIONAL,
  158. IN PKTIMER TotalTimer OPTIONAL,
  159. IN PDEVICE_EXTENSION PDevExt
  160. )
  161. /*++
  162. Routine Description:
  163. This routine runs through the various items that *could*
  164. have a reference to the current read/write Irp. It try's to kill
  165. the reason. If it does succeed in killing the reason it
  166. will decrement the reference count on the irp.
  167. NOTE: This routine assumes that it is called with the control lock held.
  168. Arguments:
  169. PpCurrentIrp - Pointer to a pointer to current irp for the
  170. particular operation.
  171. IntervalTimer - Pointer to the interval timer for the operation.
  172. NOTE: This could be null.
  173. TotalTimer - Pointer to the total timer for the operation.
  174. NOTE: This could be null.
  175. PDevExt - Pointer to device extension
  176. Return Value:
  177. None.
  178. --*/
  179. {
  180. PERF_ENTRY( PERF_RundownIrpRefs );
  181. if ( !PDevExt || !PpCurrentIrp || !(*PpCurrentIrp) ) {
  182. DbgDump(DBG_ERR, ("RundownIrpRefs: INVALID PARAMETER\n"));
  183. PERF_EXIT( PERF_RundownIrpRefs );
  184. TEST_TRAP();
  185. return;
  186. }
  187. DbgDump(DBG_IRP, (">RundownIrpRefs(%p)\n", *PpCurrentIrp));
  188. //
  189. // This routine is called with the cancel spin lock held
  190. // so we know only one thread of execution can be in here
  191. // at one time.
  192. //
  193. //
  194. // First we see if there is still a cancel routine. If
  195. // so then we can decrement the count by one.
  196. //
  197. if ((*PpCurrentIrp)->CancelRoutine) {
  198. IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_CANCEL);
  199. IoSetCancelRoutine(*PpCurrentIrp, NULL);
  200. }
  201. if (IntervalTimer) {
  202. //
  203. // Try to cancel the operation's interval timer. If the operation
  204. // returns true then the timer did have a reference to the
  205. // irp. Since we've canceled this timer that reference is
  206. // no longer valid and we can decrement the reference count.
  207. //
  208. // If the cancel returns false then this means either of two things:
  209. //
  210. // a) The timer has already fired.
  211. //
  212. // b) There never was an interval timer.
  213. //
  214. // In the case of "b" there is no need to decrement the reference
  215. // count since the "timer" never had a reference to it.
  216. //
  217. // In the case of "a", then the timer itself will be coming
  218. // along and decrement it's reference. Note that the caller
  219. // of this routine might actually be the this timer, but it
  220. // has already decremented the reference.
  221. //
  222. if (KeCancelTimer(IntervalTimer)) {
  223. IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_INTERVAL_TIMER);
  224. } else {
  225. // short circuit the read irp from the interval timer
  226. DbgDump(DBG_IRP|DBG_TIME, ("clearing IRP_REF_INTERVAL_TIMER on (%p)\n", *PpCurrentIrp ));
  227. IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_INTERVAL_TIMER);
  228. }
  229. }
  230. if (TotalTimer) {
  231. //
  232. // Try to cancel the operations total timer. If the operation
  233. // returns true then the timer did have a reference to the
  234. // irp. Since we've canceled this timer that reference is
  235. // no longer valid and we can decrement the reference count.
  236. //
  237. // If the cancel returns false then this means either of two things:
  238. //
  239. // a) The timer has already fired.
  240. //
  241. // b) There never was an total timer.
  242. //
  243. // In the case of "b" there is no need to decrement the reference
  244. // count since the "timer" never had a reference to it.
  245. //
  246. // If we have an escape char event pending, we can't overstuff,
  247. // so subtract one from the length
  248. //
  249. // In the case of "a", then the timer itself will be coming
  250. // along and decrement it's reference. Note that the caller
  251. // of this routine might actually be the this timer, but it
  252. // has already decremented the reference.
  253. //
  254. if (KeCancelTimer(TotalTimer)) {
  255. IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_TOTAL_TIMER);
  256. }
  257. }
  258. DbgDump(DBG_IRP, ("<RundownIrpRefs\n"));
  259. PERF_EXIT( PERF_RundownIrpRefs );
  260. return;
  261. }
  262. //
  263. // Recycle the passed in Irp for reuse.
  264. // May be called holding a SpinLock to protect your Irp.
  265. //
  266. VOID
  267. RecycleIrp(
  268. IN PDEVICE_OBJECT PDevObj,
  269. IN PIRP PIrp
  270. )
  271. {
  272. NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
  273. PERF_ENTRY( PERF_RecycleIrp );
  274. DbgDump(DBG_IRP, (">RecycleIrp(%p)\n", PIrp));
  275. if ( PDevObj && PIrp ) {
  276. //
  277. // recycle the Irp
  278. //
  279. IoSetCancelRoutine( PIrp, NULL );
  280. ReuseIrp( PIrp, STATUS_SUCCESS );
  281. FIXUP_RAW_IRP( PIrp, PDevObj );
  282. } else {
  283. DbgDump(DBG_ERR, ("RecycleIrp: INVALID PARAMETER !!\n"));
  284. TEST_TRAP();
  285. }
  286. DbgDump(DBG_IRP, ("<RecycleIrp\n" ));
  287. PERF_EXIT( PERF_RecycleIrp );
  288. return;
  289. }
  290. __inline
  291. VOID
  292. ReuseIrp(
  293. PIRP Irp,
  294. NTSTATUS Status
  295. )
  296. /*--
  297. Routine Description:
  298. This routine is used by drivers to initialize an already allocated IRP for reuse.
  299. It does what IoInitializeIrp does but it saves the allocation flags so that we know
  300. how to free the Irp and take care of quote requirements. Call ReuseIrp
  301. instead of calling IoInitializeIrp to reinitialize an IRP.
  302. Arguments:
  303. Irp - Pointer to Irp to be reused
  304. Status - Status to preinitialize the Iostatus field.
  305. --*/
  306. {
  307. USHORT PacketSize;
  308. CCHAR StackSize;
  309. UCHAR AllocationFlags;
  310. PERF_ENTRY( PERF_ReuseIrp );
  311. // Did anyone forget to pull their cancel routine?
  312. ASSERT(Irp->CancelRoutine == NULL) ;
  313. // We probably don't want thread enqueue'd IRPs to be used
  314. // ping-pong style as they cannot be dequeue unless they
  315. // complete entirely. Not really an issue for worker threads,
  316. // but definitely for operations on application threads.
  317. #if DBG
  318. if (!g_isWin9x) {
  319. ASSERT(IsListEmpty(&Irp->ThreadListEntry));
  320. }
  321. #endif
  322. AllocationFlags = Irp->AllocationFlags;
  323. StackSize = Irp->StackCount;
  324. PacketSize = IoSizeOfIrp(StackSize);
  325. IoInitializeIrp(Irp, PacketSize, StackSize);
  326. Irp->AllocationFlags = AllocationFlags;
  327. Irp->IoStatus.Status = Status;
  328. PERF_EXIT( PERF_ReuseIrp );
  329. return;
  330. }
  331. NTSTATUS
  332. ManuallyCancelIrp(
  333. IN PDEVICE_OBJECT PDevObj,
  334. IN PIRP PIrp
  335. )
  336. {
  337. PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
  338. PDRIVER_CANCEL pCancelRoutine;
  339. NTSTATUS status = STATUS_SUCCESS;
  340. KIRQL irql, cancelIrql;
  341. BOOLEAN bReleased = FALSE;
  342. DbgDump(DBG_IRP, (">ManuallyCancelIrp (%p)\n", PIrp ));
  343. KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
  344. if ( PIrp ) {
  345. pCancelRoutine = PIrp->CancelRoutine;
  346. PIrp->Cancel = TRUE;
  347. //
  348. // If the current irp is not in a cancelable state
  349. // then it *will* try to enter one and the above
  350. // assignment will kill it. If it already is in
  351. // a cancelable state then the following will kill it.
  352. //
  353. if (pCancelRoutine) {
  354. PIrp->CancelRoutine = NULL;
  355. PIrp->CancelIrql = irql;
  356. //
  357. // This irp is in a cancelable state. We simply
  358. // mark it as canceled and manually call the cancel routine.
  359. //
  360. bReleased = TRUE;
  361. KeReleaseSpinLock( &pDevExt->ControlLock, irql );
  362. IoAcquireCancelSpinLock(&cancelIrql);
  363. ASSERT(irql == cancelIrql);
  364. DbgDump(DBG_IRP, ("Invoking Cancel Routine (%p)\n", pCancelRoutine ));
  365. pCancelRoutine(PDevObj, PIrp);
  366. //
  367. // pCancelRoutine releases the cancel lock
  368. //
  369. } else {
  370. DbgDump(DBG_WRN, ("No CancelRoutine on %p\n", PIrp ));
  371. }
  372. } else {
  373. // the Irp could have completed already since we relesed the
  374. // spinlock before calling, so call it a success.
  375. DbgDump(DBG_WRN, ("ManuallyCancelIrp: No Irp!\n"));
  376. }
  377. if (!bReleased) {
  378. KeReleaseSpinLock( &pDevExt->ControlLock, irql );
  379. }
  380. DbgDump(DBG_IRP, (">ManuallyCancelIrp 0x%x\n", status ));
  381. return status;
  382. }
  383. //
  384. // Calculates a Serial Timeout in millisec
  385. //
  386. VOID
  387. CalculateTimeout(
  388. IN OUT PLARGE_INTEGER PTimeOut,
  389. IN ULONG Length,
  390. IN ULONG Multiplier,
  391. IN ULONG Constant
  392. )
  393. {
  394. PERF_ENTRY( PERF_CalculateTimeout );
  395. if (PTimeOut) {
  396. PTimeOut->QuadPart = (LONGLONG)0;
  397. if (Multiplier) {
  398. PTimeOut->QuadPart = UInt32x32To64( Length, Multiplier);
  399. }
  400. if (Constant) {
  401. PTimeOut->QuadPart += (LONGLONG)Constant;
  402. }
  403. //
  404. // put into (relative) 100-nano second units
  405. //
  406. PTimeOut->QuadPart = MILLISEC_TO_100NANOSEC( PTimeOut->QuadPart );
  407. } else {
  408. TEST_TRAP();
  409. }
  410. PERF_EXIT( PERF_CalculateTimeout );
  411. return;
  412. }
  413. // EOF