windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/wceusbsh/utils.c
2020-09-26 16:20:57 +08:00

568 lines
14 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
UTILS.C
Abstract:
Utility functions
Environment:
kernel mode only
Revision History:
07-15-99 : created
Author:
Jeff Midkiff (jeffmi)
Notes:
-- */
#include <wdm.h>
#include <stdio.h>
#include <stdlib.h>
#include <usbdi.h>
#include <usbdlib.h>
#include <ntddser.h>
#include "wceusbsh.h"
__inline
VOID
ReuseIrp (
PIRP Irp,
NTSTATUS Status
);
__inline
VOID
RundownIrpRefs(
IN PIRP *PpCurrentOpIrp,
IN PKTIMER IntervalTimer OPTIONAL,
IN PKTIMER TotalTimer OPTIONAL,
IN PDEVICE_EXTENSION PDevExt
);
VOID
TryToCompleteCurrentIrp(
IN PDEVICE_EXTENSION PDevExt,
IN NTSTATUS ReturnStatus,
IN PIRP *PpCurrentIrp,
IN PLIST_ENTRY PIrpQueue OPTIONAL,
IN PKTIMER PIntervalTimer OPTIONAL,
IN PKTIMER PTotalTimer OPTIONAL,
IN PSTART_ROUTINE PStartNextIrpRoutine OPTIONAL,
IN PGET_NEXT_ROUTINE PGetNextIrpRoutine OPTIONAL,
IN LONG ReferenceType,
IN BOOLEAN CompleteRequest,
IN KIRQL IrqlForRelease
)
/*++
Routine Description:
This routine attempts to rundown all of the reasons there are
references on the current Irp. If everything can be killed
then it will complete this Irp, and then try to start another.
Similiar to StartIo.
NOTE: This routine assumes that it is called with the control lock held.
Arguments:
Extension - Simply a pointer to the device extension.
SynchRoutine - A routine that will synchronize with the isr
and attempt to remove the knowledge of the
current irp from the isr. NOTE: This pointer
can be null.
IrqlForRelease - This routine is called with the control lock held.
This is the irql that was current when it was acquired.
ReturnStatus - The irp's status field will be set to this value, if
this routine can complete the irp.
Return Value:
None.
--*/
{
PERF_ENTRY( PERF_TryToCompleteCurrentIrp );
if ( !PDevExt || !PpCurrentIrp || !(*PpCurrentIrp) ) {
DbgDump(DBG_ERR, ("TryToCompleteCurrentIrp: INVALID PARAMETER\n"));
KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
PERF_EXIT( PERF_TryToCompleteCurrentIrp );
TEST_TRAP();
return;
}
DbgDump(DBG_IRP|DBG_TRACE, (">TryToCompleteCurrentIrp(%p, 0x%x)\n", *PpCurrentIrp, ReturnStatus));
//
// We can decrement the reference to "remove" the fact
// that the caller no longer will be accessing this irp.
//
IRP_CLEAR_REFERENCE(*PpCurrentIrp, ReferenceType);
//
// Try to run down all other references (i.e., Timers) to this irp.
//
RundownIrpRefs(PpCurrentIrp, PIntervalTimer, PTotalTimer, PDevExt);
//
// See if the ref count is zero after trying to kill everybody else.
//
if (!IRP_REFERENCE_COUNT(*PpCurrentIrp)) {
//
// The ref count was zero so we should complete this request.
//
PIRP pNewIrp;
DbgDump( DBG_IRP, ("!IRP_REFERENCE_COUNT\n"));
// set Irp's return status
(*PpCurrentIrp)->IoStatus.Status = ReturnStatus;
if (ReturnStatus == STATUS_CANCELLED) {
(*PpCurrentIrp)->IoStatus.Information = 0;
}
if (PGetNextIrpRoutine) {
//
// Get the next Irp off the specified Irp queue
//
KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
DbgDump( DBG_IRP, ("<< Current IRQL(1)\n"));
DbgDump( DBG_IRP, ("Calling GetNextUserIrp\n"));
(*PGetNextIrpRoutine)(PpCurrentIrp, PIrpQueue, &pNewIrp, CompleteRequest, PDevExt);
if (pNewIrp) {
//
// There was an Irp in the queue
//
DbgDump( DBG_IRP, ("Calling StartNextIrpRoutine\n"));
//
// kick-start the next Irp
//
PStartNextIrpRoutine(PDevExt);
}
} else {
PIRP pOldIrp = *PpCurrentIrp;
//
// There was no GetNextIrpRoutine.
// We will simply complete the Irp.
//
DbgDump( DBG_IRP, ("No GetNextIrpRoutine\n"));
*PpCurrentIrp = NULL;
KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
DbgDump( DBG_IRP, ("<< Current IRQL(2)\n"));
if (CompleteRequest) {
//
// complete the Irp
//
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 ));
ReleaseRemoveLock(&PDevExt->RemoveLock, pOldIrp);
IoCompleteRequest( pOldIrp, IO_NO_INCREMENT );
}
}
} else {
//
// Irp still has outstanding references
//
DbgDump(DBG_WRN|DBG_IRP|DBG_TRACE, ("Current IRP %p still has reference of %x\n", *PpCurrentIrp,
((UINT_PTR)((IoGetCurrentIrpStackLocation((*PpCurrentIrp))->
Parameters.Others.Argument4)))));
KeReleaseSpinLock(&PDevExt->ControlLock, IrqlForRelease);
DbgDump( DBG_IRP, ("<< Current IRQL(3)\n"));
}
DbgDump( DBG_IRP|DBG_TRACE, ("<TryToCompleteCurrentIrp\n"));
PERF_EXIT( PERF_TryToCompleteCurrentIrp );
return;
}
VOID
RundownIrpRefs(
IN PIRP *PpCurrentIrp,
IN PKTIMER IntervalTimer OPTIONAL,
IN PKTIMER TotalTimer OPTIONAL,
IN PDEVICE_EXTENSION PDevExt
)
/*++
Routine Description:
This routine runs through the various items that *could*
have a reference to the current read/write Irp. It try's to kill
the reason. If it does succeed in killing the reason it
will decrement the reference count on the irp.
NOTE: This routine assumes that it is called with the control lock held.
Arguments:
PpCurrentIrp - Pointer to a pointer to current irp for the
particular operation.
IntervalTimer - Pointer to the interval timer for the operation.
NOTE: This could be null.
TotalTimer - Pointer to the total timer for the operation.
NOTE: This could be null.
PDevExt - Pointer to device extension
Return Value:
None.
--*/
{
PERF_ENTRY( PERF_RundownIrpRefs );
if ( !PDevExt || !PpCurrentIrp || !(*PpCurrentIrp) ) {
DbgDump(DBG_ERR, ("RundownIrpRefs: INVALID PARAMETER\n"));
PERF_EXIT( PERF_RundownIrpRefs );
TEST_TRAP();
return;
}
DbgDump(DBG_IRP, (">RundownIrpRefs(%p)\n", *PpCurrentIrp));
//
// This routine is called with the cancel spin lock held
// so we know only one thread of execution can be in here
// at one time.
//
//
// First we see if there is still a cancel routine. If
// so then we can decrement the count by one.
//
if ((*PpCurrentIrp)->CancelRoutine) {
IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_CANCEL);
IoSetCancelRoutine(*PpCurrentIrp, NULL);
}
if (IntervalTimer) {
//
// Try to cancel the operation's interval timer. If the operation
// returns true then the timer did have a reference to the
// irp. Since we've canceled this timer that reference is
// no longer valid and we can decrement the reference count.
//
// If the cancel returns false then this means either of two things:
//
// a) The timer has already fired.
//
// b) There never was an interval timer.
//
// In the case of "b" there is no need to decrement the reference
// count since the "timer" never had a reference to it.
//
// In the case of "a", then the timer itself will be coming
// along and decrement it's reference. Note that the caller
// of this routine might actually be the this timer, but it
// has already decremented the reference.
//
if (KeCancelTimer(IntervalTimer)) {
IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_INTERVAL_TIMER);
} else {
// short circuit the read irp from the interval timer
DbgDump(DBG_IRP|DBG_TIME, ("clearing IRP_REF_INTERVAL_TIMER on (%p)\n", *PpCurrentIrp ));
IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_INTERVAL_TIMER);
}
}
if (TotalTimer) {
//
// Try to cancel the operations total timer. If the operation
// returns true then the timer did have a reference to the
// irp. Since we've canceled this timer that reference is
// no longer valid and we can decrement the reference count.
//
// If the cancel returns false then this means either of two things:
//
// a) The timer has already fired.
//
// b) There never was an total timer.
//
// In the case of "b" there is no need to decrement the reference
// count since the "timer" never had a reference to it.
//
// If we have an escape char event pending, we can't overstuff,
// so subtract one from the length
//
// In the case of "a", then the timer itself will be coming
// along and decrement it's reference. Note that the caller
// of this routine might actually be the this timer, but it
// has already decremented the reference.
//
if (KeCancelTimer(TotalTimer)) {
IRP_CLEAR_REFERENCE(*PpCurrentIrp, IRP_REF_TOTAL_TIMER);
}
}
DbgDump(DBG_IRP, ("<RundownIrpRefs\n"));
PERF_EXIT( PERF_RundownIrpRefs );
return;
}
//
// Recycle the passed in Irp for reuse.
// May be called holding a SpinLock to protect your Irp.
//
VOID
RecycleIrp(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
PERF_ENTRY( PERF_RecycleIrp );
DbgDump(DBG_IRP, (">RecycleIrp(%p)\n", PIrp));
if ( PDevObj && PIrp ) {
//
// recycle the Irp
//
IoSetCancelRoutine( PIrp, NULL );
ReuseIrp( PIrp, STATUS_SUCCESS );
FIXUP_RAW_IRP( PIrp, PDevObj );
} else {
DbgDump(DBG_ERR, ("RecycleIrp: INVALID PARAMETER !!\n"));
TEST_TRAP();
}
DbgDump(DBG_IRP, ("<RecycleIrp\n" ));
PERF_EXIT( PERF_RecycleIrp );
return;
}
__inline
VOID
ReuseIrp(
PIRP Irp,
NTSTATUS Status
)
/*--
Routine Description:
This routine is used by drivers to initialize an already allocated IRP for reuse.
It does what IoInitializeIrp does but it saves the allocation flags so that we know
how to free the Irp and take care of quote requirements. Call ReuseIrp
instead of calling IoInitializeIrp to reinitialize an IRP.
Arguments:
Irp - Pointer to Irp to be reused
Status - Status to preinitialize the Iostatus field.
--*/
{
USHORT PacketSize;
CCHAR StackSize;
UCHAR AllocationFlags;
PERF_ENTRY( PERF_ReuseIrp );
// Did anyone forget to pull their cancel routine?
ASSERT(Irp->CancelRoutine == NULL) ;
// We probably don't want thread enqueue'd IRPs to be used
// ping-pong style as they cannot be dequeue unless they
// complete entirely. Not really an issue for worker threads,
// but definitely for operations on application threads.
#if DBG
if (!g_isWin9x) {
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
}
#endif
AllocationFlags = Irp->AllocationFlags;
StackSize = Irp->StackCount;
PacketSize = IoSizeOfIrp(StackSize);
IoInitializeIrp(Irp, PacketSize, StackSize);
Irp->AllocationFlags = AllocationFlags;
Irp->IoStatus.Status = Status;
PERF_EXIT( PERF_ReuseIrp );
return;
}
NTSTATUS
ManuallyCancelIrp(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
PDRIVER_CANCEL pCancelRoutine;
NTSTATUS status = STATUS_SUCCESS;
KIRQL irql, cancelIrql;
BOOLEAN bReleased = FALSE;
DbgDump(DBG_IRP, (">ManuallyCancelIrp (%p)\n", PIrp ));
KeAcquireSpinLock( &pDevExt->ControlLock, &irql );
if ( PIrp ) {
pCancelRoutine = PIrp->CancelRoutine;
PIrp->Cancel = TRUE;
//
// If the current irp is not in a cancelable state
// then it *will* try to enter one and the above
// assignment will kill it. If it already is in
// a cancelable state then the following will kill it.
//
if (pCancelRoutine) {
PIrp->CancelRoutine = NULL;
PIrp->CancelIrql = irql;
//
// This irp is in a cancelable state. We simply
// mark it as canceled and manually call the cancel routine.
//
bReleased = TRUE;
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
IoAcquireCancelSpinLock(&cancelIrql);
ASSERT(irql == cancelIrql);
DbgDump(DBG_IRP, ("Invoking Cancel Routine (%p)\n", pCancelRoutine ));
pCancelRoutine(PDevObj, PIrp);
//
// pCancelRoutine releases the cancel lock
//
} else {
DbgDump(DBG_WRN, ("No CancelRoutine on %p\n", PIrp ));
}
} else {
// the Irp could have completed already since we relesed the
// spinlock before calling, so call it a success.
DbgDump(DBG_WRN, ("ManuallyCancelIrp: No Irp!\n"));
}
if (!bReleased) {
KeReleaseSpinLock( &pDevExt->ControlLock, irql );
}
DbgDump(DBG_IRP, (">ManuallyCancelIrp 0x%x\n", status ));
return status;
}
//
// Calculates a Serial Timeout in millisec
//
VOID
CalculateTimeout(
IN OUT PLARGE_INTEGER PTimeOut,
IN ULONG Length,
IN ULONG Multiplier,
IN ULONG Constant
)
{
PERF_ENTRY( PERF_CalculateTimeout );
if (PTimeOut) {
PTimeOut->QuadPart = (LONGLONG)0;
if (Multiplier) {
PTimeOut->QuadPart = UInt32x32To64( Length, Multiplier);
}
if (Constant) {
PTimeOut->QuadPart += (LONGLONG)Constant;
}
//
// put into (relative) 100-nano second units
//
PTimeOut->QuadPart = MILLISEC_TO_100NANOSEC( PTimeOut->QuadPart );
} else {
TEST_TRAP();
}
PERF_EXIT( PERF_CalculateTimeout );
return;
}
// EOF