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.
1373 lines
32 KiB
1373 lines
32 KiB
/*****************************************************************************
|
|
* waveclk.cpp - wave clock implementation
|
|
*****************************************************************************
|
|
* Copyright (c) 1998-2000 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "private.h"
|
|
|
|
/*****************************************************************************
|
|
* IIrpTargetInit
|
|
*****************************************************************************
|
|
* IIrpTargetInit plus CWaveClock's Init.
|
|
*/
|
|
DECLARE_INTERFACE_(IIrpTargetInit,IIrpTarget)
|
|
{
|
|
DEFINE_ABSTRACT_UNKNOWN() // For IUnknown
|
|
|
|
DEFINE_ABSTRACT_IRPTARGETFACTORY() // For IIrpTargetFactory
|
|
|
|
DEFINE_ABSTRACT_IRPTARGET() // For IIrpTarget
|
|
|
|
STDMETHOD_(NTSTATUS,Init)
|
|
( THIS_
|
|
IN PIRPSTREAMCONTROL pIrpStreamControl,
|
|
IN PKSPIN_LOCK pKSpinLock,
|
|
IN PLIST_ENTRY pListEntry
|
|
) PURE;
|
|
};
|
|
|
|
typedef IIrpTargetInit *PIRPTARGETINIT;
|
|
|
|
/*****************************************************************************
|
|
* CWaveClock
|
|
*****************************************************************************
|
|
* Wave clock implementation.
|
|
*/
|
|
class CWaveClock :
|
|
public IIrpTargetInit,
|
|
public IWaveClock,
|
|
public CUnknown
|
|
{
|
|
private:
|
|
WAVECLOCKNODE m_waveClockNode;
|
|
PKSPIN_LOCK m_pKSpinLock;
|
|
PIRPSTREAMCONTROL m_pIrpStreamControl;
|
|
KSPIN_LOCK m_ClockLock,
|
|
m_EventLock;
|
|
LIST_ENTRY m_EventList;
|
|
KMUTEX m_StateMutex;
|
|
LONGLONG m_LastTime,
|
|
m_LastPhysicalTime,
|
|
m_LastPhysicalPosition;
|
|
KSSTATE m_DeviceState;
|
|
|
|
LONGLONG
|
|
m_GetCurrentTime
|
|
( void
|
|
);
|
|
|
|
public:
|
|
DECLARE_STD_UNKNOWN();
|
|
DEFINE_STD_CONSTRUCTOR(CWaveClock);
|
|
~CWaveClock();
|
|
|
|
IMP_IIrpTarget;
|
|
IMP_IWaveClock;
|
|
|
|
STDMETHODIMP_(NTSTATUS) Init
|
|
( IN PIRPSTREAMCONTROL pIrpStreamControl,
|
|
IN PKSPIN_LOCK pKSpinLock,
|
|
IN PLIST_ENTRY pListEntry
|
|
);
|
|
|
|
//
|
|
// helper functions (also the DPC interface)
|
|
//
|
|
|
|
static
|
|
LONGLONG
|
|
FASTCALL
|
|
GetCurrentTime(
|
|
IN PFILE_OBJECT FileObject
|
|
);
|
|
|
|
static
|
|
LONGLONG
|
|
FASTCALL
|
|
GetCurrentPhysicalTime(
|
|
IN PFILE_OBJECT FileObject
|
|
);
|
|
|
|
static
|
|
LONGLONG
|
|
FASTCALL
|
|
GetCurrentCorrelatedTime(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PLONGLONG SystemTime
|
|
);
|
|
|
|
static
|
|
LONGLONG
|
|
FASTCALL
|
|
GetCurrentCorrelatedPhysicalTime(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PLONGLONG SystemTime
|
|
);
|
|
|
|
//
|
|
// property handlers and event handlers
|
|
//
|
|
|
|
static
|
|
NTSTATUS
|
|
AddEvent(
|
|
IN PIRP Irp,
|
|
IN PKSEVENT_TIME_INTERVAL EventTime,
|
|
IN PKSEVENT_ENTRY EventEntry
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetFunctionTable(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCLOCK_FUNCTIONTABLE FunctionTable
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetCorrelatedTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCORRELATED_TIME CorrelatedTime
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PLONGLONG Time
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetCorrelatedPhysicalTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCORRELATED_TIME CorrelatedTime
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetPhysicalTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PLONGLONG Time
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetResolution(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSRESOLUTION Resolution
|
|
);
|
|
|
|
static
|
|
NTSTATUS
|
|
GetState(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSSTATE State
|
|
);
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_CLOCKSET(
|
|
ClockPropertyHandlers,
|
|
CWaveClock::GetTime,
|
|
CWaveClock::GetPhysicalTime,
|
|
CWaveClock::GetCorrelatedTime,
|
|
CWaveClock::GetCorrelatedPhysicalTime,
|
|
CWaveClock::GetResolution,
|
|
CWaveClock::GetState,
|
|
CWaveClock::GetFunctionTable );
|
|
|
|
DEFINE_KSPROPERTY_SET_TABLE( ClockPropertyTable )
|
|
{
|
|
DEFINE_KSPROPERTY_SET(
|
|
&KSPROPSETID_Clock,
|
|
SIZEOF_ARRAY( ClockPropertyHandlers ),
|
|
ClockPropertyHandlers,
|
|
0,
|
|
NULL)
|
|
};
|
|
|
|
DEFINE_KSEVENT_TABLE( ClockEventHandlers )
|
|
{
|
|
DEFINE_KSEVENT_ITEM(
|
|
KSEVENT_CLOCK_INTERVAL_MARK,
|
|
sizeof( KSEVENT_TIME_INTERVAL ),
|
|
sizeof( ULONGLONG ) + sizeof( ULONGLONG ),
|
|
(PFNKSADDEVENT) CWaveClock::AddEvent,
|
|
NULL,
|
|
NULL),
|
|
DEFINE_KSEVENT_ITEM(
|
|
KSEVENT_CLOCK_POSITION_MARK,
|
|
sizeof( KSEVENT_TIME_MARK ),
|
|
sizeof( ULONGLONG ),
|
|
(PFNKSADDEVENT) CWaveClock::AddEvent,
|
|
NULL,
|
|
NULL)
|
|
};
|
|
|
|
DEFINE_KSEVENT_SET_TABLE( ClockEventTable )
|
|
{
|
|
DEFINE_KSEVENT_SET(
|
|
&KSEVENTSETID_Clock,
|
|
SIZEOF_ARRAY( ClockEventHandlers ),
|
|
ClockEventHandlers)
|
|
};
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CreateWaveClock()
|
|
*****************************************************************************
|
|
* Creates a CWaveClock object.
|
|
*/
|
|
NTSTATUS
|
|
CreateWaveClock
|
|
(
|
|
OUT PUNKNOWN * pUnknown,
|
|
IN REFCLSID,
|
|
IN PUNKNOWN pUnknownOuter OPTIONAL,
|
|
IN POOL_TYPE poolType
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pUnknown);
|
|
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Creating WAVECLK"));
|
|
|
|
STD_CREATE_BODY_
|
|
(
|
|
CWaveClock,
|
|
pUnknown,
|
|
pUnknownOuter,
|
|
poolType,
|
|
PIRPTARGET
|
|
);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcNewWaveClock()
|
|
*****************************************************************************
|
|
* Creates a new wave clock.
|
|
*/
|
|
NTSTATUS
|
|
PcNewWaveClock
|
|
( OUT PIRPTARGET * ppIrpTarget,
|
|
IN PUNKNOWN pUnknownOuter,
|
|
IN POOL_TYPE poolType,
|
|
IN PIRPSTREAMCONTROL pIrpStreamControl,
|
|
IN PKSPIN_LOCK pKSpinLock,
|
|
IN PLIST_ENTRY pListEntry
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pIrpStreamControl);
|
|
ASSERT(pKSpinLock);
|
|
ASSERT(pListEntry);
|
|
|
|
PUNKNOWN pUnknown;
|
|
NTSTATUS ntStatus =
|
|
CreateWaveClock
|
|
( &pUnknown,
|
|
GUID_NULL,
|
|
pUnknownOuter,
|
|
poolType
|
|
);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
PIRPTARGETINIT pIrpTargetInit;
|
|
ntStatus =
|
|
pUnknown->QueryInterface
|
|
( IID_IIrpTarget,
|
|
(PVOID *) &pIrpTargetInit
|
|
);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus =
|
|
pIrpTargetInit->Init
|
|
( pIrpStreamControl,
|
|
pKSpinLock,
|
|
pListEntry
|
|
);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
*ppIrpTarget = pIrpTargetInit;
|
|
}
|
|
else
|
|
{
|
|
pIrpTargetInit->Release();
|
|
}
|
|
}
|
|
|
|
pUnknown->Release();
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CWaveClock::Init()
|
|
*****************************************************************************
|
|
* Initializes a wave clock.
|
|
*/
|
|
NTSTATUS
|
|
CWaveClock::
|
|
Init
|
|
( IN PIRPSTREAMCONTROL pIrpStreamControl,
|
|
IN PKSPIN_LOCK pKSpinLock,
|
|
IN PLIST_ENTRY pListEntry
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Initializing WAVECLK (0x%08x)",this));
|
|
|
|
ASSERT(pIrpStreamControl);
|
|
ASSERT(pKSpinLock);
|
|
ASSERT(pListEntry);
|
|
|
|
//
|
|
// Save parameters.
|
|
//
|
|
m_pIrpStreamControl = pIrpStreamControl;
|
|
m_pIrpStreamControl->AddRef();
|
|
|
|
m_pKSpinLock = pKSpinLock;
|
|
|
|
//
|
|
// Initialize other members.
|
|
//
|
|
KeInitializeMutex(&m_StateMutex,0);
|
|
KeInitializeSpinLock(&m_EventLock);
|
|
KeInitializeSpinLock(&m_ClockLock);
|
|
InitializeListHead(&m_EventList);
|
|
|
|
//
|
|
// Point the wave clock node to the IWaveClock interface.
|
|
//
|
|
m_waveClockNode.pWaveClock = PWAVECLOCK(this);
|
|
|
|
//
|
|
// Add this clock to the list of clocks. We don't need to keep the list
|
|
// head because removal does not require it. The spinlock will come in
|
|
// handy, though.
|
|
//
|
|
ExInterlockedInsertTailList
|
|
( pListEntry,
|
|
&m_waveClockNode.listEntry,
|
|
pKSpinLock
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* MyInterlockedRemoveEntryList()
|
|
*****************************************************************************
|
|
* Interlocked RemoveEntryList.
|
|
*/
|
|
void
|
|
MyInterlockedRemoveEntryList
|
|
( IN PLIST_ENTRY pListEntry,
|
|
IN PKSPIN_LOCK pKSpinLock
|
|
)
|
|
{
|
|
KIRQL kIrqlOld;
|
|
KeAcquireSpinLock(pKSpinLock,&kIrqlOld);
|
|
RemoveEntryList(pListEntry);
|
|
KeReleaseSpinLock(pKSpinLock,kIrqlOld);
|
|
}
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* CWaveClock::~CWaveClock()
|
|
*****************************************************************************
|
|
* Destructor.
|
|
*/
|
|
CWaveClock::~CWaveClock()
|
|
{
|
|
_DbgPrintF(DEBUGLVL_LIFETIME,("Destroying WAVECLK (0x%08x)",this));
|
|
|
|
//
|
|
// Remove us from the list if we are in it.
|
|
//
|
|
if (m_waveClockNode.listEntry.Flink)
|
|
{
|
|
MyInterlockedRemoveEntryList(&m_waveClockNode.listEntry,m_pKSpinLock);
|
|
}
|
|
|
|
//
|
|
// Release the control interface if we have a reference.
|
|
//
|
|
if (m_pIrpStreamControl)
|
|
{
|
|
m_pIrpStreamControl->Release();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CWaveClock::NonDelegatingQueryInterface()
|
|
*****************************************************************************
|
|
* Get an interface.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CWaveClock::NonDelegatingQueryInterface
|
|
(
|
|
IN REFIID riid,
|
|
OUT PVOID * ppvObject
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ppvObject);
|
|
|
|
_DbgPrintF(DEBUGLVL_BLAB,("CWaveClock::NonDelegatingQueryInterface"));
|
|
|
|
if
|
|
( IsEqualGUIDAligned(riid,IID_IUnknown)
|
|
|| IsEqualGUIDAligned(riid,IID_IIrpTarget)
|
|
)
|
|
{
|
|
*ppvObject = PVOID(PIRPTARGETINIT(this));
|
|
}
|
|
else
|
|
if (IsEqualGUIDAligned(riid,IID_IWaveClock))
|
|
{
|
|
*ppvObject = PVOID(PWAVECLOCK(this));
|
|
}
|
|
else
|
|
{
|
|
*ppvObject = NULL;
|
|
}
|
|
|
|
if (*ppvObject)
|
|
{
|
|
PUNKNOWN(*ppvObject)->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CWaveClock::DeviceIoControl
|
|
(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Processes device I/O control for this file object on this device object
|
|
|
|
Arguments:
|
|
IN PDEVICE_OBJECT DeviceObject -
|
|
pointer to the device object
|
|
|
|
IN PIRP Irp -
|
|
pointer to I/O request packet
|
|
|
|
Return:
|
|
STATUS_SUCCESS or an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( DeviceObject );
|
|
ASSERT( Irp );
|
|
|
|
_DbgPrintF( DEBUGLVL_BLAB, ("CWaveClock::DeviceIoControl"));
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_KS_PROPERTY:
|
|
Status =
|
|
KsPropertyHandler(
|
|
Irp,
|
|
SIZEOF_ARRAY( ClockPropertyTable ),
|
|
(PKSPROPERTY_SET) ClockPropertyTable );
|
|
break;
|
|
|
|
case IOCTL_KS_ENABLE_EVENT:
|
|
_DbgPrintF( DEBUGLVL_VERBOSE, ("CWaveClock::EnableEvent"));
|
|
|
|
Status =
|
|
KsEnableEvent(
|
|
Irp,
|
|
SIZEOF_ARRAY( ClockEventTable ),
|
|
(PKSEVENT_SET) ClockEventTable,
|
|
NULL,
|
|
KSEVENTS_NONE,
|
|
NULL);
|
|
break;
|
|
|
|
case IOCTL_KS_DISABLE_EVENT:
|
|
_DbgPrintF( DEBUGLVL_VERBOSE, ("CWaveClock::DisableEvent"));
|
|
|
|
Status =
|
|
KsDisableEvent(
|
|
Irp,
|
|
&m_EventList,
|
|
KSEVENTS_SPINLOCK,
|
|
&m_EventLock );
|
|
break;
|
|
|
|
default:
|
|
|
|
return KsDefaultDeviceIoCompletion( DeviceObject, Irp );
|
|
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CWaveClock::Close(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Close handler for the clock file object
|
|
|
|
Arguments:
|
|
IN PDEVICE_OBJECT DeviceObject -
|
|
pointer to the device object
|
|
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
Return:
|
|
STATUS success or an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject);
|
|
ASSERT(Irp);
|
|
|
|
_DbgPrintF( DEBUGLVL_VERBOSE, ("CWaveClock::Close"));
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Free events associated with this pin.
|
|
//
|
|
|
|
KsFreeEventList(
|
|
irpSp->FileObject,
|
|
&m_EventList,
|
|
KSEVENTS_SPINLOCK,
|
|
&m_EventLock );
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DEFINE_INVALID_CREATE(CWaveClock);
|
|
DEFINE_INVALID_READ(CWaveClock);
|
|
DEFINE_INVALID_WRITE(CWaveClock);
|
|
DEFINE_INVALID_FLUSH(CWaveClock);
|
|
DEFINE_INVALID_QUERYSECURITY(CWaveClock);
|
|
DEFINE_INVALID_SETSECURITY(CWaveClock);
|
|
DEFINE_INVALID_FASTDEVICEIOCONTROL(CWaveClock);
|
|
DEFINE_INVALID_FASTREAD(CWaveClock);
|
|
DEFINE_INVALID_FASTWRITE(CWaveClock);
|
|
|
|
#pragma code_seg()
|
|
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CWaveClock::
|
|
GenerateEvents
|
|
( void
|
|
)
|
|
{
|
|
LONGLONG Time;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
if (m_DeviceState == KSSTATE_RUN) {
|
|
|
|
Time = m_GetCurrentTime();
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
KeAcquireSpinLockAtDpcLevel( &m_EventLock );
|
|
|
|
for(ListEntry = m_EventList.Flink;
|
|
ListEntry != &m_EventList;) {
|
|
|
|
PKSEVENT_ENTRY EventEntry;
|
|
PKSINTERVAL Interval;
|
|
|
|
EventEntry =
|
|
(PKSEVENT_ENTRY)
|
|
CONTAINING_RECORD( ListEntry, KSEVENT_ENTRY, ListEntry );
|
|
|
|
//
|
|
// Pre-inc, KsGenerateEvent() can remove this item from the list.
|
|
//
|
|
ListEntry = ListEntry->Flink;
|
|
//
|
|
// The event-specific data was added onto the end of the entry.
|
|
//
|
|
Interval = (PKSINTERVAL)(EventEntry + 1);
|
|
//
|
|
// Time for this event to go off.
|
|
//
|
|
if (Interval->TimeBase <= Time) {
|
|
_DbgPrintF(
|
|
DEBUGLVL_VERBOSE, ("Generating event for time: %ld at time: %ld",
|
|
Interval->TimeBase, Time) );
|
|
|
|
if (EventEntry->EventItem->EventId !=
|
|
KSEVENT_CLOCK_INTERVAL_MARK) {
|
|
//
|
|
// A single-shot should only go off once, so make
|
|
// it a value which will never be reached again.
|
|
//
|
|
Interval->TimeBase = 0x7fffffffffffffff;
|
|
|
|
} else {
|
|
LONGLONG Intervals;
|
|
//
|
|
// An interval timer should only go off once per time,
|
|
// so update it to the next timeout.
|
|
//
|
|
Intervals =
|
|
(Time - Interval->TimeBase + Interval->Interval - 1) / Interval->Interval;
|
|
Interval->TimeBase += Intervals * Interval->Interval;
|
|
}
|
|
|
|
KsGenerateEvent( EventEntry );
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &m_EventLock );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CWaveClock::SetState(
|
|
KSSTATE State
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This method is called by the port to notify of a state change.
|
|
|
|
Arguments:
|
|
KSSTATE State -
|
|
New state
|
|
|
|
Return:
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Synchronize with GetState,
|
|
//
|
|
KeWaitForMutexObject(
|
|
&m_StateMutex,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
//
|
|
// set the new state,
|
|
//
|
|
m_DeviceState = State;
|
|
switch (State) {
|
|
|
|
case KSSTATE_STOP:
|
|
m_LastTime = m_LastPhysicalTime = m_LastPhysicalPosition = 0;
|
|
break;
|
|
|
|
case KSSTATE_RUN:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// and then release the mutex.
|
|
//
|
|
KeReleaseMutex( &m_StateMutex, FALSE );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::AddEvent(
|
|
IN PIRP Irp,
|
|
IN PKSEVENT_TIME_INTERVAL EventTime,
|
|
IN PKSEVENT_ENTRY EventEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the AddEvent() handler for the clock events.
|
|
|
|
NOTE: This routine acquires a spinlock, must be in non-paged code.
|
|
|
|
Arguments:
|
|
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSEVENT_TIME_INTERVAL EventTime -
|
|
specified time interval or one shot
|
|
|
|
IN PKSEVENT_ENTRY EventEntry -
|
|
pointer to event entry structure
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL irqlOld;
|
|
PKSINTERVAL Interval;
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
ASSERT( pCWaveClock );
|
|
|
|
_DbgPrintF( DEBUGLVL_VERBOSE, ("CWaveClock::AddEvent"));
|
|
|
|
//
|
|
// Space for the interval is located at the end of the basic
|
|
// event structure.
|
|
//
|
|
Interval = (PKSINTERVAL)(EventEntry + 1);
|
|
//
|
|
// Either just an event time was passed, or a time base plus an
|
|
// interval. In both cases the first LONGLONG is present and saved.
|
|
//
|
|
Interval->TimeBase = EventTime->TimeBase;
|
|
if (EventEntry->EventItem->EventId == KSEVENT_CLOCK_INTERVAL_MARK)
|
|
{
|
|
Interval->Interval = EventTime->Interval;
|
|
}
|
|
|
|
KeAcquireSpinLock( &pCWaveClock->m_EventLock, &irqlOld );
|
|
InsertHeadList( &pCWaveClock->m_EventList, &EventEntry->ListEntry );
|
|
KeReleaseSpinLock( &pCWaveClock->m_EventLock, irqlOld );
|
|
//
|
|
// If this event is passed, signal immediately.
|
|
// Note, KS_CLOCK_POSITION_MARK is a single-shot event
|
|
//
|
|
pCWaveClock->GenerateEvents();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
LONGLONG
|
|
FASTCALL
|
|
CWaveClock::
|
|
GetCurrentTime
|
|
(
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Computes the current presentation time.
|
|
|
|
NOTE: This routine acquires a spinlock, must be in non-paged code.
|
|
|
|
Arguments:
|
|
PFILE_OBJECT FileObject -
|
|
this clock's file object
|
|
|
|
Return:
|
|
resultant presentation time normalized to 100ns units.
|
|
|
|
--*/
|
|
|
|
{
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromFileObject(FileObject);
|
|
|
|
return pCWaveClock->m_GetCurrentTime();
|
|
}
|
|
|
|
LONGLONG
|
|
CWaveClock::
|
|
m_GetCurrentTime
|
|
( void
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Computes the current presentation time.
|
|
|
|
NOTE: This routine acquires a spinlock, must be in non-paged code.
|
|
|
|
Arguments:
|
|
|
|
Return:
|
|
resultant presentation time normalized to 100ns units.
|
|
|
|
--*/
|
|
{
|
|
IRPSTREAMPACKETINFO irpStreamPacketInfoUnmapping;
|
|
KIRQL irqlOld;
|
|
LONGLONG StreamTime;
|
|
NTSTATUS Status;
|
|
PIRPSTREAMCONTROL pIrpStreamControl;
|
|
|
|
StreamTime = 0;
|
|
|
|
//
|
|
// Query the position from the IRP stream.
|
|
//
|
|
pIrpStreamControl = m_pIrpStreamControl;
|
|
ASSERT(pIrpStreamControl);
|
|
IRPSTREAM_POSITION irpStreamPosition;
|
|
Status = pIrpStreamControl->GetPosition(&irpStreamPosition);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Never exceed current stream extent.
|
|
//
|
|
if
|
|
( irpStreamPosition.ullStreamPosition
|
|
> irpStreamPosition.ullCurrentExtent
|
|
)
|
|
{
|
|
StreamTime =
|
|
irpStreamPosition.ullCurrentExtent;
|
|
}
|
|
else
|
|
{
|
|
StreamTime =
|
|
irpStreamPosition.ullStreamPosition;
|
|
}
|
|
|
|
StreamTime =
|
|
pIrpStreamControl->NormalizePosition(StreamTime);
|
|
}
|
|
|
|
KeAcquireSpinLock( &m_ClockLock, &irqlOld );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
if (StreamTime < m_LastTime) {
|
|
_DbgPrintF(
|
|
DEBUGLVL_VERBOSE,
|
|
("new time is less than last reported time! (%I64d, %I64d)",
|
|
StreamTime, m_LastTime) );
|
|
StreamTime = m_LastTime;
|
|
} else {
|
|
m_LastTime = StreamTime;
|
|
}
|
|
} else {
|
|
StreamTime = m_LastTime;
|
|
}
|
|
|
|
KeReleaseSpinLock( &m_ClockLock, irqlOld );
|
|
|
|
return StreamTime;
|
|
}
|
|
|
|
LONGLONG
|
|
FASTCALL
|
|
CWaveClock::GetCurrentCorrelatedTime(
|
|
PFILE_OBJECT FileObject,
|
|
PLONGLONG SystemTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
PFILE_OBJECT FileObject -
|
|
|
|
PLONGLONG SystemTime -
|
|
pointer
|
|
|
|
Return:
|
|
current presentation time in 100ns
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Time, Frequency;
|
|
|
|
Time = KeQueryPerformanceCounter( &Frequency );
|
|
|
|
//
|
|
// Convert ticks to 100ns units.
|
|
//
|
|
*SystemTime = KSCONVERT_PERFORMANCE_TIME(Frequency.QuadPart,Time);
|
|
|
|
return GetCurrentTime( FileObject );
|
|
}
|
|
|
|
LONGLONG
|
|
FASTCALL
|
|
CWaveClock::GetCurrentPhysicalTime(
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Computes the current physical time.
|
|
|
|
NOTE: This routine acquires a spinlock, must be in non-paged code.
|
|
|
|
Arguments:
|
|
PFILE_OBJECT FileObject -
|
|
this clock's file object
|
|
|
|
Return:
|
|
current physical time in 100ns
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL irqlOld;
|
|
LONGLONG PhysicalTime;
|
|
NTSTATUS Status;
|
|
ULONG CurrentPosition;
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromFileObject(FileObject);
|
|
|
|
PhysicalTime = 0;
|
|
|
|
//
|
|
// Query the position from the IRP stream.
|
|
//
|
|
PIRPSTREAMCONTROL pIrpStreamControl =
|
|
pCWaveClock->m_pIrpStreamControl;
|
|
ASSERT( pIrpStreamControl );
|
|
IRPSTREAM_POSITION irpStreamPosition;
|
|
Status = pIrpStreamControl->GetPosition(&irpStreamPosition);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
PhysicalTime =
|
|
pIrpStreamControl->NormalizePosition
|
|
( irpStreamPosition.ullStreamPosition
|
|
+ irpStreamPosition.ullPhysicalOffset
|
|
);
|
|
}
|
|
|
|
KeAcquireSpinLock( &pCWaveClock->m_ClockLock, &irqlOld );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
//
|
|
// Verify that this new physical time is >= to the last
|
|
// reported physical time. If not, set the time to the
|
|
// last reported time. Flag this as an error in debug.
|
|
//
|
|
if (PhysicalTime < pCWaveClock->m_LastPhysicalTime) {
|
|
_DbgPrintF(
|
|
DEBUGLVL_VERBOSE,
|
|
("new physical time is less than last reported physical time! (%I64d, %I64d)",
|
|
PhysicalTime, pCWaveClock->m_LastPhysicalTime) );
|
|
PhysicalTime = pCWaveClock->m_LastPhysicalTime;
|
|
} else {
|
|
//
|
|
// Set m_LastPhysicalTime to the updated time.
|
|
//
|
|
pCWaveClock->m_LastPhysicalTime = PhysicalTime;
|
|
}
|
|
} else {
|
|
PhysicalTime = pCWaveClock->m_LastPhysicalTime;
|
|
}
|
|
|
|
KeReleaseSpinLock( &pCWaveClock->m_ClockLock, irqlOld );
|
|
|
|
return PhysicalTime;
|
|
}
|
|
|
|
LONGLONG
|
|
FASTCALL
|
|
CWaveClock::GetCurrentCorrelatedPhysicalTime(
|
|
PFILE_OBJECT FileObject,
|
|
PLONGLONG SystemTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the current physical time correlated with the system time.
|
|
|
|
Arguments:
|
|
PFILE_OBJECT FileObject -
|
|
this clock's file object
|
|
|
|
PLONGLONG SystemTime -
|
|
pointer to the resultant system time
|
|
|
|
Return:
|
|
current physical time in 100ns
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LARGE_INTEGER Time, Frequency;
|
|
|
|
Time = KeQueryPerformanceCounter( &Frequency );
|
|
|
|
//
|
|
// Convert ticks to 100ns units.
|
|
//
|
|
*SystemTime = KSCONVERT_PERFORMANCE_TIME(Frequency.QuadPart,Time);
|
|
return GetCurrentTime( FileObject );
|
|
}
|
|
|
|
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetFunctionTable(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCLOCK_FUNCTIONTABLE FunctionTable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the DPC interface function table for this clock.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PKSCLOCK_FUNCTIONTABLE FunctionTable -
|
|
pointer to the resultant function table
|
|
|
|
Return:
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
FunctionTable->GetTime = GetCurrentTime;
|
|
FunctionTable->GetPhysicalTime = GetCurrentPhysicalTime;
|
|
FunctionTable->GetCorrelatedTime = GetCurrentCorrelatedTime;
|
|
FunctionTable->GetCorrelatedPhysicalTime = GetCurrentCorrelatedPhysicalTime;
|
|
Irp->IoStatus.Information = sizeof(*FunctionTable);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetCorrelatedTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCORRELATED_TIME CorrelatedTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the current presentation time correlated with the system time.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PKSCORRELATED_TIME CorrelatedTime -
|
|
resultant correlated presentation time
|
|
|
|
Return:
|
|
STATUS_SUCCESS else an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
CorrelatedTime->Time =
|
|
pCWaveClock->GetCurrentCorrelatedTime(
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject,
|
|
&CorrelatedTime->SystemTime );
|
|
Irp->IoStatus.Information = sizeof( KSCORRELATED_TIME );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PLONGLONG Time
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the current presentation time.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PLONGLONG Time -
|
|
resultant presentation time
|
|
|
|
Return:
|
|
STATUS_SUCCESS else an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
*Time = pCWaveClock->GetCurrentTime(
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject );
|
|
Irp->IoStatus.Information = sizeof( LONGLONG );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetCorrelatedPhysicalTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSCORRELATED_TIME CorrelatedTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the current physical time correlated with the system time.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PKSCORRELATED_TIME CorrelatedTime -
|
|
resultant correlated physical time
|
|
|
|
Return:
|
|
STATUS_SUCCESS else an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
CorrelatedTime->Time =
|
|
pCWaveClock->GetCurrentCorrelatedPhysicalTime(
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject,
|
|
&CorrelatedTime->SystemTime );
|
|
|
|
Irp->IoStatus.Information = sizeof( KSCORRELATED_TIME );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetPhysicalTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PLONGLONG Time
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the clock's physical time. This is the actual clock physical time
|
|
which is not halted for starvation, etc.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PLONGLONG Time -
|
|
resultant time in 100 ns units
|
|
|
|
Return:
|
|
STATUS_SUCCESS or an appropriate error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
*Time =
|
|
pCWaveClock->GetCurrentPhysicalTime(
|
|
IoGetCurrentIrpStackLocation( Irp )->FileObject );
|
|
Irp->IoStatus.Information = sizeof( LONGLONG );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetResolution(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSRESOLUTION Resolution
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Retrieves the resolution of this clock.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PKSRESOLUTIONM Resolution -
|
|
pointer to the resultant resolution structure which stores the
|
|
granularity and error in 100ns units.
|
|
|
|
Return Value:
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
//
|
|
// This clock has a resolution dependant on the data format. Assume
|
|
// that for cyclic devices, a byte position is computed for the DMA
|
|
// controller and convert this to 100ns units. The error (event
|
|
// notification error) is +/- NotificationFrequency/2
|
|
|
|
Resolution->Granularity =
|
|
pCWaveClock->m_pIrpStreamControl->NormalizePosition(1);
|
|
|
|
Resolution->Error =
|
|
(_100NS_UNITS_PER_SECOND / 1000 * WAVECYC_NOTIFICATION_FREQUENCY) / 2;
|
|
|
|
Irp->IoStatus.Information = sizeof(*Resolution);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CWaveClock::GetState(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSSTATE State
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the underlying pin's state.
|
|
|
|
Arguments:
|
|
IN PIRP Irp -
|
|
pointer to the I/O request packet
|
|
|
|
IN PKSPROPERTY Property -
|
|
pointer to the property structure
|
|
|
|
OUT PKSSTATE State -
|
|
pointer to resultant KSSTATE
|
|
|
|
Return:
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CWaveClock *pCWaveClock =
|
|
(CWaveClock *) KsoGetIrpTargetFromIrp(Irp);
|
|
|
|
//
|
|
// Synchronize with SetState,
|
|
//
|
|
KeWaitForMutexObject(
|
|
&pCWaveClock->m_StateMutex,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
//
|
|
// retrieve the state
|
|
//
|
|
*State = pCWaveClock->m_DeviceState;
|
|
//
|
|
// and then release the mutex
|
|
//
|
|
KeReleaseMutex( &pCWaveClock->m_StateMutex, FALSE );
|
|
|
|
Irp->IoStatus.Information = sizeof(*State);
|
|
return STATUS_SUCCESS;
|
|
}
|