summaryrefslogtreecommitdiff
path: root/AVStream/avshws/hwsim.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'AVStream/avshws/hwsim.cpp')
-rw-r--r--AVStream/avshws/hwsim.cpp868
1 files changed, 868 insertions, 0 deletions
diff --git a/AVStream/avshws/hwsim.cpp b/AVStream/avshws/hwsim.cpp
new file mode 100644
index 00000000..9a6d5f86
--- /dev/null
+++ b/AVStream/avshws/hwsim.cpp
@@ -0,0 +1,868 @@
+/**************************************************************************
+
+ AVStream Simulated Hardware Sample
+
+ Copyright (c) 2001, Microsoft Corporation.
+
+ File:
+
+ hwsim.cpp
+
+ Abstract:
+
+ This file contains the hardware simulation. It fakes "DMA" transfers,
+ scatter gather mapping handling, ISR's, etc... The ISR routine in
+ here will be called when an ISR would be generated by the fake hardware
+ and it will directly call into the device level ISR for more accurate
+ simulation.
+
+ History:
+
+ created 3/9/2001
+
+**************************************************************************/
+
+#include "avshws.h"
+
+
+/*************************************************/
+KDEFERRED_ROUTINE SimulatedInterrupt;
+
+void
+SimulatedInterrupt (
+ IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArg1,
+ IN PVOID SystemArg2
+ )
+{
+ CHardwareSimulation* HardwareSim = (CHardwareSimulation*)DeferredContext;
+
+ if (HardwareSim)
+ {
+ HardwareSim -> FakeHardware ();
+ }
+}
+
+
+/**************************************************************************
+
+ PAGEABLE CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg("PAGE")
+#endif // ALLOC_PRAGMA
+
+
+
+CHardwareSimulation::
+CHardwareSimulation (
+ IN IHardwareSink *HardwareSink
+ ) :
+ m_HardwareSink (HardwareSink),
+ m_ScatterGatherMappingsMax (SCATTER_GATHER_MAPPINGS_MAX)
+
+/*++
+
+Routine Description:
+
+ Construct a hardware simulation
+
+Arguments:
+
+ HardwareSink -
+ The hardware sink interface. This is used to trigger
+ fake interrupt service routines from.
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ //
+ // Initialize the DPC's, timer's, and locks necessary to simulate
+ // this capture hardware.
+ //
+ KeInitializeDpc (
+ &m_IsrFakeDpc,
+ SimulatedInterrupt,
+ this
+ );
+
+ KeInitializeEvent (
+ &m_HardwareEvent,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ KeInitializeTimer (&m_IsrTimer);
+
+ KeInitializeSpinLock (&m_ListLock);
+
+}
+
+/*************************************************/
+
+
+CHardwareSimulation *
+CHardwareSimulation::
+Initialize (
+ IN KSOBJECT_BAG Bag,
+ IN IHardwareSink *HardwareSink
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the hardware simulation
+
+Arguments:
+
+ HardwareSink -
+ The hardware sink interface. This is what ISR's will be
+ triggered through.
+
+Return Value:
+
+ A fully initialized hardware simulation or NULL if the simulation
+ could not be initialized.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ CHardwareSimulation *HwSim =
+ new (NonPagedPoolNx, 'miSH') CHardwareSimulation (HardwareSink);
+
+ return HwSim;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CHardwareSimulation::
+Start (
+ IN CImageSynthesizer *ImageSynth,
+ IN LONGLONG TimePerFrame,
+ IN ULONG Width,
+ IN ULONG Height,
+ IN ULONG ImageSize
+ )
+
+/*++
+
+Routine Description:
+
+ Start the hardware simulation. This will kick the interrupts on,
+ begin issuing DPC's, filling in capture information, etc...
+ We keep track of starvation starting at this point.
+
+Arguments:
+
+ ImageSynth -
+ The image synthesizer to use to generate pictures to display
+ on the capture buffer.
+
+ TimePerFrame -
+ The time per frame... we issue interrupts this often.
+
+ Width -
+ The image width
+
+ Height -
+ The image height
+
+ ImageSize -
+ The size of the image. We allocate a temporary scratch buffer
+ based on this size to fake hardware.
+
+Return Value:
+
+ Success / Failure (typical failure will be out of memory on the
+ scratch buffer, etc...)
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ m_ImageSynth = ImageSynth;
+ m_TimePerFrame = TimePerFrame;
+ m_ImageSize = ImageSize;
+ m_Height = Height;
+ m_Width = Width;
+
+ InitializeListHead (&m_ScatterGatherMappings);
+ m_NumMappingsCompleted = 0;
+ m_ScatterGatherMappingsQueued = 0;
+ m_NumFramesSkipped = 0;
+ m_InterruptTime = 0;
+
+ KeQuerySystemTime (&m_StartTime);
+
+ //
+ // Allocate a scratch buffer for the synthesizer.
+ //
+ m_SynthesisBuffer = reinterpret_cast <PUCHAR> (
+ ExAllocatePoolWithTag (
+ NonPagedPoolNx,
+ m_ImageSize,
+ AVSHWS_POOLTAG
+ )
+ );
+
+ if (!m_SynthesisBuffer) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If everything is ok, start issuing interrupts.
+ //
+ if (NT_SUCCESS (Status)) {
+
+ //
+ // Initialize the entry lookaside.
+ //
+ ExInitializeNPagedLookasideList (
+ &m_ScatterGatherLookaside,
+ NULL,
+ NULL,
+ POOL_NX_ALLOCATION,
+ sizeof (SCATTER_GATHER_ENTRY),
+ 'nEGS',
+ 0
+ );
+
+ //
+ // Set up the synthesizer with the width, height, and scratch buffer.
+ //
+ m_ImageSynth -> SetImageSize (m_Width, m_Height);
+ m_ImageSynth -> SetBuffer (m_SynthesisBuffer);
+
+ LARGE_INTEGER NextTime;
+ NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame;
+
+ m_HardwareState = HardwareRunning;
+ KeSetTimer (&m_IsrTimer, NextTime, &m_IsrFakeDpc);
+
+ }
+
+ return Status;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CHardwareSimulation::
+Pause (
+ BOOLEAN Pausing
+ )
+
+/*++
+
+Routine Description:
+
+ Pause the hardware simulation... When the hardware simulation is told
+ to pause, it stops issuing interrupts, etc... but it does not reset
+ the counters
+
+Arguments:
+
+ Pausing -
+ Indicates whether the hardware is pausing or not.
+
+ TRUE -
+ Pause the hardware
+
+ FALSE -
+ Unpause the hardware from a previous pause
+
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ if (Pausing && m_HardwareState == HardwareRunning) {
+ //
+ // If we were running, stop completing mappings, etc...
+ //
+ m_StopHardware = TRUE;
+
+ KeWaitForSingleObject (
+ &m_HardwareEvent,
+ Suspended,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ NT_ASSERT (m_StopHardware == FALSE);
+
+ m_HardwareState = HardwarePaused;
+
+ } else if (!Pausing && m_HardwareState == HardwarePaused) {
+
+ //
+ // For unpausing the hardware, we need to compute the relative time
+ // and restart interrupts.
+ //
+ LARGE_INTEGER UnpauseTime;
+
+ KeQuerySystemTime (&UnpauseTime);
+ m_InterruptTime = (ULONG) (
+ (UnpauseTime.QuadPart - m_StartTime.QuadPart) /
+ m_TimePerFrame
+ );
+
+ UnpauseTime.QuadPart = m_StartTime.QuadPart +
+ (m_InterruptTime + 1) * m_TimePerFrame;
+
+ m_HardwareState = HardwareRunning;
+ KeSetTimer (&m_IsrTimer, UnpauseTime, &m_IsrFakeDpc);
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+/**************************************************************************
+
+ LOCKED CODE
+
+**************************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma code_seg()
+#endif // ALLOC_PRAGMA
+
+NTSTATUS
+CHardwareSimulation::
+Stop (
+ )
+
+/*++
+
+Routine Description:
+
+ Stop the hardware simulation.... Wait until the hardware simulation
+ has successfully stopped and then return.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+ KIRQL Irql;
+ //
+ // If the hardware is told to stop while it's running, we need to
+ // halt the interrupts first. If we're already paused, this has
+ // already been done.
+ //
+ if (m_HardwareState == HardwareRunning) {
+
+ m_StopHardware = TRUE;
+
+ KeWaitForSingleObject (
+ &m_HardwareEvent,
+ Suspended,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ NT_ASSERT (m_StopHardware == FALSE);
+
+ }
+
+ m_HardwareState = HardwareStopped;
+
+ //
+ // The image synthesizer may still be around. Just for safety's
+ // sake, NULL out the image synthesis buffer and toast it.
+ //
+ m_ImageSynth -> SetBuffer (NULL);
+
+ if (m_SynthesisBuffer) {
+ ExFreePool (m_SynthesisBuffer);
+ m_SynthesisBuffer = NULL;
+ }
+
+ //
+ // Protect the S/G list
+ //
+ KeAcquireSpinLock (&m_ListLock, &Irql);
+ //
+ // Free S/G buffer
+ //
+ //
+ while (m_ScatterGatherMappingsQueued > 0) {
+ LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings);
+ m_ScatterGatherMappingsQueued--;
+ PSCATTER_GATHER_ENTRY SGEntry =
+ reinterpret_cast <PSCATTER_GATHER_ENTRY> (
+ CONTAINING_RECORD (
+ listEntry,
+ SCATTER_GATHER_ENTRY,
+ ListEntry
+ )
+ );
+ //
+ // Release the scatter / gather entry back to our lookaside.
+ //
+ ExFreeToNPagedLookasideList (
+ &m_ScatterGatherLookaside,
+ reinterpret_cast <PVOID> (SGEntry)
+ );
+ }
+
+ m_NumMappingsCompleted = 0;
+ m_ScatterGatherBytesQueued = 0;
+ //
+ // Delete the scatter / gather lookaside for this run.
+ //
+ ExDeleteNPagedLookasideList (&m_ScatterGatherLookaside);
+
+ KeReleaseSpinLock (&m_ListLock, Irql);
+
+ return STATUS_SUCCESS;
+
+}
+
+
+ULONG
+CHardwareSimulation::
+ReadNumberOfMappingsCompleted (
+ )
+
+/*++
+
+Routine Description:
+
+ Read the number of scatter / gather mappings which have been
+ completed (TOTAL NUMBER) since the last reset of the simulated
+ hardware
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Total number of completed mappings.
+
+--*/
+
+{
+
+ //
+ // Don't care if this is being updated this moment in the DPC... I only
+ // need a number to return which isn't too great (too small is ok).
+ // In real hardware, this wouldn't be done this way anyway.
+ //
+ return m_NumMappingsCompleted;
+
+}
+
+/*************************************************/
+
+
+ULONG
+CHardwareSimulation::
+ProgramScatterGatherMappings (
+ IN PKSSTREAM_POINTER Clone,
+ IN PUCHAR *Buffer,
+ IN PKSMAPPING Mappings,
+ IN ULONG MappingsCount,
+ IN ULONG MappingStride
+ )
+
+/*++
+
+Routine Description:
+
+ Program the scatter gather mapping list. This shoves a bunch of
+ entries on a list for access during the fake interrupt. Note that
+ we have physical addresses here only for simulation. We really
+ access via the virtual address.... although we chunk it into multiple
+ buffers to more realistically simulate S/G
+
+Arguments:
+
+ Buffer -
+ The virtual address of the buffer mapped by the mapping list
+
+ Mappings -
+ The KSMAPPINGS array corresponding to the buffer
+
+ MappingsCount -
+ The number of mappings in the mappings array
+
+ MappingStride -
+ The mapping stride used in initialization of AVStream DMA
+
+Return Value:
+
+ Number of mappings actually inserted.
+
+--*/
+
+{
+
+ KIRQL Irql;
+
+ ULONG MappingsInserted = 0;
+
+ //
+ // Protect our S/G list with a spinlock.
+ //
+ KeAcquireSpinLock (&m_ListLock, &Irql);
+
+ //
+ // Loop through the scatter / gather list and break the buffer up into
+ // chunks equal to the scatter / gather mappings. Stuff the virtual
+ // addresses of these chunks on a list somewhere. We update the buffer
+ // pointer the caller passes as a more convenient way of doing this.
+ //
+ // If I could just remap physical in the list to virtual easily here,
+ // I wouldn't need to do it.
+ //
+#if !defined(_X86_)
+ do
+ {
+ PSCATTER_GATHER_ENTRY Entry =
+ reinterpret_cast <PSCATTER_GATHER_ENTRY> (
+ ExAllocateFromNPagedLookasideList (
+ &m_ScatterGatherLookaside
+ )
+ );
+
+ if (!Entry) {
+ break;
+ }
+ Entry -> Virtual = *Buffer;
+ Entry -> ByteCount = MappingsCount;
+ Entry -> CloneEntry = Clone;
+
+ //
+ // Move forward a specific number of bytes in chunking this into
+ // mapping sized va buffers.
+ //
+ *Buffer += MappingsCount;
+ Mappings = reinterpret_cast <PKSMAPPING> (
+ (reinterpret_cast <PUCHAR> (Mappings) + MappingStride)
+ );
+
+ InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry));
+ MappingsInserted = MappingsCount;
+ m_ScatterGatherMappingsQueued++;
+ m_ScatterGatherBytesQueued += MappingsCount;
+
+ }
+ while(FALSE);
+
+#else
+ for (ULONG MappingNum = 0;
+ MappingNum < MappingsCount &&
+ m_ScatterGatherMappingsQueued < m_ScatterGatherMappingsMax;
+ MappingNum++) {
+
+ PSCATTER_GATHER_ENTRY Entry =
+ reinterpret_cast <PSCATTER_GATHER_ENTRY> (
+ ExAllocateFromNPagedLookasideList (
+ &m_ScatterGatherLookaside
+ )
+ );
+
+ if (!Entry) {
+ break;
+ }
+
+ Entry -> Virtual = *Buffer;
+ Entry -> ByteCount = Mappings -> ByteCount;
+
+ //
+ // Move forward a specific number of bytes in chunking this into
+ // mapping sized va buffers.
+ //
+ *Buffer += Entry -> ByteCount;
+ Mappings = reinterpret_cast <PKSMAPPING> (
+ (reinterpret_cast <PUCHAR> (Mappings) + MappingStride)
+ );
+
+ InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry));
+ MappingsInserted++;
+ m_ScatterGatherMappingsQueued++;
+ m_ScatterGatherBytesQueued += Entry -> ByteCount;
+
+ }
+#endif
+
+ KeReleaseSpinLock (&m_ListLock, Irql);
+
+ return MappingsInserted;
+
+}
+
+/*************************************************/
+
+
+NTSTATUS
+CHardwareSimulation::
+FillScatterGatherBuffers (
+ )
+
+/*++
+
+Routine Description:
+
+ The hardware has synthesized a buffer in scratch space and we're to
+ fill scatter / gather buffers.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Success / Failure
+
+--*/
+
+{
+
+ //
+ // We're using this list lock to protect our scatter / gather lists instead
+ // of some hardware mechanism / KeSynchronizeExecution / whatever.
+ //
+ KeAcquireSpinLockAtDpcLevel (&m_ListLock);
+
+ PUCHAR Buffer = reinterpret_cast <PUCHAR> (m_SynthesisBuffer);
+ ULONG BufferRemaining = m_ImageSize;
+
+ //
+ // For simplification, if there aren't enough scatter / gather buffers
+ // queued, we don't partially fill the ones that are available. We just
+ // skip the frame and consider it starvation.
+ //
+ // This could be enforced by only programming scatter / gather mappings
+ // for a buffer if all of them fit in the table also...
+ //
+ while (BufferRemaining &&
+ m_ScatterGatherMappingsQueued > 0 &&
+ m_ScatterGatherBytesQueued >= BufferRemaining) {
+
+ LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings);
+ m_ScatterGatherMappingsQueued--;
+
+ PSCATTER_GATHER_ENTRY SGEntry =
+ reinterpret_cast <PSCATTER_GATHER_ENTRY> (
+ CONTAINING_RECORD (
+ listEntry,
+ SCATTER_GATHER_ENTRY,
+ ListEntry
+ )
+ );
+
+ //
+ // Since we're software, we'll be accessing this by virtual address...
+ //
+ ULONG BytesToCopy =
+ (BufferRemaining < SGEntry -> ByteCount) ?
+ BufferRemaining :
+ SGEntry -> ByteCount;
+
+ LONG Width = m_Width*(m_ImageSynth->GetBytesPerPixel());
+
+ LONG Stride = Width;
+ if(SGEntry->CloneEntry->StreamHeader->Size >= sizeof(KSSTREAM_HEADER)+sizeof(KS_FRAME_INFO))
+ {
+ PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (SGEntry->CloneEntry->StreamHeader+1);
+ if(FrameInfo->lSurfacePitch != 0)
+ {
+ Stride = FrameInfo->lSurfacePitch;
+ if(FrameInfo->lSurfacePitch < 0)
+ {
+ Stride = -Stride;
+ }
+ }
+ }
+
+ for(ULONG y = 0; y < m_Height; y++)
+ {
+ RtlCopyMemory((SGEntry->Virtual+(ULONG)Stride*y), Buffer, Width);
+ Buffer += Width;
+ BytesToCopy -= Width;
+ BufferRemaining -= Width;
+ }
+
+ m_NumMappingsCompleted++;
+ m_ScatterGatherBytesQueued -= SGEntry -> ByteCount;
+
+ //
+ // Release the scatter / gather entry back to our lookaside.
+ //
+ ExFreeToNPagedLookasideList (
+ &m_ScatterGatherLookaside,
+ reinterpret_cast <PVOID> (SGEntry)
+ );
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel (&m_ListLock);
+
+ if (BufferRemaining) return STATUS_INSUFFICIENT_RESOURCES;
+ else return STATUS_SUCCESS;
+
+}
+
+/*************************************************/
+
+
+void
+CHardwareSimulation::
+FakeHardware (
+ )
+
+/*++
+
+Routine Description:
+
+ Simulate an interrupt and what the hardware would have done in the
+ time since the previous interrupt.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+
+{
+
+ m_InterruptTime++;
+
+ //
+ // The hardware can be in a pause state in which case, it issues interrupts
+ // but does not complete mappings. In this case, don't bother synthesizing
+ // a frame and doing the work of looking through the mappings table.
+ //
+ if (m_HardwareState == HardwareRunning) {
+
+ //
+ // Generate a "time stamp" just to overlay it onto the capture image.
+ // It makes it more exciting than bars that do nothing.
+ //
+ LONGLONG PtsRel = ((m_InterruptTime + 1) * m_TimePerFrame);
+
+ ULONG Min = (ULONG)(PtsRel / 600000000);
+ ULONG RemMin = (ULONG)(PtsRel % 600000000);
+ ULONG Sec = (ULONG)(RemMin / 10000000);
+ ULONG RemSec = (ULONG)(RemMin % 10000000);
+ ULONG Hund = (ULONG)(RemSec / 100000);
+
+ //
+ // Synthesize a buffer in scratch space.
+ //
+ m_ImageSynth -> SynthesizeBars ();
+
+ CHAR Text [256];
+ Text[0] = '\0';
+ (void) RtlStringCbPrintfA(Text, sizeof(Text), "%ld:%02ld.%02ld", Min, Sec, Hund);
+
+ //
+ // Overlay a clock onto the scratch space image.
+ //
+ m_ImageSynth -> OverlayText (
+ POSITION_CENTER,
+ (m_Height - 28),
+ 1,
+ Text,
+ BLACK,
+ WHITE
+ );
+
+ //
+ // Overlay a counter of skipped frames onto the scratch image.
+ //
+ (void) RtlStringCbPrintfA(Text, sizeof(Text), "Skipped: %ld", m_NumFramesSkipped);
+ m_ImageSynth -> OverlayText (
+ 10,
+ 10,
+ 1,
+ Text,
+ TRANSPARENT,
+ BLUE
+ );
+
+ //
+ // Fill scatter gather buffers
+ //
+ if (!NT_SUCCESS (FillScatterGatherBuffers ())) {
+ InterlockedIncrement (PLONG (&m_NumFramesSkipped));
+ }
+
+ }
+
+ //
+ // Issue an interrupt to our hardware sink. This is a "fake" interrupt.
+ // It will occur at DISPATCH_LEVEL.
+ //
+ m_HardwareSink -> Interrupt ();
+
+ //
+ // Reschedule the timer if the hardware isn't being stopped.
+ //
+ if (!m_StopHardware) {
+
+ //
+ // Reschedule the timer for the next interrupt time.
+ //
+ LARGE_INTEGER NextTime;
+ NextTime.QuadPart = m_StartTime.QuadPart +
+ (m_TimePerFrame * (m_InterruptTime + 1));
+
+ KeSetTimer (&m_IsrTimer, NextTime, &m_IsrFakeDpc);
+
+ } else {
+ //
+ // If someone is waiting on the hardware to stop, raise the stop
+ // event and clear the flag.
+ //
+ m_StopHardware = FALSE;
+ KeSetEvent (&m_HardwareEvent, IO_NO_INCREMENT, FALSE);
+ }
+
+}