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

--*/

{
    PAGED_CODE();

    KScopedMutex Lock( m_ListLock );

    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_PinState == PinRunning)
    {
        //
        // Generate a "time stamp" just to overlay it onto the capture image.
        // It makes it more exciting than bars that do nothing.
        //
        ULONGLONG time = ConvertQPCtoTimeStamp(NULL);
        DBG_TRACE("QPC=0x%016llX", time);

        m_Synthesizer->SetFrameNumber( m_InterruptTime );
        m_Synthesizer->SetRelativePts( (m_InterruptTime + 1) * m_TimePerFrame );
        m_Synthesizer->SetQpcTime( time );

        m_Synthesizer->DoSynthesize();

        CHAR Text[64];

        CExtendedProperty   Control;
        m_Sensor->GetVideoStabilization( &Control );
        RtlStringCbPrintfA(Text, sizeof(Text), "DVS: %s", DVS_Text(Control.Flags));
        m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, BLACK, WHITE );

        m_Sensor->GetOpticalImageStabilization( &Control );
        RtlStringCbPrintfA(Text, sizeof(Text), "OIS: %s", OIS_Text(Control.Flags));
        m_Synthesizer->OverlayText( 0, m_Height-48, 1, Text, BLACK, WHITE );

        //
        // 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_Sensor -> Interrupt (m_PinID);

    //
    //  Schedule the timer for the next interrupt time, if the pin is still running.
    //
    if( m_PinState == PinRunning )
    {
        LARGE_INTEGER NextTime;
        NextTime.QuadPart = m_StartTime.QuadPart +
                            (m_TimePerFrame * (m_InterruptTime + 1));

#ifdef ENABLE_TRACING  // To keep us from a tight spin when trying to debug this code...
        LARGE_INTEGER Now;
        KeQuerySystemTime(&Now);

        if( Now.QuadPart >= NextTime.QuadPart )
        {
            NextTime.QuadPart = 0LL - m_TimePerFrame ;
        }

#endif
        m_IsrTimer.Set( NextTime );
    }
}
NTSTATUS
CHardwareSimulation::
FillScatterGatherBuffers()

/*++

Routine Description:

    The hardware has synthesized a buffer and we're to fill a frame buffer.

Arguments:

    None

Return Value:

    Success / Failure

--*/

{
    PAGED_CODE();

    DBG_ENTER("() m_PinState=%s, m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%u, "
              "m_ScatterGatherBytesQueue=0x%08X",
              GetPinStateTxt(m_PinState),
              m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued);

    NTSTATUS    ntStatus = STATUS_SUCCESS;

    ULONG BufferRemaining = m_ImageSize;

    //
    //  If there isn't a frame buffer queued, we justskip the frame and consider 
    //  it starvation.
    //
    while (BufferRemaining &&
            !IsListEmpty(&m_ScatterGatherMappings) &&
            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
                )
            );

        //  Deal with cancellation.
        PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE);
        if (pIrp)
        {
            if (pIrp->Cancel)
            {
                DBG_TRACE( "Cancelling..." );
                FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" );
                continue;
            }
        }

        //
        // Since we're software, we'll be accessing this by virtual address...
        //
        ULONG Stride = 0;
        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);
            Stride = (ULONG) ABS(FrameInfo->lSurfacePitch);
        }

        //  Have the synthesizer output a frame to the buffer.
        ULONG   BytesCopied = m_Synthesizer->DoCommit( SGEntry->Virtual, SGEntry->ByteCount, Stride );
        NT_ASSERT( BytesCopied );
        DBG_TRACE( "BytesCopied = %d", BytesCopied );

        //Adding time stamp
        if(m_PhotoConfirmationEntry.isRequired())
        {
            SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time =
                m_PhotoConfirmationEntry.getTime();
            DBG_TRACE("Using Photo Confirmation time = 0x%016llX", m_PhotoConfirmationEntry.getTime());
        }
        else
        {
            SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = ConvertQPCtoTimeStamp(NULL);
            DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time );
        }

        SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Numerator =
            SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Denominator = 1;

        SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID ;

        // Add metadata to the sample.
        EmitMetadata( SGEntry -> CloneEntry -> StreamHeader );

        BufferRemaining = 0; //-= BytesCopied;
        m_NumMappingsCompleted++;
        m_ScatterGatherBytesQueued -= SGEntry -> ByteCount;

        //
        // Release the scatter / gather entry back to our lookaside.
        //
        ExFreeToNPagedLookasideList (
            &m_ScatterGatherLookaside,
            reinterpret_cast <PVOID> (SGEntry)
        );

    }

    //  Report an error if we used the last buffer.
    if (BufferRemaining)
    {
        //DBG_TRACE("BufferRemaining=%u", BufferRemaining);
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    DBG_LEAVE("() = 0x%08X", ntStatus);
    return ntStatus;
}
NTSTATUS
CImageHardwareSimulation::
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

