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.
 
 
 
 
 
 

3326 lines
99 KiB

/*****************************************************************************
* irpstrm.cpp - IRP stream object implementation
*****************************************************************************
* Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
*/
#ifndef PC_KDEXT
#include "private.h"
VOID
IrpStreamCancelRoutine
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#if DBG
#define DbgAcquireMappingIrp(a,b) AcquireMappingIrp(a,b)
#define DbgAcquireUnmappingIrp(a) AcquireUnmappingIrp(a)
#else
#define DbgAcquireMappingIrp(a,b) AcquireMappingIrp(b)
#define DbgAcquireUnmappingIrp(a) AcquireUnmappingIrp()
#endif
#endif // PC_KDEXT
#define WHOLE_MDL_THRESHOLD (PAGE_SIZE * 16)
#define PRE_OFFSET_THRESHOLD ((PAGE_SIZE / 4) * 3)
#define POST_OFFSET_THRESHOLD (PAGE_SIZE / 4)
#define MAX_PAGES_PER_MDL 16
#define POOL_TAG_IRPSTREAM_IRP_CONTEXT 'sIcP'
#define MAPPING_QUEUE_SIZE 128 // maximum entries in the mapping queue
#define MAX_MAPPINGS 15 // maximum mappings per IoMapTransfer call
// (this results in at most 16 map registers)
/*****************************************************************************
* PACKET_HEADER
*****************************************************************************
* Extension of KSSTREAM_HEADER containing a pointer to the matching MDL and
* progress indicators for locking, mapping and unmapping.
*
* Invariants: BytesTotal >= MapPosition >= UnmapPosition
*
* It is true of MapPosition and UnmapPosition that at most one packet in an
* IrpStream may have a value for the field that is not zero or BytesTotal.
* If there is such a packet, all packets preceding it have 0 in that field
* and all packets following have BytesTotal in that field. The two fields
* form two progress indicators showing the position in the IrpStream that is
* currently being mapped or unmapped.
*
* When both the BytesX are equal, the packet is ready for completion. When
* this is true of all packets in an IRP, the IRP is ready for completion.
*
* InputPosition and OutputPosition are used to locate the packet in the
* stream. InputPosition refers to the byte position of the packet on the
* input side. This means that looped packets are counted only once in this
* context. OutputPosition refers to the byte position of the packet on the
* output side. This means that looped packets are counted as many times as
* they are 'played'.
*/
typedef struct PACKET_HEADER_
{
PKSSTREAM_HEADER StreamHeader;
PMDL MdlAddress;
ULONG BytesTotal;
ULONG MapPosition;
ULONG UnmapPosition;
ULONG MapCount;
ULONGLONG InputPosition;
ULONGLONG OutputPosition;
BOOLEAN IncrementMapping;
BOOLEAN IncrementUnmapping;
struct PACKET_HEADER_ * Next;
}
PACKET_HEADER, *PPACKET_HEADER;
typedef struct
{
#if (DBG)
ULONG IrpLabel;
ULONG Reserved;
#endif
PEPROCESS SubmissionProcess;
PVOID IrpStream;
PPACKET_HEADER LockingPacket;
PPACKET_HEADER MappingPacket;
PPACKET_HEADER UnmappingPacket;
PACKET_HEADER Packets[1]; // variable
}
IRP_CONTEXT, *PIRP_CONTEXT;
typedef struct
{
PHYSICAL_ADDRESS PhysicalAddress;
PIRP Irp;
PPACKET_HEADER PacketHeader;
PVOID VirtualAddress;
ULONG ByteCount;
ULONG Flags;
PVOID MapRegisterBase;
PVOID Tag;
ULONG MappingStatus;
PVOID SubpacketVa;
ULONG SubpacketBytes;
}
MAPPING_QUEUE_ENTRY, *PMAPPING_QUEUE_ENTRY;
#define MAPPING_STATUS_EMPTY 0x0000
#define MAPPING_STATUS_MAPPED 0x0001
#define MAPPING_STATUS_DELIVERED 0x0002
#define MAPPING_STATUS_REVOKED 0x0004
#define MAPPING_FLAG_END_OF_SUBPACKET 0x0002
#define PPACKET_HEADER_LOOP PPACKET_HEADER(1)
#define CAST_LVALUE(type,lvalue) (*((type*)&(lvalue)))
#define FLINK_IRP_STORAGE(Irp) \
CAST_LVALUE(PLIST_ENTRY,(Irp)->Tail.Overlay.ListEntry.Flink)
#define BLINK_IRP_STORAGE(Irp) \
CAST_LVALUE(PLIST_ENTRY,(Irp)->Tail.Overlay.ListEntry.Blink)
#define FIRST_STREAM_HEADER_IRP_STORAGE(Irp) \
CAST_LVALUE(PKSSTREAM_HEADER,(Irp)->AssociatedIrp.SystemBuffer)
#define IRP_CONTEXT_IRP_STORAGE(Irp) \
CAST_LVALUE(PIRP_CONTEXT,IoGetCurrentIrpStackLocation(Irp)-> \
Parameters.Others.Argument4)
/*****************************************************************************
* CIrpStream
*****************************************************************************
* Irp stream implementation.
*/
class CIrpStream : public IIrpStreamVirtual,
public IIrpStreamPhysical,
public CUnknown
{
private:
PIKSSHELLTRANSPORT m_TransportSink;
PIKSSHELLTRANSPORT m_TransportSource;
KSSTATE m_ksState;
BOOLEAN m_Flushing;
BOOLEAN JustInTime;
BOOLEAN WriteOperation;
BOOLEAN WasExhausted;
ULONG ProbeFlags;
PIRP LockedIrp;
KSPIN_LOCK m_kSpinLock;
KSPIN_LOCK m_RevokeLock;
KSPIN_LOCK m_irpStreamPositionLock;
IRPSTREAM_POSITION m_irpStreamPosition;
ULONGLONG InputPosition;
ULONGLONG OutputPosition;
PADAPTER_OBJECT BusMasterAdapterObject;
PDEVICE_OBJECT FunctionalDeviceObject;
PIRPSTREAMNOTIFY Notify;
PIRPSTREAMNOTIFYPHYSICAL NotifyPhysical;
//
// Master spin lock taken when acquiring an IRP.
//
KIRQL m_kIrqlOld;
LIST_ENTRY PreLockQueue;
KSPIN_LOCK PreLockQueueLock;
LIST_ENTRY LockedQueue;
KSPIN_LOCK LockedQueueLock;
LIST_ENTRY MappedQueue;
KSPIN_LOCK MappedQueueLock;
struct
{
PMAPPING_QUEUE_ENTRY Array;
ULONG Head;
ULONG Tail;
ULONG Get;
} MappingQueue;
#if (DBG)
ULONG MappingsOutstanding;
ULONG MappingsQueued;
ULONG IrpLabel;
ULONG IrpLabelToComplete;
ULONGLONG timeStep;
ULONG irpCompleteCount;
PCHAR MappingIrpOwner;
PCHAR UnmappingIrpOwner;
#endif
PIRP AcquireMappingIrp
(
#if DBG
IN PCHAR Owner,
#endif
IN BOOLEAN NotifyExhausted
);
PIRP AcquireUnmappingIrp
(
#if DBG
IN PCHAR Owner
#endif
);
void ReleaseMappingIrp
(
IN PIRP Irp,
IN PPACKET_HEADER PacketHeader OPTIONAL
);
void ReleaseUnmappingIrp
(
IN PIRP Irp,
IN PPACKET_HEADER PacketHeader OPTIONAL
);
NTSTATUS EnqueueMapping
(
IN PHYSICAL_ADDRESS PhysicalAddress,
IN PIRP Irp,
IN PPACKET_HEADER PacketHeader,
IN PVOID VirtualAddress,
IN ULONG ByteCount,
IN ULONG Flags,
IN PVOID MapRegisterBase,
IN ULONG MappingStatus,
IN PVOID SubpacketVa,
IN ULONG SubpacketBytes
);
PMAPPING_QUEUE_ENTRY GetQueuedMapping
( void
);
PMAPPING_QUEUE_ENTRY DequeueMapping
( void
);
void
CancelMappings
(
IN PIRP pIrp
);
void DbgQueues
( void
);
void
ForwardIrpsInQueue
(
IN PLIST_ENTRY Queue,
IN PKSPIN_LOCK SpinLock
);
public:
DECLARE_STD_UNKNOWN();
DEFINE_STD_CONSTRUCTOR(CIrpStream);
~CIrpStream();
IMP_IIrpStreamVirtual;
IMP_IIrpStreamPhysical_;
PRKTHREAD m_CancelAllIrpsThread;
/*************************************************************************
* Friends
*/
friend
IO_ALLOCATION_ACTION
CallbackFromIoAllocateAdapterChannel
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Reserved,
IN PVOID MapRegisterBase,
IN PVOID VoidContext
);
friend
VOID
IrpStreamCancelRoutine
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#ifdef PC_KDEXT
// Debugger extension routines
friend
VOID
PCKD_AcquireDeviceData
(
PDEVICE_CONTEXT DeviceContext,
PLIST_ENTRY SubdeviceList,
ULONG Flags
);
friend
VOID
PCKD_AcquireIrpStreamData
(
PVOID CurrentPinEntry,
CIrpStream *RemoteIrpStream,
CIrpStream *LocalIrpStream
);
#endif
};
/*****************************************************************************
* CALLBACK_CONTEXT
*****************************************************************************
* Context for IoAllocateAdapterChannel() callback.
*/
typedef struct
{
CIrpStream * IrpStream;
// Used for BusMasterAdapterObject, WriteOperation, ApplyMappingConstraints(), EnqueueMapping()
PPACKET_HEADER PacketHeader;
// Used for MdlAddress, MapRegisterBase (out)
// Queue references this also
PIRP Irp;
// Used for mapping cancellation
KEVENT Event;
// Used for partial mappings
ULONG BytesThisMapping;
// Used for partial mappings
BOOL LastSubPacket;
}
CALLBACK_CONTEXT, *PCALLBACK_CONTEXT;
#ifndef PC_KDEXT
/*****************************************************************************
* Factory.
*/
#pragma code_seg("PAGE")
/*****************************************************************************
* CreateIrpStream()
*****************************************************************************
* Creates an IrpStream object.
*/
NTSTATUS
CreateIrpStream
(
OUT PUNKNOWN * Unknown,
IN REFCLSID,
IN PUNKNOWN UnknownOuter OPTIONAL,
IN POOL_TYPE PoolType
)
{
PAGED_CODE();
ASSERT(Unknown);
_DbgPrintF(DEBUGLVL_LIFETIME,("Creating IRPSTREAM"));
STD_CREATE_BODY_( CIrpStream,
Unknown,
UnknownOuter,
PoolType,
PIRPSTREAMVIRTUAL );
}
/*****************************************************************************
* PcNewIrpStreamVirtual()
*****************************************************************************
* Creates and initializes an IrpStream object with a virtual access
* interface.
*/
PORTCLASSAPI
NTSTATUS
NTAPI
PcNewIrpStreamVirtual
(
OUT PIRPSTREAMVIRTUAL * OutIrpStreamVirtual,
IN PUNKNOWN OuterUnknown OPTIONAL,
IN BOOLEAN WriteOperation,
IN PKSPIN_CONNECT PinConnect,
IN PDEVICE_OBJECT DeviceObject
)
{
PAGED_CODE();
ASSERT(OutIrpStreamVirtual);
ASSERT(PinConnect);
ASSERT(DeviceObject);
PUNKNOWN unknown;
NTSTATUS ntStatus = CreateIrpStream( &unknown,
GUID_NULL,
OuterUnknown,
NonPagedPool );
if(NT_SUCCESS(ntStatus))
{
PIRPSTREAMVIRTUAL irpStream;
ntStatus = unknown->QueryInterface( IID_IIrpStreamVirtual,
(PVOID *) &irpStream );
if(NT_SUCCESS(ntStatus))
{
ntStatus = irpStream->Init( WriteOperation,
PinConnect,
DeviceObject,
NULL );
if(NT_SUCCESS(ntStatus))
{
*OutIrpStreamVirtual = irpStream;
}
else
{
irpStream->Release();
}
}
unknown->Release();
}
return ntStatus;
}
/*****************************************************************************
* PcNewIrpStreamPhysical()
*****************************************************************************
* Creates and initializes an IrpStream object with a physical access
* interface.
*/
PORTCLASSAPI
NTSTATUS
NTAPI
PcNewIrpStreamPhysical
(
OUT PIRPSTREAMPHYSICAL * OutIrpStreamPhysical,
IN PUNKNOWN OuterUnknown OPTIONAL,
IN BOOLEAN WriteOperation,
IN PKSPIN_CONNECT PinConnect,
IN PDEVICE_OBJECT DeviceObject,
IN PADAPTER_OBJECT AdapterObject
)
{
PAGED_CODE();
ASSERT(OutIrpStreamPhysical);
ASSERT(DeviceObject);
ASSERT(AdapterObject);
PUNKNOWN unknown;
NTSTATUS ntStatus = CreateIrpStream( &unknown,
GUID_NULL,
OuterUnknown,
NonPagedPool );
if(NT_SUCCESS(ntStatus))
{
PIRPSTREAMPHYSICAL irpStream;
ntStatus = unknown->QueryInterface( IID_IIrpStreamPhysical,
(PVOID *) &irpStream );
if(NT_SUCCESS(ntStatus))
{
ntStatus = irpStream->Init( WriteOperation,
PinConnect,
DeviceObject,
AdapterObject );
if(NT_SUCCESS(ntStatus))
{
*OutIrpStreamPhysical = irpStream;
}
else
{
irpStream->Release();
}
}
unknown->Release();
}
return ntStatus;
}
/*****************************************************************************
* Member functions.
*/
/*****************************************************************************
* CIrpStream::~CIrpStream()
*****************************************************************************
* Destructor.
*/
CIrpStream::
~CIrpStream
( void
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_LIFETIME,("Destroying IRPSTREAM (0x%08x)",this));
CancelAllIrps( TRUE ); // reset position counters
if(Notify)
{
Notify->Release();
}
if(NotifyPhysical)
{
NotifyPhysical->Release();
}
if(MappingQueue.Array)
{
ExFreePool(MappingQueue.Array);
}
}
/*****************************************************************************
* CIrpStream::NonDelegatingQueryInterface()
*****************************************************************************
* Obtains an interface.
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
NonDelegatingQueryInterface
(
REFIID Interface,
PVOID * Object
)
{
PAGED_CODE();
ASSERT(Object);
if(IsEqualGUIDAligned(Interface,IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(PIRPSTREAMVIRTUAL(this)));
}
else
if(IsEqualGUIDAligned(Interface,IID_IIrpStreamSubmit))
{
*Object = PVOID(PIRPSTREAMSUBMIT(PIRPSTREAMVIRTUAL(this)));
}
else
if(IsEqualGUIDAligned(Interface,IID_IIrpStream))
{
*Object = PVOID(PIRPSTREAM(PIRPSTREAMVIRTUAL(this)));
}
else
if(IsEqualGUIDAligned(Interface,IID_IKsShellTransport))
{
*Object = PVOID(PIKSSHELLTRANSPORT(PIRPSTREAMVIRTUAL(this)));
}
else
if(IsEqualGUIDAligned(Interface,IID_IIrpStreamVirtual))
{
// Only valid if not configured for physical access.
if(BusMasterAdapterObject)
{
*Object = NULL;
}
else
{
*Object = QICAST(PIRPSTREAMVIRTUAL);
}
}
else
if(IsEqualGUIDAligned(Interface,IID_IIrpStreamPhysical))
{
// Only valid if configured for physical access or uninitialized.
if(BusMasterAdapterObject || (ProbeFlags == 0))
{
*Object = QICAST(PIRPSTREAMPHYSICAL);
}
else
{
*Object = NULL;
}
}
else
{
*Object = NULL;
}
if(*Object)
{
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
/*****************************************************************************
* GetPartialMdlCountForMdl()
*****************************************************************************
* Determine number of partial MDLs that are required for a source MDL.
*/
ULONG
GetPartialMdlCountForMdl
(
IN PVOID Va,
IN ULONG Size
)
{
ULONG result = 1;
if(Size > WHOLE_MDL_THRESHOLD)
{
ULONG pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Va,Size);
if(BYTE_OFFSET(Va) > PRE_OFFSET_THRESHOLD)
{
pageCount--;
}
if(BYTE_OFFSET(PVOID(PBYTE(Va) + Size - 1)) < POST_OFFSET_THRESHOLD)
{
pageCount--;
}
result = (pageCount + MAX_PAGES_PER_MDL - 1) / MAX_PAGES_PER_MDL;
}
return result;
}
STDMETHODIMP_(NTSTATUS)
CIrpStream::
TransferKsIrp
(
IN PIRP Irp,
OUT PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles the arrival of a streaming IRP via the shell
transport.
Arguments:
Irp -
Contains a pointer to the streaming IRP submitted to the queue.
NextTransport -
Contains a pointer to a location at which to deposit a pointer
to the next transport interface to receive the IRP. May be set
to NULL indicating the IRP should not be forwarded further.
Return Value:
STATUS_SUCCESS, STATUS_PENDING or some error status.
--*/
{
ASSERT(Irp);
ASSERT(NextTransport);
ASSERT(m_TransportSink);
ASSERT(m_TransportSource);
//
// Shunt IRPs to the next object if we are not ready.
//
if(m_Flushing || (m_ksState == KSSTATE_STOP) || Irp->Cancel ||
! NT_SUCCESS(Irp->IoStatus.Status))
{
*NextTransport = m_TransportSink;
return STATUS_SUCCESS;
}
//
// Not smart enough to do this yet.
//
*NextTransport = NULL;
//
// Prepare the IRP using KS's handiest function.
//
NTSTATUS ntStatus;
if(ProbeFlags)
{
ntStatus = KsProbeStreamIrp( Irp,
ProbeFlags,
sizeof(KSSTREAM_HEADER) );
}
else
{
ntStatus = STATUS_SUCCESS;
}
PIRP_CONTEXT irpContext;
if(NT_SUCCESS(ntStatus))
{
ntStatus = STATUS_PENDING;
ULONG packetCount = 0;
//
// Count the number of 'packet headers' we will be needing.
//
PKSSTREAM_HEADER streamHeader = FIRST_STREAM_HEADER_IRP_STORAGE(Irp);
if( (!streamHeader->DataUsed && WriteOperation) ||
(!streamHeader->FrameExtent && !WriteOperation) )
{
//
// At least one for the Irp context.
//
packetCount = 1;
}
else
{
for(PMDL mdl = Irp->MdlAddress; mdl; mdl = mdl->Next, streamHeader++)
{
if(JustInTime)
{
packetCount += GetPartialMdlCountForMdl(
#ifdef UNDER_NT
MmGetSystemAddressForMdlSafe(mdl,HighPagePriority),
#else
MmGetSystemAddressForMdl(mdl),
#endif
( WriteOperation ?
streamHeader->DataUsed :
streamHeader->FrameExtent ) );
}
else
{
packetCount++;
}
}
}
irpContext = PIRP_CONTEXT( ExAllocatePoolWithTag( NonPagedPool,
( sizeof(IRP_CONTEXT) +
( sizeof(PACKET_HEADER) *
(packetCount - 1) ) ),
POOL_TAG_IRPSTREAM_IRP_CONTEXT ) );
if(irpContext)
{
IRP_CONTEXT_IRP_STORAGE(Irp) = irpContext;
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
if(NT_SUCCESS(ntStatus))
{
irpContext->SubmissionProcess = IoGetCurrentProcess();
irpContext->LockingPacket =
irpContext->MappingPacket =
irpContext->UnmappingPacket = &irpContext->Packets[0];
irpContext->IrpStream = PVOID(this);
#if (DBG)
irpContext->IrpLabel = IrpLabel++;
#endif
PKSSTREAM_HEADER streamHeader = FIRST_STREAM_HEADER_IRP_STORAGE(Irp);
PPACKET_HEADER packetHeader = &irpContext->Packets[0];
PPACKET_HEADER firstLooped = NULL;
PPACKET_HEADER prevLooped = NULL;
if( (!streamHeader->DataUsed && WriteOperation) ||
(!streamHeader->FrameExtent && !WriteOperation) )
{
RtlZeroMemory( packetHeader, sizeof( PACKET_HEADER ) );
packetHeader->MapCount = 1;
packetHeader->StreamHeader = streamHeader;
packetHeader->InputPosition = InputPosition;
packetHeader->OutputPosition = OutputPosition;
}
else
{
for(PMDL mdl = Irp->MdlAddress;
mdl && NT_SUCCESS(ntStatus);
mdl = mdl->Next, streamHeader++)
{
ULONG bytesRemaining = ( WriteOperation ?
streamHeader->DataUsed :
streamHeader->FrameExtent );
m_irpStreamPosition.ullCurrentExtent += bytesRemaining;
BOOLEAN createPartials = ( JustInTime &&
( bytesRemaining > WHOLE_MDL_THRESHOLD ) );
ULONG currentOffset = MmGetMdlByteOffset(mdl);
#ifdef UNDER_NT
PVOID currentVA = MmGetSystemAddressForMdlSafe(mdl,HighPagePriority);
#else
PVOID currentVA = MmGetSystemAddressForMdl(mdl);
#endif
while(bytesRemaining)
{
PMDL partialMdl;
ULONG partialMdlSize;
if(! createPartials)
{
partialMdl = mdl;
partialMdlSize = bytesRemaining;
}
else
{
ASSERT(!"Trying to create partials");
#if 0
partialMdlSize = MAX_PAGES_PER_MDL * PAGE_SIZE;
if(currentOffset)
{
//
// Handle initial offset.
//
partialMdlSize -= currentOffset;
if(currentOffset > PRE_OFFSET_THRESHOLD)
{
partialMdlSize += PAGE_SIZE;
}
currentOffset = 0;
}
else
if(partialMdlSize > bytesRemaining)
{
partialMdlSize = bytesRemaining;
}
ASSERT(partialMdlSize <= bytesRemaining);
partialMdl = IoAllocateMdl( currentVA,
partialMdlSize,
FALSE,
FALSE,
NULL );
if(partialMdl)
{
IoBuildPartialMdl( mdl,
partialMdl,
currentVA,
partialMdlSize );
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
#endif
}
bytesRemaining -= partialMdlSize;
currentVA = PVOID(PBYTE(currentVA) + partialMdlSize);
if( streamHeader->OptionsFlags
& KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA
)
{
if(prevLooped)
{
// Point the previous looped packet to this one.
prevLooped->Next = packetHeader;
}
else
{
// No previous looped packet.
firstLooped = packetHeader;
}
prevLooped = packetHeader;
}
packetHeader->StreamHeader = streamHeader;
packetHeader->MdlAddress = partialMdl;
packetHeader->BytesTotal = partialMdlSize;
packetHeader->MapPosition = 0;
packetHeader->UnmapPosition = 0;
packetHeader->MapCount =
(packetHeader == &irpContext->Packets[0]) ? 1 : 0;
packetHeader->IncrementMapping =
packetHeader->IncrementUnmapping =
(mdl->Next != NULL) || bytesRemaining;
packetHeader->Next = firstLooped;
packetHeader->InputPosition = InputPosition;
packetHeader->OutputPosition = OutputPosition;
InputPosition += packetHeader->BytesTotal;
packetHeader++;
}
}
}
_DbgPrintF(DEBUGLVL_BLAB,("AddIrp() IRP %d 0x%8x",IrpLabel,Irp));
IoMarkIrpPending(Irp);
if(JustInTime)
{
// PreLockQueue feeds JustInTime thread.
KsAddIrpToCancelableQueue( &PreLockQueue,
&PreLockQueueLock,
Irp,
KsListEntryTail,
KsCancelRoutine );
}
else
{
// IRP is locked down in advance and ready to map.
KsAddIrpToCancelableQueue( &LockedQueue,
&LockedQueueLock,
Irp,
KsListEntryTail,
IrpStreamCancelRoutine );
}
}
if(NT_SUCCESS(ntStatus))
{
// need to change WasExhausted BEFORE notifying the sink since
// notifying the sink may result in starvation again.
BOOLEAN TempWasExhausted = WasExhausted;
WasExhausted = FALSE;
if(Notify)
{
Notify->IrpSubmitted(Irp,TempWasExhausted);
}
else
if(NotifyPhysical)
{
NotifyPhysical->IrpSubmitted(Irp,TempWasExhausted);
}
ntStatus = STATUS_PENDING;
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CIrpStream::GetPosition()
*****************************************************************************
* Gets the current position.
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
GetPosition
(
OUT PIRPSTREAM_POSITION pIrpStreamPosition
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
KIRQL oldIrql;
KeAcquireSpinLock(&m_irpStreamPositionLock, &oldIrql );
*pIrpStreamPosition = m_irpStreamPosition;
//
// Assume stream position and unmapping position are the same.
//
pIrpStreamPosition->ullStreamPosition = pIrpStreamPosition->ullUnmappingPosition;
//
// Assume no physical offset.
//
pIrpStreamPosition->ullPhysicalOffset = 0;
//
// Give the notification sink a chance to modify the position.
//
if(Notify)
{
ntStatus = Notify->GetPosition(pIrpStreamPosition);
}
else
if(NotifyPhysical)
{
ntStatus = NotifyPhysical->GetPosition(pIrpStreamPosition);
}
KeReleaseSpinLock(&m_irpStreamPositionLock, oldIrql);
return ntStatus;
}
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CIrpStream::
Connect
(
IN PIKSSHELLTRANSPORT NewTransport OPTIONAL,
OUT PIKSSHELLTRANSPORT *OldTransport OPTIONAL,
IN KSPIN_DATAFLOW DataFlow
)
/*++
Routine Description:
This routine establishes a shell transport connect.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CIrpStream::Connect"));
PAGED_CODE();
KsShellStandardConnect( NewTransport,
OldTransport,
DataFlow,
PIKSSHELLTRANSPORT(PIRPSTREAMVIRTUAL(this)),
&m_TransportSource,
&m_TransportSink);
}
STDMETHODIMP_(NTSTATUS)
CIrpStream::
SetDeviceState
(
IN KSSTATE NewState,
IN KSSTATE OldState,
OUT PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the device state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CIrpStream::SetDeviceState"));
PAGED_CODE();
ASSERT(NextTransport);
if(m_ksState != NewState)
{
m_ksState = NewState;
_DbgPrintF(DEBUGLVL_VERBOSE,("#### IrpStream%p.SetDeviceState: from %d to %d (%d,%d)",this,OldState,NewState,IsListEmpty(&LockedQueue),IsListEmpty(&MappedQueue)));
if(NewState > OldState)
{
*NextTransport = m_TransportSink;
}
else
{
*NextTransport = m_TransportSource;
}
if(NewState == KSSTATE_STOP)
{
if(! WriteOperation)
{
TerminatePacket();
}
CancelAllIrps(TRUE);
}
//
// Adjust the active pin count
//
if( (NewState == KSSTATE_RUN) && (OldState != KSSTATE_RUN) )
{
UpdateActivePinCount( PDEVICE_CONTEXT(FunctionalDeviceObject->DeviceExtension),
TRUE );
// Adjust the stream base position
if(NotifyPhysical)
{
NTSTATUS ntStatus = NotifyPhysical->GetPosition(&m_irpStreamPosition);
if( NT_SUCCESS(ntStatus) )
{
m_irpStreamPosition.ullStreamOffset = m_irpStreamPosition.ullStreamPosition -
m_irpStreamPosition.ullUnmappingPosition;
}
}
}
else
{
if( (NewState != KSSTATE_RUN) && (OldState == KSSTATE_RUN) )
{
UpdateActivePinCount( PDEVICE_CONTEXT(FunctionalDeviceObject->DeviceExtension),
FALSE );
}
}
}
else
{
*NextTransport = NULL;
}
return STATUS_SUCCESS;
}
STDMETHODIMP_(void)
CIrpStream::
SetResetState
(
IN KSRESET ksReset,
OUT PIKSSHELLTRANSPORT* NextTransport
)
/*++
Routine Description:
This routine handles notification that the reset state has changed.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CIrpStream::SetResetState"));
PAGED_CODE();
ASSERT(NextTransport);
//
// Take no action if we were already in this state.
//
if(m_Flushing != (ksReset == KSRESET_BEGIN))
{
//
// Tell the caller to forward the state change to our sink.
//
*NextTransport = m_TransportSink;
//
// Set our local copy of the state.
//
m_Flushing = (ksReset == KSRESET_BEGIN);
//
// Flush the queues if we are beginning a reset.
//
if(m_Flushing)
{
CancelAllIrps(TRUE);
}
}
else
{
*NextTransport = NULL;
}
}
/*****************************************************************************
* CIrpStream::Init()
*****************************************************************************
* Initializes the object.
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
Init
(
IN BOOLEAN WriteOperation_,
IN PKSPIN_CONNECT PinConnect,
IN PDEVICE_OBJECT DeviceObject,
IN PADAPTER_OBJECT AdapterObject OPTIONAL
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_LIFETIME,("Initializing IRPSTREAM (0x%08x)",this));
ASSERT(DeviceObject);
NTSTATUS ntStatus = STATUS_SUCCESS;
#if (DBG)
timeStep = PcGetTimeInterval(0);
irpCompleteCount = 0;
#endif
m_ksState = KSSTATE_STOP;
m_irpStreamPosition.bLoopedInterface =
( PinConnect->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING );
InputPosition = 0;
OutputPosition = 0;
WriteOperation = WriteOperation_;
JustInTime = FALSE;
FunctionalDeviceObject = DeviceObject;
BusMasterAdapterObject = AdapterObject;
ProbeFlags = (( WriteOperation ?
KSPROBE_STREAMWRITE :
KSPROBE_STREAMREAD ) |
KSPROBE_ALLOCATEMDL );
WasExhausted = TRUE;
#if (DBG)
MappingsOutstanding = 0;
MappingsQueued = 0;
#endif
KeInitializeSpinLock(&m_kSpinLock);
KeInitializeSpinLock(&m_RevokeLock);
KeInitializeSpinLock(&m_irpStreamPositionLock);
m_CancelAllIrpsThread = NULL;
if(JustInTime)
{
InitializeListHead(&PreLockQueue);
KeInitializeSpinLock(&PreLockQueueLock);
}
else
{
ProbeFlags |= KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS;
}
InitializeListHead(&LockedQueue);
KeInitializeSpinLock(&LockedQueueLock);
InitializeListHead(&MappedQueue);
KeInitializeSpinLock(&MappedQueueLock);
//
// Source pins don't need to probe because the requestor does it for us.
//
if(PinConnect->PinToHandle)
{
ProbeFlags = 0;
}
// allocate the mapping array
if(BusMasterAdapterObject)
{
MappingQueue.Array = PMAPPING_QUEUE_ENTRY( ExAllocatePoolWithTag( NonPagedPool,
sizeof(MAPPING_QUEUE_ENTRY) * MAPPING_QUEUE_SIZE,
'qMcP' ) );
if(! MappingQueue.Array)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
return ntStatus;
}
#pragma code_seg()
void
CIrpStream::
ForwardIrpsInQueue
(
IN PLIST_ENTRY Queue,
IN PKSPIN_LOCK SpinLock
)
/*++
Routine Description:
This routine forwards all the IRPs in a queue via the shell transport.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CIrpStream::ForwardIrpsInQueue"));
ASSERT(Queue);
ASSERT(SpinLock);
while(1)
{
PIRP irp = KsRemoveIrpFromCancelableQueue( Queue,
SpinLock,
KsListEntryHead,
KsAcquireAndRemoveOnlySingleItem );
if(! irp)
{
break;
}
// TODO: what about revoking the mappings?
//
// Forward the IRP to the next object.
//
if( IRP_CONTEXT_IRP_STORAGE(irp) )
{
_DbgPrintF(DEBUGLVL_VERBOSE,("ForwardIrpsInQueue Freeing non-null context (0x%08x) for IRP (0x%08x)",IRP_CONTEXT_IRP_STORAGE(irp),irp));
ExFreePool(IRP_CONTEXT_IRP_STORAGE(irp));
IRP_CONTEXT_IRP_STORAGE(irp) = NULL;
}
KsShellTransferKsIrp(m_TransportSink,irp);
}
}
/*****************************************************************************
* CIrpStream::CancelAllIrps()
*****************************************************************************
* Cancel all IRPs.
*/
STDMETHODIMP_(void)
CIrpStream::CancelAllIrps
(
IN BOOL ClearPositionCounters
)
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::CancelAllIrps ClearPositionCounters=%s",ClearPositionCounters ? "TRUE" : "FALSE"));
KIRQL kIrqlOldRevoke;
KIRQL kIrqlOldMaster;
// grab the revoke spinlock (must always grab this BEFORE the master spinlock)
KeAcquireSpinLock(&m_RevokeLock, &kIrqlOldRevoke);
// grab the master spinlock
KeAcquireSpinLock(&m_kSpinLock, &kIrqlOldMaster);
// remember this so we won't re-acquire the two locks at CancelMapping in this context
m_CancelAllIrpsThread = KeGetCurrentThread();
if(ProbeFlags)
{
if(JustInTime)
{
KsCancelIo( &PreLockQueue,
&PreLockQueueLock );
}
KsCancelIo( &LockedQueue,
&LockedQueueLock );
KsCancelIo( &MappedQueue,
&MappedQueueLock );
}
else
{
ForwardIrpsInQueue( &MappedQueue,
&MappedQueueLock );
ForwardIrpsInQueue( &LockedQueue,
&LockedQueueLock );
if(JustInTime)
{
ForwardIrpsInQueue( &PreLockQueue,
&PreLockQueueLock );
}
}
//
// clear the input and output position counts
//
BOOLEAN bLooped = m_irpStreamPosition.bLoopedInterface;
ULONGLONG ullStreamOffset = m_irpStreamPosition.ullStreamOffset;
RtlZeroMemory(&m_irpStreamPosition,sizeof(m_irpStreamPosition));
m_irpStreamPosition.bLoopedInterface = bLooped;
m_irpStreamPosition.ullStreamOffset = ullStreamOffset;
if(ClearPositionCounters)
{
InputPosition = 0;
OutputPosition = 0;
}
// release the spinlocks, master first
m_CancelAllIrpsThread = NULL;
KeReleaseSpinLock(&m_kSpinLock, kIrqlOldMaster);
KeReleaseSpinLock(&m_RevokeLock, kIrqlOldRevoke);
}
/*****************************************************************************
* CIrpStream::TerminatePacket()
*****************************************************************************
* Bypasses all reamining mappings for the current packet.
*/
STDMETHODIMP_(void)
CIrpStream::
TerminatePacket
( void
)
{
if(BusMasterAdapterObject)
{
// TODO: What do we do for PCI?
}
else
{
PIRP irp = DbgAcquireUnmappingIrp("TerminatePacket");
if(irp)
{
PPACKET_HEADER packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->UnmappingPacket;
//
// The mapping window should be closed.
//
if( (packetHeader->MapCount == 1) &&
(packetHeader->MapPosition == packetHeader->UnmapPosition) &&
(packetHeader->MapPosition != 0) )
{
//
// Adjust for unused extent.
//
if(m_irpStreamPosition.ullCurrentExtent != ULONGLONG(-1))
{
m_irpStreamPosition.ullCurrentExtent +=
packetHeader->UnmapPosition;
m_irpStreamPosition.ullCurrentExtent -=
packetHeader->BytesTotal;
}
//
// We are not at the start of the packet, and this packet
// should be terminated. The adjusted BytesTotal will get
// copied back into DataUsed in the stream header.
//
packetHeader->BytesTotal = packetHeader->UnmapPosition;
}
else
{
//
// We are at the start of the packet or the packet window is
// not closed.
//
packetHeader = NULL;
}
ReleaseUnmappingIrp(irp,packetHeader);
}
}
}
/*****************************************************************************
* CIrpStream::ChangeOptionsFlags()
*****************************************************************************
* Change the flags for the current mapping and unmapping IRPs.
*
* "Mapping" IRP is the packet currently submitted to the device
* "Unmapping" IRP is the packet currently completed by the device
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
ChangeOptionsFlags
(
IN ULONG MappingOrMask,
IN ULONG MappingAndMask,
IN ULONG UnmappingOrMask,
IN ULONG UnmappingAndMask
)
{
PIRP pIrp;
PPACKET_HEADER pPacketHeader;
ULONG oldOptionsFlags;
NTSTATUS ntStatus = STATUS_SUCCESS;
if((MappingOrMask) || (~MappingAndMask))
{
pIrp = DbgAcquireMappingIrp("ChangeOptionsFlags",FALSE);
if(pIrp)
{
pPacketHeader = IRP_CONTEXT_IRP_STORAGE(pIrp)->MappingPacket;
if((pPacketHeader) && (pPacketHeader->StreamHeader))
{
oldOptionsFlags = pPacketHeader->StreamHeader->OptionsFlags;
oldOptionsFlags |= MappingOrMask;
oldOptionsFlags &= MappingAndMask;
pPacketHeader->StreamHeader->OptionsFlags = oldOptionsFlags;
}
else
ntStatus = STATUS_UNSUCCESSFUL;
ReleaseMappingIrp(pIrp,NULL);
}
else
{
ntStatus = STATUS_UNSUCCESSFUL;
}
}
if((UnmappingOrMask) || (~UnmappingAndMask))
{
pIrp = DbgAcquireUnmappingIrp("ChangeOptionsFlags");
if(pIrp)
{
pPacketHeader = IRP_CONTEXT_IRP_STORAGE(pIrp)->UnmappingPacket;
if((pPacketHeader) && (pPacketHeader->StreamHeader))
{
oldOptionsFlags = pPacketHeader->StreamHeader->OptionsFlags;
oldOptionsFlags |= UnmappingOrMask;
oldOptionsFlags &= UnmappingAndMask;
pPacketHeader->StreamHeader->OptionsFlags = oldOptionsFlags;
}
else
ntStatus = STATUS_UNSUCCESSFUL;
ReleaseUnmappingIrp(pIrp,NULL);
}
else
{
ntStatus = STATUS_UNSUCCESSFUL;
}
}
return ntStatus;
}
/*****************************************************************************
* CIrpStream::GetPacketInfo()
*****************************************************************************
* Get information about the current packet.
*
* "Mapping" information is the packet information currently
* submitted to the device
* "Unmapping" information is the packet information currently
* completed by the device
*
* OutputPosition is the unrolled position of the stream, e.g. the total
* number of bytes to the device.
* InputPosition is the position within the data not include the unrolled
* loops.
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
GetPacketInfo
(
OUT PIRPSTREAMPACKETINFO Mapping OPTIONAL,
OUT PIRPSTREAMPACKETINFO Unmapping OPTIONAL
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
if(Mapping)
{
PIRP irp = DbgAcquireMappingIrp("GetPacketInfo",FALSE);
if(irp)
{
PPACKET_HEADER packetHeader =
IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
Mapping->Header = *packetHeader->StreamHeader;
Mapping->InputPosition = packetHeader->InputPosition;
Mapping->OutputPosition = packetHeader->OutputPosition;
Mapping->CurrentOffset = packetHeader->MapPosition;
ReleaseMappingIrp(irp,NULL);
}
else
{
RtlZeroMemory(&Mapping->Header,sizeof(KSSTREAM_HEADER));
Mapping->InputPosition = InputPosition;
Mapping->OutputPosition = OutputPosition;
Mapping->CurrentOffset = 0;
}
}
if(NT_SUCCESS(ntStatus) && Unmapping)
{
PIRP irp = DbgAcquireUnmappingIrp("GetPacketInfo");
if(irp)
{
PPACKET_HEADER packetHeader =
IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
Unmapping->Header = *packetHeader->StreamHeader;
Unmapping->InputPosition = packetHeader->InputPosition;
Unmapping->OutputPosition = packetHeader->OutputPosition;
Unmapping->CurrentOffset = packetHeader->UnmapPosition;
ReleaseUnmappingIrp(irp,NULL);
}
else
{
RtlZeroMemory(&Unmapping->Header,sizeof(KSSTREAM_HEADER));
Unmapping->InputPosition = InputPosition;
Unmapping->OutputPosition = OutputPosition;
Unmapping->CurrentOffset = 0;
}
}
return ntStatus;
}
/*****************************************************************************
* CIrpStream::SetPacketOffsets()
*****************************************************************************
* Set packet mapping and unmapping offsets to a specified value.
*/
STDMETHODIMP_(NTSTATUS)
CIrpStream::
SetPacketOffsets
(
IN ULONG MappingOffset,
IN ULONG UnmappingOffset
)
{
NTSTATUS ntStatus;
KIRQL oldIrql;
// grab the revoke spinlock BEFORE getting the irp (which will grab the
// master spinlock) so that we don't deadlock
KeAcquireSpinLock(&m_RevokeLock, &oldIrql);
//
// For physical mapping, all mappings must be cancelled.
//
CancelMappings(NULL);
PIRP irp = DbgAcquireMappingIrp("SetPacketOffsets",FALSE);
if(irp)
{
PPACKET_HEADER packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
packetHeader->MapPosition = MappingOffset;
packetHeader->UnmapPosition = UnmappingOffset;
m_irpStreamPosition.ulMappingOffset = MappingOffset;
m_irpStreamPosition.ullMappingPosition = MappingOffset;
m_irpStreamPosition.ulUnmappingOffset = UnmappingOffset;
m_irpStreamPosition.ullUnmappingPosition= UnmappingOffset;
//
// Make sure we have good packet sizes. Normally, the packet sizes
// are recorded in m_irpStreamPosition when the packets are accessed
// (e.g. through GetLockedRegion or Complete). This is generally
// cool because the offsets are zero until this happens anyway. In
// this case, we have non-zero offsets and the possibility that the
// packet has not been accessed yet, hence no valid packet sizes.
// Here's some code to fix that.
//
if(m_irpStreamPosition.ulMappingPacketSize == 0)
{
m_irpStreamPosition.ulMappingPacketSize =
packetHeader->BytesTotal;
}
if(m_irpStreamPosition.ulUnmappingPacketSize == 0)
{
m_irpStreamPosition.ulUnmappingPacketSize =
packetHeader->BytesTotal;
}
// Adjust the stream base position
if(NotifyPhysical)
{
NTSTATUS ntStatus2 = NotifyPhysical->GetPosition(&m_irpStreamPosition);
if( NT_SUCCESS(ntStatus2) )
{
m_irpStreamPosition.ullStreamOffset = m_irpStreamPosition.ullStreamPosition -
m_irpStreamPosition.ullUnmappingPosition;
}
}
ReleaseMappingIrp(irp,NULL);
KeReleaseSpinLock(&m_RevokeLock, oldIrql);
// kick the notify sink
if(Notify)
{
Notify->IrpSubmitted(NULL,TRUE);
}
else
if(NotifyPhysical)
{
NotifyPhysical->IrpSubmitted(NULL,TRUE);
}
ntStatus = STATUS_SUCCESS;
}
else
{
KeReleaseSpinLock(&m_RevokeLock, oldIrql);
ntStatus = STATUS_UNSUCCESSFUL;
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CIrpStream::RegisterNotifySink()
*****************************************************************************
* Registers a notification sink.
*/
STDMETHODIMP_(void)
CIrpStream::
RegisterNotifySink
(
IN PIRPSTREAMNOTIFY NotificationSink OPTIONAL
)
{
PAGED_CODE();
if(Notify)
{
Notify->Release();
}
Notify = NotificationSink;
if(Notify)
{
Notify->AddRef();
}
}
#pragma code_seg()
/*****************************************************************************
* CIrpStream::GetLockedRegion()
*****************************************************************************
* Get a locked contiguous region of the IRP stream. This region must be
* released using ReleaseLockedRegion() within a few microseconds.
*/
STDMETHODIMP_(void)
CIrpStream::
GetLockedRegion
(
OUT PULONG ByteCount,
OUT PVOID * SystemAddress
)
{
ASSERT(ByteCount);
ASSERT(SystemAddress);
BOOL Done;
PIRP irp;
PPACKET_HEADER packetHeader;
Done = FALSE;
//
// Find an IRP that has requires some work...
//
do
{
irp = DbgAcquireMappingIrp("GetLockedRegion",TRUE);
Done = TRUE;
if(irp)
{
packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
//
// If packetHeader->BytesTotal is 0, then this packet is completed.
//
if(! packetHeader->BytesTotal)
{
packetHeader->OutputPosition = OutputPosition;
ReleaseMappingIrp(irp,packetHeader);
irp = NULL;
Done = FALSE;
}
}
}
while(!Done);
if(irp)
{
ASSERT(! LockedIrp);
LockedIrp = irp;
//
// Record new mapping packet information in the position structure.
//
if(packetHeader->MapPosition == 0)
{
packetHeader->OutputPosition = OutputPosition;
m_irpStreamPosition.ulMappingOffset = 0;
m_irpStreamPosition.ulMappingPacketSize =
packetHeader->BytesTotal;
m_irpStreamPosition.bMappingPacketLooped =
( ( packetHeader->StreamHeader->OptionsFlags
& KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA
)
!= 0
);
}
*ByteCount = packetHeader->BytesTotal - packetHeader->MapPosition;
if(*ByteCount)
{
*SystemAddress = PVOID(
#ifdef UNDER_NT
PBYTE(MmGetSystemAddressForMdlSafe(packetHeader->MdlAddress,HighPagePriority))
#else
PBYTE(MmGetSystemAddressForMdl(packetHeader->MdlAddress))
#endif
+ packetHeader->MapPosition );
}
else
{
*SystemAddress = NULL;
LockedIrp = NULL;
ReleaseMappingIrp(irp,NULL);
}
}
else
{
*ByteCount = 0;
*SystemAddress = NULL;
}
}
/*****************************************************************************
* CIrpStream::ReleaseLockedRegion()
*****************************************************************************
* Releases the region previously obtained with GetLockedRegion().
*/
STDMETHODIMP_(void)
CIrpStream::
ReleaseLockedRegion
(
IN ULONG ByteCount
)
{
if(LockedIrp)
{
PIRP irp = LockedIrp;
LockedIrp = NULL;
PPACKET_HEADER packetHeader =
IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
ULONG bytes = packetHeader->BytesTotal - packetHeader->MapPosition;
if(bytes > ByteCount)
{
bytes = ByteCount;
}
packetHeader->MapPosition += bytes;
m_irpStreamPosition.ullMappingPosition += bytes;
m_irpStreamPosition.ulMappingOffset += bytes;
if(packetHeader->MapPosition == packetHeader->BytesTotal)
{
OutputPosition += packetHeader->BytesTotal;
}
else
{
// ReleaseMappingIrp() wants only completed headers.
packetHeader = NULL;
}
ReleaseMappingIrp(irp,packetHeader);
}
}
/*****************************************************************************
* CIrpStream::Copy()
*****************************************************************************
* Copy to or from locked-down memory.
*/
STDMETHODIMP_(void)
CIrpStream::
Copy
(
IN BOOLEAN WriteOperation,
IN ULONG RequestedSize,
OUT PULONG ActualSize,
IN OUT PVOID Buffer
)
{
ASSERT(ActualSize);
ASSERT(Buffer);
PBYTE buffer = PBYTE(Buffer);
ULONG remaining = RequestedSize;
ULONG loopMax = 10000;
while(remaining)
{
ASSERT(loopMax--);
ULONG byteCount;
PVOID systemAddress;
GetLockedRegion( &byteCount,
&systemAddress );
if(! byteCount)
{
break;
}
if(byteCount > remaining)
{
byteCount = remaining;
}
if(WriteOperation)
{
RtlCopyMemory(PVOID(buffer),systemAddress,byteCount);
}
else
{
RtlCopyMemory(systemAddress,PVOID(buffer),byteCount);
}
ReleaseLockedRegion(byteCount);
buffer += byteCount;
remaining -= byteCount;
}
*ActualSize = RequestedSize - remaining;
}
/*****************************************************************************
* CIrpStream::GetIrpStreamPositionLock()
*****************************************************************************
* So we protect access to m_IrpStreamPosition
*/
STDMETHODIMP_(PKSPIN_LOCK)
CIrpStream::GetIrpStreamPositionLock()
{
return &m_irpStreamPositionLock;
}
/*****************************************************************************
* CIrpStream::Complete()
*****************************************************************************
* Complete.
*/
STDMETHODIMP_(void)
CIrpStream::
Complete
(
IN ULONG RequestedSize,
OUT PULONG ActualSize
)
{
ASSERT(ActualSize);
if(RequestedSize == 0)
{
*ActualSize = 0;
return;
}
ULONG remaining = RequestedSize;
PIRP irp;
ULONG loopMax = 10000;
while(irp = DbgAcquireUnmappingIrp("Complete"))
{
ASSERT(loopMax--);
PPACKET_HEADER packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->UnmappingPacket;
ULONG unmapped;
//
// Record new unmapping packet information in the position structure.
//
if(packetHeader->UnmapPosition == 0)
{
m_irpStreamPosition.ulUnmappingOffset = 0;
m_irpStreamPosition.ulUnmappingPacketSize = packetHeader->BytesTotal;
m_irpStreamPosition.bUnmappingPacketLooped = ((packetHeader->StreamHeader->OptionsFlags &
KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA) != 0 );
}
if(packetHeader->MapCount == 1)
{
unmapped = packetHeader->MapPosition - packetHeader->UnmapPosition;
}
else
{
unmapped = packetHeader->BytesTotal - packetHeader->UnmapPosition;
}
if(unmapped > remaining)
{
unmapped = remaining;
}
remaining -= unmapped;
if(unmapped == 0)
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete unmapping zero-length segment"));
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete packetHeader->MapCount = %d",packetHeader->MapCount));
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete packetHeader->UnmapPosition = %d",packetHeader->UnmapPosition));
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete packetHeader->MapPosition = %d",packetHeader->MapPosition));
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete packetHeader->BytesTotal = %d",packetHeader->BytesTotal));
_DbgPrintF(DEBUGLVL_VERBOSE,("CIrpStream::Complete remaining = %d",remaining));
}
if(JustInTime)
{
// TODO: Unlock the bytes.
}
packetHeader->UnmapPosition += unmapped;
m_irpStreamPosition.ullUnmappingPosition += unmapped;
m_irpStreamPosition.ulUnmappingOffset += unmapped;
if(packetHeader->UnmapPosition != packetHeader->BytesTotal)
{
// ReleaseUnmappingIrp() wants only completed headers.
packetHeader = NULL;
}
ReleaseUnmappingIrp(irp,packetHeader);
//
// If all IRP processing is completed (e.g., the packet header
// has data, but the processing loop has completed the requested
// length) then break from this loop.
//
if(!remaining && unmapped)
{
break;
}
//
// If we have unmapped everything that was mapped in the packet but
// not all of the packet bytes, kick out of the loop
//
if( !unmapped && !packetHeader )
{
break;
}
}
*ActualSize = RequestedSize - remaining;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CIrpStream::RegisterPhysicalNotifySink()
*****************************************************************************
* Registers a notification sink.
*/
STDMETHODIMP_(void)
CIrpStream::
RegisterPhysicalNotifySink
(
IN PIRPSTREAMNOTIFYPHYSICAL NotificationSink OPTIONAL
)
{
PAGED_CODE();
if(NotifyPhysical)
{
NotifyPhysical->Release();
}
NotifyPhysical = NotificationSink;
if(NotifyPhysical)
{
NotifyPhysical->AddRef();
}
}
#pragma code_seg()
/*****************************************************************************
* CIrpStream::GetMapping()
*****************************************************************************
* Gets a mapping.
*/
STDMETHODIMP_(void)
CIrpStream::
GetMapping
(
IN PVOID Tag,
OUT PPHYSICAL_ADDRESS PhysicalAddress,
OUT PVOID * VirtualAddress,
OUT PULONG ByteCount,
OUT PULONG Flags
)
{
ASSERT(PhysicalAddress);
ASSERT(VirtualAddress);
ASSERT(ByteCount);
ASSERT(Flags);
KIRQL OldIrql;
//Acquire the revoke spinlock
KeAcquireSpinLock(&m_RevokeLock, &OldIrql);
PMAPPING_QUEUE_ENTRY entry = GetQueuedMapping();
// skip over any revoked mappings
while( (NULL != entry) && (entry->MappingStatus == MAPPING_STATUS_REVOKED) )
{
entry = GetQueuedMapping();
}
if(! entry)
{
PIRP irp = DbgAcquireMappingIrp("GetMapping",TRUE);
if(irp)
{
PPACKET_HEADER packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->MappingPacket;
// update mapping packet info
m_irpStreamPosition.ulMappingPacketSize = packetHeader->BytesTotal;
m_irpStreamPosition.bMappingPacketLooped = ( ( packetHeader->StreamHeader->OptionsFlags &
KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA ) != 0 );
m_irpStreamPosition.ulMappingOffset = packetHeader->MapPosition;
//
// Deal with one-shot buffer.
//
if( packetHeader->MapPosition &&
( packetHeader->MapPosition == packetHeader->BytesTotal ) )
{
ReleaseMappingIrp(irp,NULL);
}
else
{
// grab the global DMA lock that serializes IoAllocateAdapter calls (we're already at DISPATCH_LEVEL)
KeAcquireSpinLockAtDpcLevel( PDEVICE_CONTEXT(FunctionalDeviceObject->DeviceExtension)->DriverDmaLock );
ULONG BytesToMap = packetHeader->BytesTotal - packetHeader->MapPosition;
ULONG BytesThisMapping = BytesToMap > (PAGE_SIZE * MAX_MAPPINGS) ?
(PAGE_SIZE * MAX_MAPPINGS) :
BytesToMap;
_DbgPrintF(DEBUGLVL_VERBOSE,("GetMapping mapping a new packet (0x%08x)",BytesThisMapping));
packetHeader->OutputPosition = OutputPosition;
ULONG mapRegisterCount = ( BytesThisMapping ?
ADDRESS_AND_SIZE_TO_SPAN_PAGES( PUCHAR(MmGetMdlVirtualAddress( packetHeader->MdlAddress )) +
packetHeader->MapPosition,
BytesThisMapping ) :
0 );
if(mapRegisterCount != 0)
{
CALLBACK_CONTEXT callbackContext;
callbackContext.IrpStream = this;
callbackContext.PacketHeader = packetHeader;
callbackContext.Irp = irp;
KeInitializeEvent(&callbackContext.Event,NotificationEvent,FALSE);
callbackContext.BytesThisMapping = BytesThisMapping;
callbackContext.LastSubPacket = (BytesThisMapping == BytesToMap);
// note - we're already at DISPATCH_LEVEL (we're holding spinlocks)
NTSTATUS ntStatus = IoAllocateAdapterChannel( BusMasterAdapterObject,
FunctionalDeviceObject,
mapRegisterCount,
CallbackFromIoAllocateAdapterChannel,
PVOID(&callbackContext) );
if(NT_SUCCESS(ntStatus))
{
NTSTATUS WaitStatus;
LARGE_INTEGER ZeroTimeout = RtlConvertLongToLargeInteger(0);
ULONG RetryCount = 0;
while( RetryCount++ < 10000 )
{
// Wait for the scatter/gather processing to be completed.
WaitStatus = KeWaitForSingleObject( &callbackContext.Event,
Suspended,
KernelMode,
FALSE,
&ZeroTimeout );
if( WaitStatus == STATUS_SUCCESS )
{
entry = GetQueuedMapping();
break;
}
}
} else
{
ReleaseMappingIrp( irp, NULL );
KeReleaseSpinLockFromDpcLevel( PDEVICE_CONTEXT(FunctionalDeviceObject->DeviceExtension)->DriverDmaLock );
_DbgPrintF(DEBUGLVL_TERSE,("IoAllocateAdapterChannel FAILED (0x%08x)",ntStatus));
}
} else
{
ReleaseMappingIrp( irp,NULL );
KeReleaseSpinLockFromDpcLevel( PDEVICE_CONTEXT(FunctionalDeviceObject->DeviceExtension)->DriverDmaLock );
}
}
} else
{
_DbgPrintF(DEBUGLVL_VERBOSE,("GetMapping() unable to get an IRP"));
}
}
if(entry)
{
// it had better be mapped...
ASSERT( entry->MappingStatus == MAPPING_STATUS_MAPPED );
entry->Tag = Tag;
entry->MappingStatus = MAPPING_STATUS_DELIVERED;
*PhysicalAddress = entry->PhysicalAddress;
*VirtualAddress = entry->VirtualAddress;
*ByteCount = entry->ByteCount;
*Flags = (entry->Flags & (MAPPING_FLAG_END_OF_PACKET | MAPPING_FLAG_END_OF_SUBPACKET)) ?
MAPPING_FLAG_END_OF_PACKET : 0;
m_irpStreamPosition.ullMappingPosition += entry->ByteCount;
m_irpStreamPosition.ulMappingOffset += entry->ByteCount;
#if (DBG)
MappingsOutstanding++;
#endif
}
else
{
WasExhausted = TRUE;
*ByteCount = 0;
}
KeReleaseSpinLock(&m_RevokeLock, OldIrql);
}
/*****************************************************************************
* CIrpStream::ReleaseMapping()
*****************************************************************************
* Releases a mapping obtained through GetMapping().
*/
STDMETHODIMP_(void)
CIrpStream::
ReleaseMapping
(
IN PVOID Tag
)
{
KIRQL OldIrql;
//Acquire the revoke spinlock
KeAcquireSpinLock(&m_RevokeLock, &OldIrql);
PMAPPING_QUEUE_ENTRY entry = DequeueMapping();
while( (NULL != entry) && (entry->MappingStatus != MAPPING_STATUS_DELIVERED) )
{
entry->MappingStatus = MAPPING_STATUS_EMPTY;
entry->Tag = PVOID(-1);
entry = DequeueMapping();
}
// check if we found and entry
if( !entry )
{
KeReleaseSpinLock(&m_RevokeLock, OldIrql);
_DbgPrintF(DEBUGLVL_VERBOSE,("ReleaseMapping failed to find a mapping to release"));
return;
}
//
// Due to race conditions between portcls and the WDM driver, the driver
// might first release the second mapping and then the first mapping in
// the row.
// By design, it doesn't make sense for a audio driver to play "in the
// middle" of a stream and release mappings there. The only exception
// where mappings might not be released in order how the driver got them
// is mentioned above.
// Since we know that, we don't need to search for the right mapping!
//
// mark the entry as empty
entry->MappingStatus = MAPPING_STATUS_EMPTY;
entry->Tag = PVOID(-1);
#if (DBG)
MappingsOutstanding--;
#endif
// get the unmapping irp
PIRP irp = DbgAcquireUnmappingIrp("ReleaseMapping");
if( irp )
{
PPACKET_HEADER packetHeader = IRP_CONTEXT_IRP_STORAGE(irp)->UnmappingPacket;
// update position info
packetHeader->UnmapPosition += entry->ByteCount;
m_irpStreamPosition.ulUnmappingPacketSize = packetHeader->BytesTotal;
m_irpStreamPosition.ulUnmappingOffset = packetHeader->UnmapPosition;
m_irpStreamPosition.ullUnmappingPosition += entry->ByteCount;
m_irpStreamPosition.bUnmappingPacketLooped = ( ( packetHeader->StreamHeader->OptionsFlags &
KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA ) != 0 );
// check if this is the last mapping in the packet or subpacket
if( ( entry->Flags & MAPPING_FLAG_END_OF_PACKET ) ||
( entry->Flags & MAPPING_FLAG_END_OF_SUBPACKET) )
{
// Flush the DMA adapter buffers.
IoFlushAdapterBuffers( BusMasterAdapterObject,
packetHeader->MdlAddress,
entry->MapRegisterBase,
entry->SubpacketVa,
entry->SubpacketBytes,
WriteOperation );
IoFreeMapRegisters( BusMasterAdapterObject,
entry->MapRegisterBase,
ADDRESS_AND_SIZE_TO_SPAN_PAGES(entry->SubpacketVa,entry->SubpacketBytes) );
}
// release the unmapping irp and only pass the packet header if the packet is completed
ReleaseUnmappingIrp(irp, (entry->Flags & MAPPING_FLAG_END_OF_PACKET) ? packetHeader : NULL);
}
KeReleaseSpinLock(&m_RevokeLock, OldIrql);
}
/*****************************************************************************
* CallbackFromIoAllocateAdapterChannel()
*****************************************************************************
* Callback from IoAllocateAdapterChannel to create scatter/gather entries.
*/
static
IO_ALLOCATION_ACTION
CallbackFromIoAllocateAdapterChannel
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Reserved,
IN PVOID MapRegisterBase,
IN PVOID VoidContext
)
{
ASSERT(DeviceObject);
ASSERT(VoidContext);
PCALLBACK_CONTEXT context = PCALLBACK_CONTEXT(VoidContext);
PIRP Irp = context->Irp;
PUCHAR virtualAddress = PUCHAR(MmGetMdlVirtualAddress(context->PacketHeader->MdlAddress));
#ifdef UNDER_NT
PUCHAR entryVA = PUCHAR(MmGetSystemAddressForMdlSafe(context->PacketHeader->MdlAddress,HighPagePriority));
#else
PUCHAR entryVA = PUCHAR(MmGetSystemAddressForMdl(context->PacketHeader->MdlAddress));
#endif
ULONG bytesRemaining = context->BytesThisMapping;
ULONG flags = context->LastSubPacket ? MAPPING_FLAG_END_OF_PACKET : MAPPING_FLAG_END_OF_SUBPACKET;
//
// Consider mapping offset in case we have set position.
//
virtualAddress += context->PacketHeader->MapPosition;
entryVA += context->PacketHeader->MapPosition;
PVOID subpacketVa = virtualAddress;
while(bytesRemaining)
{
ULONG segmentLength = bytesRemaining;
// Create one mapping.
PHYSICAL_ADDRESS physicalAddress = IoMapTransfer( context->IrpStream->BusMasterAdapterObject,
context->PacketHeader->MdlAddress,
MapRegisterBase,
virtualAddress,
&segmentLength,
context->IrpStream->WriteOperation );
bytesRemaining -= segmentLength;
virtualAddress += segmentLength;
// enqueue the mapping
while(segmentLength)
{
NTSTATUS ntStatus;
// TODO: break up large mappings based on hardware constraints
ntStatus = context->IrpStream->EnqueueMapping( physicalAddress,
Irp,
context->PacketHeader,
PVOID(entryVA),
segmentLength,
((bytesRemaining == 0) ? flags : 0),
MapRegisterBase,
MAPPING_STATUS_MAPPED,
((bytesRemaining == 0) ? subpacketVa : NULL),
((bytesRemaining == 0) ? context->BytesThisMapping : 0 ) );
if( NT_SUCCESS(ntStatus) )
{
entryVA += segmentLength;
physicalAddress.LowPart += segmentLength;
segmentLength = 0;
}
else
{
// TODO: deal properly with a full mapping queue
ASSERT(!"MappingQueue FULL");
}
}
}
context->PacketHeader->MapPosition += context->BytesThisMapping;
context->IrpStream->OutputPosition += context->BytesThisMapping;
context->IrpStream->ReleaseMappingIrp(context->Irp,
((context->PacketHeader->MapPosition == context->PacketHeader->BytesTotal) ? context->PacketHeader : NULL));
KeSetEvent(&context->Event,0,FALSE);
KeReleaseSpinLock( PDEVICE_CONTEXT(context->IrpStream->FunctionalDeviceObject->DeviceExtension)->DriverDmaLock, KeGetCurrentIrql() );
return DeallocateObjectKeepRegisters;
}
/*****************************************************************************
* CIrpStream::AcquireMappingIrp()
*****************************************************************************
* Acquire the IRP in which mapping is currently occuring.
*/
PIRP
CIrpStream::
AcquireMappingIrp
(
#if DBG
IN PCHAR Owner,
#endif
IN BOOLEAN NotifyExhausted
)
{
KIRQL kIrqlOld;
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
m_kIrqlOld = kIrqlOld;
PIRP irp = KsRemoveIrpFromCancelableQueue( &LockedQueue,
&LockedQueueLock,
KsListEntryHead,
KsAcquireOnlySingleItem );
if(! irp)
{
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
}
#if DBG
if(irp)
{
_DbgPrintF(DEBUGLVL_BLAB,("AcquireMappingIrp() %d 0x%8x",IRP_CONTEXT_IRP_STORAGE(irp)->IrpLabel,irp));
MappingIrpOwner = Owner;
}
else
{
_DbgPrintF(DEBUGLVL_BLAB,("AcquireMappingIrp() NO MAPPING IRP AVAILABLE"));
}
DbgQueues();
#endif
return irp;
}
/*****************************************************************************
* CIrpStream::AcquireUnmappingIrp()
*****************************************************************************
* Acquire the IRP in which unmapping is currently occuring.
*/
PIRP
CIrpStream::
AcquireUnmappingIrp
(
#if DBG
IN PCHAR Owner
#endif
)
{
KIRQL kIrqlOld;
KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld);
m_kIrqlOld = kIrqlOld;
// The IRP that we should be unmapping is at the head of the mapped queue
// if it is completely mapped. Otherwise it's at the head of the locked
// queue, and the mapped queue is empty.
// Acquire the head IRP in the locked queue just in case.
PIRP lockedIrp = KsRemoveIrpFromCancelableQueue( &LockedQueue,
&LockedQueueLock,
KsListEntryHead,
KsAcquireOnlySingleItem );
// Acquire the head IRP in the mapped queue.
PIRP irp = KsRemoveIrpFromCancelableQueue( &MappedQueue,
&MappedQueueLock,
KsListEntryHead,
KsAcquireOnlySingleItem );
if(irp)
{
// Don't need the IRP from the locked queue.
if(lockedIrp)
{
KsReleaseIrpOnCancelableQueue( lockedIrp,
IrpStreamCancelRoutine );
}
}
else
if(IsListEmpty(&MappedQueue))
{
// Mapped queue is empty, try locked queue.
if(lockedIrp)
{
irp = lockedIrp;
}
}
else
{
// There's a busy IRP in the mapped queue.
if(lockedIrp)
{
KsReleaseIrpOnCancelableQueue( lockedIrp,
IrpStreamCancelRoutine );
}
}
if(! irp)
{
KeReleaseSpinLock(&m_kSpinLock,kIrqlOld);
}
#if DBG
if(irp)
{
_DbgPrintF(DEBUGLVL_BLAB,("AcquireUnmappingIrp() %d 0x%8x",IRP_CONTEXT_IRP_STORAGE(irp)->IrpLabel,irp));
UnmappingIrpOwner = Owner;
}
else
{
_DbgPrintF(DEBUGLVL_BLAB,("AcquireUnmappingIrp() NO UNMAPPING IRP AVAILABLE"));
}
DbgQueues();
#endif
return irp;
}
/*****************************************************************************
* CIrpStream::ReleaseMappingIrp()
*****************************************************************************
* Releases the mapping IRP previously acquired through AcqureMappingIrp(),
* possibly handling the completion of a packet.
*/
void
CIrpStream::
ReleaseMappingIrp
(
IN PIRP pIrp,
IN PPACKET_HEADER pPacketHeader OPTIONAL
)
{
ASSERT(pIrp);
if(pPacketHeader)
{
if(pPacketHeader->IncrementMapping)
{
pPacketHeader->IncrementMapping = FALSE;
pPacketHeader++;
}
else
{
PPACKET_HEADER prevPacketHeader = pPacketHeader;
pPacketHeader = pPacketHeader->Next;
//
// If looping back, stop if there is another IRP.
//
if( pPacketHeader &&
(pPacketHeader <= prevPacketHeader) &&
(FLINK_IRP_STORAGE(pIrp) != &LockedQueue) )
{
pPacketHeader = NULL;
}
}
if(pPacketHeader)
{
// Use next packet header next time.
IRP_CONTEXT_IRP_STORAGE(pIrp)->MappingPacket = pPacketHeader;
pPacketHeader->MapCount++;
pPacketHeader->MapPosition = 0;
m_irpStreamPosition.ulMappingOffset = 0;
m_irpStreamPosition.ulMappingPacketSize = pPacketHeader->BytesTotal;
m_irpStreamPosition.bMappingPacketLooped = ( ( pPacketHeader->StreamHeader->OptionsFlags &
KSSTREAM_HEADER_OPTIONSF_LOOPEDDATA ) != 0 );
KsReleaseIrpOnCancelableQueue( pIrp,
IrpStreamCancelRoutine );
}
else if( m_irpStreamPosition.bLoopedInterface && (FLINK_IRP_STORAGE(pIrp) == &LockedQueue) )
{
//
// Completed one-shot with looped interface and there are no more
// packets. Just hang out here.
//
KsReleaseIrpOnCancelableQueue( pIrp,
IrpStreamCancelRoutine );
}
else
{
//
// IRP is completely mapped.
//
//
// See if we need to initiate unmapping.
//
BOOL bKickUnmapping = FALSE;
if(IsListEmpty(&MappedQueue))
{
pPacketHeader = IRP_CONTEXT_IRP_STORAGE(pIrp)->UnmappingPacket;
bKickUnmapping = ( pPacketHeader->UnmapPosition == pPacketHeader->BytesTotal );
}
//
// Add the IRP to the mapped queued.
//
KsRemoveSpecificIrpFromCancelableQueue(pIrp);
KsAddIrpToCancelableQueue( &MappedQueue,
&MappedQueueLock,
pIrp,
KsListEntryTail,
IrpStreamCancelRoutine );
if(bKickUnmapping)
{
//
// Unmap the completed header.
//
PIRP pIrpRemoved = KsRemoveIrpFromCancelableQueue( &MappedQueue,
&MappedQueueLock,
KsListEntryHead,
KsAcquireOnlySingleItem );
ASSERT(pIrpRemoved == pIrp);
ReleaseUnmappingIrp( pIrp, IRP_CONTEXT_IRP_STORAGE(pIrp)->UnmappingPacket );
return; // ReleaseUnmappingIrp() releases the spinlock.
}
}
}
else
{
KsReleaseIrpOnCancelableQueue( pIrp,
IrpStreamCancelRoutine );
}
KeReleaseSpinLock(&m_kSpinLock,m_kIrqlOld);
}
/*****************************************************************************
* CIrpStream::ReleaseUnmappingIrp()
*****************************************************************************
* Releases the unmapping IRP acquired through AcquireUnmappingIrp(),
* possibly handling the completion of a packet.
*/
void
CIrpStream::
ReleaseUnmappingIrp
(
IN PIRP pIrp,
IN PPACKET_HEADER pPacketHeader OPTIONAL
)
{
ASSERT(pIrp);
//
// Loop until there are no more packets completely unmapped.
//
while(1)
{
//
// If we don't have a newly unmapped packet, just release.
//
if(! pPacketHeader)
{
KsReleaseIrpOnCancelableQueue( pIrp,
IrpStreamCancelRoutine );
break;
}
//
// Loop 'til we find the next packet in the IRP if there is one.
//
while(1)
{
//
// Copy back total byte count into data used for capture.
// It's a no-op for render.
//
pPacketHeader->StreamHeader->DataUsed = pPacketHeader->BytesTotal;
pPacketHeader->MapCount--;
if(pPacketHeader->IncrementUnmapping)
{
pPacketHeader->IncrementUnmapping = FALSE;
pPacketHeader++;
}
else
{
pPacketHeader = pPacketHeader->Next;
if(! pPacketHeader)
{
break;
}
else
if(pPacketHeader->MapCount == 0)
{
pPacketHeader = NULL;
break;
}
}
//
// Loop only if this is a zero-length packet.
//
if(pPacketHeader->BytesTotal)
{
break;
}
}
if(pPacketHeader)
{
//
// Use next packet header next time.
//
IRP_CONTEXT_IRP_STORAGE(pIrp)->UnmappingPacket = pPacketHeader;
pPacketHeader->UnmapPosition = 0;
pPacketHeader = NULL;
}
else
{
//
// Remove the IRP from the queue.
//
KsRemoveSpecificIrpFromCancelableQueue(pIrp);
//
// Done with IRP...free the context memory we allocated
//
if( IRP_CONTEXT_IRP_STORAGE(pIrp) )
{
ExFreePool( IRP_CONTEXT_IRP_STORAGE(pIrp) );
IRP_CONTEXT_IRP_STORAGE(pIrp) = NULL;
} else
{
ASSERT( !"Freeing IRP with no context");
}
//
// Indicate in the IRP how much data we have captured.
//
if(! WriteOperation)
{
pIrp->IoStatus.Information = IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.OutputBufferLength;
}
//
// Mark it happy.
//
pIrp->IoStatus.Status = STATUS_SUCCESS;
//
// Pass it to the next transport sink.
//
ASSERT(m_TransportSink);
KsShellTransferKsIrp(m_TransportSink,pIrp);
//
// Acquire the head IRP in the mapped queue.
//
pIrp = KsRemoveIrpFromCancelableQueue( &MappedQueue,
&MappedQueueLock,
KsListEntryHead,
KsAcquireOnlySingleItem );
//
// No IRP. Outta here.
//
if(! pIrp)
{
break;
}
//
// See if we need to complete this packet.
//
pPacketHeader = IRP_CONTEXT_IRP_STORAGE(pIrp)->UnmappingPacket;
if(pPacketHeader->UnmapPosition != pPacketHeader->BytesTotal)
{
pPacketHeader = NULL;
}
}
}
KeReleaseSpinLock(&m_kSpinLock,m_kIrqlOld);
}
/*****************************************************************************
* CIrpStream::EnqueueMapping()
*****************************************************************************
* Add a mapping to the mapping queue.
*/
NTSTATUS
CIrpStream::
EnqueueMapping
(
IN PHYSICAL_ADDRESS PhysicalAddress,
IN PIRP Irp,
IN PPACKET_HEADER PacketHeader,
IN PVOID VirtualAddress,
IN ULONG ByteCount,
IN ULONG Flags,
IN PVOID MapRegisterBase,
IN ULONG MappingStatus,
IN PVOID SubpacketVa,
IN ULONG SubpacketBytes
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
if( (MappingQueue.Tail + 1 == MappingQueue.Head) ||
( (MappingQueue.Tail + 1 == MAPPING_QUEUE_SIZE) &&
(MappingQueue.Head == 0) ) )
{
// mapping queue looks full. check to see if we can move the head to make
// room.
if( (MappingQueue.Array[MappingQueue.Head].MappingStatus != MAPPING_STATUS_MAPPED) &&
(MappingQueue.Array[MappingQueue.Head].MappingStatus != MAPPING_STATUS_DELIVERED) )
{
PMAPPING_QUEUE_ENTRY entry = DequeueMapping();
ASSERT(entry);
if (entry)
{
entry->MappingStatus = MAPPING_STATUS_EMPTY;
}
else
{
ntStatus = STATUS_UNSUCCESSFUL;
}
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("EnqueueMapping MappingQueue FULL! (0x%08x)",this));
ntStatus = STATUS_UNSUCCESSFUL;
}
}
if (NT_SUCCESS(ntStatus))
{
MappingQueue.Array[MappingQueue.Tail].PhysicalAddress = PhysicalAddress;
MappingQueue.Array[MappingQueue.Tail].Irp = Irp;
MappingQueue.Array[MappingQueue.Tail].PacketHeader = PacketHeader;
MappingQueue.Array[MappingQueue.Tail].VirtualAddress = VirtualAddress;
MappingQueue.Array[MappingQueue.Tail].ByteCount = ByteCount;
MappingQueue.Array[MappingQueue.Tail].Flags = Flags;
MappingQueue.Array[MappingQueue.Tail].MapRegisterBase = MapRegisterBase;
MappingQueue.Array[MappingQueue.Tail].MappingStatus = MappingStatus;
MappingQueue.Array[MappingQueue.Tail].SubpacketVa = SubpacketVa;
MappingQueue.Array[MappingQueue.Tail].SubpacketBytes = SubpacketBytes;
#if (DBG)
MappingsQueued++;
#endif
if(++MappingQueue.Tail == MAPPING_QUEUE_SIZE)
{
MappingQueue.Tail = 0;
}
}
return ntStatus;
}
/*****************************************************************************
* CIrpStream::GetQueuedMapping()
*****************************************************************************
* Get a queued mapping from the mapping queue.
*/
PMAPPING_QUEUE_ENTRY
CIrpStream::
GetQueuedMapping
( void
)
{
PMAPPING_QUEUE_ENTRY result;
if(MappingQueue.Get == MappingQueue.Tail)
{
result = NULL;
}
else
{
result = &MappingQueue.Array[MappingQueue.Get];
if(++MappingQueue.Get == MAPPING_QUEUE_SIZE)
{
MappingQueue.Get = 0;
}
}
return result;
}
/*****************************************************************************
* CIrpStream::DequeueMapping()
*****************************************************************************
* Remove a mapping from the mapping queue.
*/
PMAPPING_QUEUE_ENTRY
CIrpStream::
DequeueMapping
( void
)
{
PMAPPING_QUEUE_ENTRY result;
if(MappingQueue.Head == MappingQueue.Tail)
{
result = NULL;
}
else
{
result = &MappingQueue.Array[MappingQueue.Head];
#if (DBG)
MappingsQueued--;
#endif
if(++MappingQueue.Head == MAPPING_QUEUE_SIZE)
{
MappingQueue.Head = 0;
}
}
return result;
}
/*****************************************************************************
* IrpStreamCancelRoutine()
*****************************************************************************
* Do cancellation.
*/
VOID
IrpStreamCancelRoutine
(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
ASSERT(DeviceObject);
ASSERT(Irp);
_DbgPrintF(DEBUGLVL_VERBOSE,("CancelRoutine Cancelling IRP: 0x%08x",Irp));
//
// Mark the IRP cancelled and call the standard routine. Doing the
// marking first has the effect of not completing the IRP in the standard
// routine. The standard routine removes the IRP from the queue and
// releases the cancel spin lock.
//
Irp->IoStatus.Status = STATUS_CANCELLED;
KsCancelRoutine(DeviceObject,Irp);
// TODO: Search the mapping queue for mappings to revoke.
// TODO: Free associated map registers.
if (IRP_CONTEXT_IRP_STORAGE(Irp))
{
// get the IrpStream context
CIrpStream *that = (CIrpStream *)(PIRP_CONTEXT(IRP_CONTEXT_IRP_STORAGE(Irp))->IrpStream);
//
// if we get here from CancelAllIrps we are assured that the spinlocks
// are held properly. if we get here from an arbitrary irp cancellation we won't
// have either the revoke or the mapping spinlock held. In that case we need to
// grab both locks here and release them after the CancelMappings call.
//
if ( that->m_CancelAllIrpsThread == KeGetCurrentThread()) {
that->CancelMappings(Irp);
} else {
//
// If we get here from CancelAllIrps we are assured that the spinlocks
// are held properly. However, if we get here from an arbitrary irp
// cancellation, we won't hold either the revoke or the mapping spinlock.
// In that case, we need to grab both locks around CancelMappings().
//
KIRQL kIrqlOldRevoke;
// must always grab revoke lock BEFORE master lock
KeAcquireSpinLock(&that->m_RevokeLock, &kIrqlOldRevoke);
KeAcquireSpinLockAtDpcLevel(&that->m_kSpinLock);
that->CancelMappings(Irp);
// release the spinlocks, master first
KeReleaseSpinLockFromDpcLevel(&that->m_kSpinLock);
KeReleaseSpinLock(&that->m_RevokeLock, kIrqlOldRevoke);
}
// Free the context memory we allocated
ExFreePool(IRP_CONTEXT_IRP_STORAGE(Irp));
IRP_CONTEXT_IRP_STORAGE(Irp) = NULL;
}
else
{
ASSERT( !"Freeing IRP with no context");
}
IoCompleteRequest(Irp,IO_NO_INCREMENT);
}
/*****************************************************************************
* CIrpStream::CancelMappings()
*****************************************************************************
* Cancel mappings for an IRP or all IRPs.
*/
void
CIrpStream::
CancelMappings
(
IN PIRP pIrp
)
{
// NOTE: the revoke and master spinlocks must be held before calling this routine
// check only if we have a non-empty mapping queue
if( (MappingQueue.Array) &&
(MappingQueue.Head != MappingQueue.Tail) )
{
ULONG ulPosition = MappingQueue.Head;
ULONG ulFirst = ULONG(-1);
ULONG ulLast = ULONG(-1);
ULONG ulMappingCount = 0;
// walk mapping queue from head to tail
while( ulPosition != MappingQueue.Tail )
{
// get the mapping queue entry
PMAPPING_QUEUE_ENTRY entry = &MappingQueue.Array[ulPosition];
// check if this mapping belongs to the irp(s) being cancelled
if( (NULL == pIrp) || (entry->Irp == pIrp) )
{
// check if the mapping has been delivered
if( entry->MappingStatus == MAPPING_STATUS_DELIVERED )
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CancelMappings %d needs revoking",ulPosition));
// keep track of this for the driver revoke call
if( ulFirst == ULONG(-1) )
{
ulFirst = ulPosition;
}
ulLast = ulPosition;
ulMappingCount++;
}
// is this the last mapping in a packet (and not previously revoked)?
if( ( ( entry->Flags & MAPPING_FLAG_END_OF_PACKET ) ||
( entry->Flags & MAPPING_FLAG_END_OF_SUBPACKET) ) &&
( entry->MappingStatus != MAPPING_STATUS_REVOKED ) )
{
// do we need to revoke anything in the driver?
if( ulMappingCount )
{
ULONG ulRevoked = ulMappingCount; // init to how many we are asking for
// revoke mappings in the driver
if( NotifyPhysical )
{
_DbgPrintF(DEBUGLVL_VERBOSE,("CancelMappings REVOKING (%d)",ulMappingCount));
NotifyPhysical->MappingsCancelled( MappingQueue.Array[ulFirst].Tag,
MappingQueue.Array[ulLast].Tag,
&ulRevoked );
#if (DBG)
MappingsOutstanding -= ulRevoked;
#endif
}
// check if all were revoked
if( ulRevoked != ulMappingCount )
{
_DbgPrintF(DEBUGLVL_TERSE,("Mappings not fully revoked (%d of %d)",
ulRevoked,
ulMappingCount));
}
// reset the revoke tracking
ulFirst = ULONG(-1);
ulLast = ULONG(-1);
ulMappingCount = 0;
}
// get the packet header
PPACKET_HEADER header = entry->PacketHeader;
// release the mappings in this subpacket
if( ( header ) &&
( entry->SubpacketVa ) &&
( entry->SubpacketBytes ) )
{
// flush and free the mappings and map registers
IoFlushAdapterBuffers( BusMasterAdapterObject,
header->MdlAddress,
entry->MapRegisterBase,
entry->SubpacketVa,
entry->SubpacketBytes,
WriteOperation );
IoFreeMapRegisters( BusMasterAdapterObject,
entry->MapRegisterBase,
ADDRESS_AND_SIZE_TO_SPAN_PAGES( entry->SubpacketVa,
entry->SubpacketBytes ) );
if( entry->Flags & MAPPING_FLAG_END_OF_PACKET )
{
// decrement the map count if this is the end of a packet
header->MapCount--;
}
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("Mapping entry with EOP flag set and NULL packet header"));
}
}
// mark the mapping as revoked
entry->MappingStatus = MAPPING_STATUS_REVOKED;
}
// move on to the next entry
if( ++ulPosition == MAPPING_QUEUE_SIZE )
{
ulPosition = 0;
}
}
}
}
#if (DBG)
/*****************************************************************************
* CIrpStream::DbgQueues()
*****************************************************************************
* Show the queues.
*/
void
CIrpStream::
DbgQueues
( void
)
{
PLIST_ENTRY entry = LockedQueue.Flink;
_DbgPrintF(DEBUGLVL_BLAB,("DbgQueues() LockedQueue"));
while(entry != &LockedQueue)
{
PIRP irp = PIRP(CONTAINING_RECORD(entry,IRP,Tail.Overlay.ListEntry));
_DbgPrintF(DEBUGLVL_BLAB,(" %d 0x%8x",IRP_CONTEXT_IRP_STORAGE(irp)->IrpLabel,irp));
entry = entry->Flink;
}
entry = MappedQueue.Flink;
_DbgPrintF(DEBUGLVL_BLAB,("DbgQueues() MappedQueue"));
while(entry != &MappedQueue)
{
PIRP irp = PIRP(CONTAINING_RECORD(entry,IRP,Tail.Overlay.ListEntry));
_DbgPrintF(DEBUGLVL_BLAB,(" %d 0x%8x",IRP_CONTEXT_IRP_STORAGE(irp)->IrpLabel,irp));
entry = entry->Flink;
}
}
#include "stdio.h"
STDMETHODIMP_(void)
CIrpStream::
DbgRollCall
(
IN ULONG MaxNameSize,
OUT PCHAR Name,
OUT PIKSSHELLTRANSPORT* NextTransport,
OUT PIKSSHELLTRANSPORT* PrevTransport
)
/*++
Routine Description:
This routine produces a component name and the transport pointers.
Arguments:
Return Value:
--*/
{
_DbgPrintF(DEBUGLVL_BLAB,("CIrpStream::DbgRollCall"));
PAGED_CODE();
ASSERT(Name);
ASSERT(NextTransport);
ASSERT(PrevTransport);
ULONG references = AddRef() - 1; Release();
_snprintf(Name,MaxNameSize,"IrpStream%p refs=%d\n",this,references);
*NextTransport = m_TransportSink;
*PrevTransport = m_TransportSource;
}
#endif // DBG
#endif // PC_KDEXT