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