--*/

{
    PAGED_CODE();

    DBG_ENTER("() m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%d, "
              "m_ScatterGatherBytesQueue=0x%08X",
              m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued);

    //
    // We're using this list lock to protect our scatter / gather lists instead
    // of some hardware mechanism / KeSynchronizeExecution / whatever.
    //
    //KeAcquireSpinLockAtDpcLevel (&m_ListLock);

    ULONG BufferRemaining = m_ImageSize;

    //
    // If there aren't enough scatter / gather buffers queued, consider it starvation.
    //
    while(  BufferRemaining &&
            !IsListEmpty(&m_ScatterGatherMappings) &&
            m_ScatterGatherBytesQueued >= BufferRemaining)
    {
        DBG_TRACE( "BufferRemaining=0x%08X, m_ScatterGatherBytesQueued=0x%08X, m_ScatterGatherMappingsQueued=%d",
                   BufferRemaining, m_ScatterGatherBytesQueued, m_ScatterGatherMappingsQueued );

        LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings);
        m_ScatterGatherMappingsQueued--;

        PSCATTER_GATHER_ENTRY SGEntry =
            reinterpret_cast <PSCATTER_GATHER_ENTRY> (
                CONTAINING_RECORD (
                    listEntry,
                    SCATTER_GATHER_ENTRY,
                    ListEntry
                )
            );

        //  Deal with cancellation.
        PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE);
        if (pIrp)
        {
            if (pIrp->Cancel)
            {
                DBG_TRACE( "Cancelling..." );
                FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" );
                continue;
            }
        }

        //
        // Since we're software, we'll be accessing this by virtual address...
        //
        ULONG BytesToCopy = min( BufferRemaining, SGEntry->ByteCount );

        //  Have the synthesizer output a frame to the buffer.
        DBG_TRACE( "DataUsed before Commit() = %d", SGEntry->CloneEntry->StreamHeader->DataUsed );
        ULONG   BytesCopied =
            m_Synthesizer->DoCommit( SGEntry->Virtual, BytesToCopy );
        NT_ASSERT(BytesCopied);
        DBG_TRACE( "BytesCopied = %d", BytesCopied );

        BufferRemaining = 0; //-= BytesCopied;

        //  Add metadata to the sample.
        EmitMetadata( SGEntry -> CloneEntry -> StreamHeader );

        ULONGLONG time = ConvertQPCtoTimeStamp(NULL);

        if (IsPhotoConfirmationNeeded())
        {
            DBG_TRACE( "PhotoConfirmation is needed.  Frame=%d, Time=0x%016llX", m_PfsFrameNumber, (LONGLONG) time );
            SGEntry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO( m_PfsFrameNumber, (LONGLONG) time );
        }

        SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time =  time;
        DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time );

        SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID;

        DBG_TRACE("m_FlashStatus=0x%016llX", m_FlashStatus);

        if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER ||
                m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER)
        {
            if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH && time >= m_TriggerTime && m_TriggerTime != 0 && !m_bFlashed)
            {
                m_bFlashed = TRUE;
                DBG_TRACE("(Single) FLASHED!!!");
            }
        }

        //
        // Release the scatter / gather entry back to our lookaside.
        //
        if( m_bTriggered && !IsPfsEOS() )
        {
            DBG_TRACE("m_PinMode=%d", m_PinMode);
            m_pClone = SGEntry->CloneEntry;
            m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo;
            m_NumMappingsCompleted++;
            m_ScatterGatherBytesQueued -= SGEntry -> ByteCount;

            DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired()?"TRUE":"FALSE" );

            if(m_PinMode != PinBurstMode)
            {
                m_bTriggered = FALSE;
                DBG_TRACE("m_bTriggered=FALSE");
            }

            //  Update the VPS frame and loop numbers here.  Mark the frame as the EOS
            //  if we've completed the sequence.
            //
            //  Note: It's actually up to DevProxy to stop feeding us frames!
            if( AdvanceFrameCounter() )
            {
                //  We've reached the end of a VPS sequence!  Mark the frame as EOS.
                SGEntry->CloneEntry->StreamHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_ENDOFPHOTOSEQUENCE;
                m_bEndOfSequence = TRUE;
            }

            ExFreeToNPagedLookasideList (
                &m_ScatterGatherLookaside,
                reinterpret_cast <PVOID> (SGEntry)
            );
        }
        else
        {
            InsertTailList( &m_ScatterGatherMappings, listEntry );
            m_ScatterGatherMappingsQueued++;
            m_pClone = NULL;
        }
    }

    DBG_LEAVE("()");

    if (BufferRemaining)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    else
    {
        return STATUS_SUCCESS;
    }
}