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
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
|