<|start_filename|>audio/sysvad/APO/SwapAPO/SwapAPODll.cpp<|end_filename|>
//
// SwapAPODll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Author:
//
// Description:
//
// SwapAPODll.cpp : Implementation of DLL Exports.

#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsync.h>
#include <mmreg.h>

#include "resource.h"
#include "SwapAPODll.h"
#include <SwapAPO.h>

#include <SwapAPODll_i.c>

//-------------------------------------------------------------------------
// Array of APO_REG_PROPERTIES structures implemented in this module.
// Each new APO implementation will be added to this array.
//
APO_REG_PROPERTIES const *gCoreAPOs[] =
{
    &CSwapAPOMFX::sm_RegProperties.m_Properties,
    &CSwapAPOSFX::sm_RegProperties.m_Properties
};

// {secret}
class CSwapAPODllModule : public CAtlDllModuleT< CSwapAPODllModule >
{
public :
    DECLARE_LIBID(LIBID_SwapAPODlllib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SWAPAPODLL, "{0A21D954-674A-4C09-806E-DB4FBE8F199C}")
};

// {secret}
CSwapAPODllModule _AtlModule;


// {secret}
extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
    {
    }
    // do necessary cleanup only if the DLL is being unloaded dynamically
    else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved))
    {
    }

    return _AtlModule.DllMain(dwReason, lpReserved);
}


// {secret}
__control_entrypoint(DllExport)
STDAPI DllCanUnloadNow(void)
{
    return _AtlModule.DllCanUnloadNow();
}


// {secret}
_Check_return_
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
{
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}

<|start_filename|>avstream/avssamp/video.cpp<|end_filename|>
/**************************************************************************

    AVStream Filter-Centric Sample

    Copyright (c) 1999 - 2001, Microsoft Corporation

    File:

        video.cpp

    Abstract:

        This file contains the video capture pin implementation.

    History:

        created 6/11/01

**************************************************************************/

#include "avssamp.h"

/**************************************************************************

    PAGEABLE CODE

**************************************************************************/

#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA

NTSTATUS
CVideoCapturePin::
DispatchCreate (
    IN PKSPIN Pin,
    IN PIRP Irp
    )

/*++

Routine Description:

    Create a new video capture pin.  This is the creation dispatch for
    the video capture pin.

Arguments:

    Pin -
        The pin being created

    Irp -
        The creation Irp

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;

    CVideoCapturePin *CapPin = new (NonPagedPool) CVideoCapturePin (Pin);
    CCapturePin *BasePin = static_cast <CCapturePin *> (CapPin);

    if (!CapPin) {
        //
        // Return failure if we couldn't create the pin.
        //
        Status = STATUS_INSUFFICIENT_RESOURCES;

    } else {
        //
        // Add the item to the object bag if we we were successful. 
        // Whenever the pin closes, the bag is cleaned up and we will be
        // freed.
        //
        Status = KsAddItemToObjectBag (
            Pin -> Bag,
            reinterpret_cast <PVOID> (BasePin),
            reinterpret_cast <PFNKSFREE> (CCapturePin::BagCleanup)
            );

        if (!NT_SUCCESS (Status)) {
            delete CapPin;
        } else {
            Pin -> Context = reinterpret_cast <PVOID> (BasePin);
        }

    }

    //
    // If we succeeded so far, stash the video info header away and change
    // our allocator framing to reflect the fact that only now do we know
    // the framing requirements based on the connection format.
    //
    PKS_VIDEOINFOHEADER VideoInfoHeader = NULL;

    if (NT_SUCCESS (Status)) {

        VideoInfoHeader = CapPin -> CaptureVideoInfoHeader ();
        if (!VideoInfoHeader) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS (Status)) {
        
        //
        // We need to edit the descriptor to ensure we don't mess up any other
        // pins using the descriptor or touch read-only memory.
        //
        Status = KsEdit (Pin, &Pin -> Descriptor, 'aChS');

        if (NT_SUCCESS (Status)) {
            Status = KsEdit (
                Pin, 
                &(Pin -> Descriptor -> AllocatorFraming),
                'aChS'
                );
        }

        //
        // If the edits proceeded without running out of memory, adjust 
        // the framing based on the video info header.
        //
        if (NT_SUCCESS (Status)) {

            //
            // We've KsEdit'ed this...  I'm safe to cast away constness as
            // long as the edit succeeded.
            //
            PKSALLOCATOR_FRAMING_EX Framing =
                const_cast <PKSALLOCATOR_FRAMING_EX> (
                    Pin -> Descriptor -> AllocatorFraming
                    );

            Framing -> FramingItem [0].Frames = 2;

            //
            // The physical and optimal ranges must be biSizeImage.  We only
            // support one frame size, precisely the size of each capture
            // image.
            //
            Framing -> FramingItem [0].PhysicalRange.MinFrameSize =
                Framing -> FramingItem [0].PhysicalRange.MaxFrameSize =
                Framing -> FramingItem [0].FramingRange.Range.MinFrameSize =
                Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize =
                VideoInfoHeader -> bmiHeader.biSizeImage;

            Framing -> FramingItem [0].PhysicalRange.Stepping = 
                Framing -> FramingItem [0].FramingRange.Range.Stepping =
                0;

        }

    }

    if (NT_SUCCESS (Status)) {
        //
        // Adjust the stream header size.  The video packets have extended
        // header info (KS_FRAME_INFO).
        //
        Pin -> StreamHeaderSize = sizeof (KSSTREAM_HEADER) +
            sizeof (KS_FRAME_INFO);

    }

    return Status;

}

/*************************************************/


PKS_VIDEOINFOHEADER 
CVideoCapturePin::
CaptureVideoInfoHeader (
    )

/*++

Routine Description:

    Capture the video info header out of the connection format.  This
    is what we use to base synthesized images off.

Arguments:

    None

Return Value:

    The captured video info header or NULL if there is insufficient
    memory.

--*/

{

    PAGED_CODE();

    PKS_VIDEOINFOHEADER ConnectionHeader =
        &((reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> 
            (m_Pin -> ConnectionFormat)) -> 
            VideoInfoHeader);

    m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> (
        ExAllocatePoolZero (
            NonPagedPool,
            KS_SIZE_VIDEOHEADER (ConnectionHeader),
            AVSSMP_POOLTAG
            )
        );

    if (!m_VideoInfoHeader)
        return NULL;

    //
    // Bag the newly allocated header space.  This will get cleaned up
    // automatically when the pin closes.
    //
    NTSTATUS Status =
        KsAddItemToObjectBag (
            m_Pin -> Bag,
            reinterpret_cast <PVOID> (m_VideoInfoHeader),
            NULL
            );

    if (!NT_SUCCESS (Status)) {

        ExFreePool (m_VideoInfoHeader);
        return NULL;

    } else {

        //
        // Copy the connection format video info header into the newly 
        // allocated "captured" video info header.
        //
        RtlCopyMemory (
            m_VideoInfoHeader,
            ConnectionHeader,
            KS_SIZE_VIDEOHEADER (ConnectionHeader)
            );

    }

    return m_VideoInfoHeader;

}

/*************************************************/


NTSTATUS
CVideoCapturePin::
IntersectHandler (
    IN PKSFILTER Filter,
    IN PIRP Irp,
    IN PKSP_PIN PinInstance,
    IN PKSDATARANGE CallerDataRange,
    IN PKSDATARANGE DescriptorDataRange,
    IN ULONG BufferSize,
    OUT PVOID Data OPTIONAL,
    OUT PULONG DataSize
    )

/*++

Routine Description:

    This routine handles video pin intersection queries by determining the
    intersection between two data ranges.

Arguments:

    Filter -
        Contains a void pointer to the  filter structure.

    Irp -
        Contains a pointer to the data intersection property request.

    PinInstance -
        Contains a pointer to a structure indicating the pin in question.

    CallerDataRange -
        Contains a pointer to one of the data ranges supplied by the client
        in the data intersection request.  The format type, subtype and
        specifier are compatible with the DescriptorDataRange.

    DescriptorDataRange -
        Contains a pointer to one of the data ranges from the pin descriptor
        for the pin in question.  The format type, subtype and specifier are
        compatible with the CallerDataRange.

    BufferSize -
        Contains the size in bytes of the buffer pointed to by the Data
        argument.  For size queries, this value will be zero.

    Data -
        Optionally contains a pointer to the buffer to contain the data 
        format structure representing the best format in the intersection 
        of the two data ranges.  For size queries, this pointer will be 
        NULL.

    DataSize -
        Contains a pointer to the location at which to deposit the size 
        of the data format.  This information is supplied by the function 
        when the format is actually delivered and in response to size 
        queries.

Return Value:

    STATUS_SUCCESS if there is an intersection and it fits in the supplied
    buffer, STATUS_BUFFER_OVERFLOW for successful size queries, 
    STATUS_NO_MATCH if the intersection is empty, or 
    STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small.

--*/

{
    PAGED_CODE();

    const GUID VideoInfoSpecifier = 
        {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)};
    
    NT_ASSERT(Filter);
    NT_ASSERT(Irp);
    NT_ASSERT(PinInstance);
    NT_ASSERT(CallerDataRange);
    NT_ASSERT(DescriptorDataRange);
    NT_ASSERT(DataSize);
    
    ULONG DataFormatSize;
    
    //
    // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER
    //
    if (IsEqualGUID(CallerDataRange->Specifier, VideoInfoSpecifier) &&
        CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_VIDEO)) {
            
        PKS_DATARANGE_VIDEO callerDataRange = 
            reinterpret_cast <PKS_DATARANGE_VIDEO> (CallerDataRange);

        PKS_DATARANGE_VIDEO descriptorDataRange = 
            reinterpret_cast <PKS_DATARANGE_VIDEO> (DescriptorDataRange);

        PKS_DATAFORMAT_VIDEOINFOHEADER FormatVideoInfoHeader;

        //
        // Check that the other fields match
        //
        if ((callerDataRange->bFixedSizeSamples != 
                descriptorDataRange->bFixedSizeSamples) ||
            (callerDataRange->bTemporalCompression != 
                descriptorDataRange->bTemporalCompression) ||
            (callerDataRange->StreamDescriptionFlags != 
                descriptorDataRange->StreamDescriptionFlags) ||
            (callerDataRange->MemoryAllocationFlags != 
                descriptorDataRange->MemoryAllocationFlags) ||
            (RtlCompareMemory (&callerDataRange->ConfigCaps,
                    &descriptorDataRange->ConfigCaps,
                    sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) != 
                    sizeof (KS_VIDEO_STREAM_CONFIG_CAPS))) 
        {
            return STATUS_NO_MATCH;
        }

        //
        // KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from
        // the caller's data range.  This **MUST** be validated; the
        // extended bmiHeader size (biSize) must not extend past the end
        // of the range buffer.  Possible arithmetic overflow is also
        // checked for.
        //
        {
            ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
                &callerDataRange->VideoInfoHeader
                );

            ULONG DataRangeSize = 
                FIELD_OFFSET (KS_DATARANGE_VIDEO, VideoInfoHeader) +
                VideoHeaderSize;

            //
            // Check that biSize does not extend past the buffer.  The 
            // first two checks are for arithmetic overflow on the 
            // operations to compute the alleged size.  (On unsigned
            // math, a+b < a iff an arithmetic overflow occurred).
            //
            if (
                VideoHeaderSize < callerDataRange->
                    VideoInfoHeader.bmiHeader.biSize ||
                DataRangeSize < VideoHeaderSize ||
                DataRangeSize > callerDataRange -> DataRange.FormatSize
                ) {

                return STATUS_INVALID_PARAMETER;

            }

        }

        DataFormatSize = 
            sizeof (KSDATAFORMAT) + 
            KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader);
            
        //
        // If the passed buffer size is 0, it indicates that this is a size
        // only query.  Return the size of the intersecting data format and
        // pass back STATUS_BUFFER_OVERFLOW.
        //
        if (BufferSize == 0) {

            *DataSize = DataFormatSize;
            return STATUS_BUFFER_OVERFLOW;

        }
        
        //
        // Verify that the provided structure is large enough to
        // accept the result.
        //
        if (BufferSize < DataFormatSize) 
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        //
        // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader
        //
        *DataSize = DataFormatSize;
            
        FormatVideoInfoHeader = PKS_DATAFORMAT_VIDEOINFOHEADER( Data );

        //
        // Copy over the KSDATAFORMAT.  This is precisely the same as the
        // KSDATARANGE (it's just the GUIDs, etc...  not the format information
        // following any data format.
        // 
        RtlCopyMemory (
            &FormatVideoInfoHeader->DataFormat, 
            DescriptorDataRange, 
            sizeof (KSDATAFORMAT));

        FormatVideoInfoHeader->DataFormat.FormatSize = DataFormatSize;

        //
        // Copy over the callers requested VIDEOINFOHEADER
        //

        RtlCopyMemory (
            &FormatVideoInfoHeader->VideoInfoHeader, 
            &callerDataRange->VideoInfoHeader,
            KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader) 
            );

        //
        // Calculate biSizeImage for this request, and put the result in both
        // the biSizeImage field of the bmiHeader AND in the SampleSize field
        // of the DataFormat.
        //
        // Note that for compressed sizes, this calculation will probably not
        // be just width * height * bitdepth
        //
        FormatVideoInfoHeader->VideoInfoHeader.bmiHeader.biSizeImage =
            FormatVideoInfoHeader->DataFormat.SampleSize = 
            KS_DIBSIZE (FormatVideoInfoHeader->VideoInfoHeader.bmiHeader);

        //
        // REVIEW - Perform other validation such as cropping and scaling checks
        // 
        
        return STATUS_SUCCESS;
        
    } // End of VIDEOINFOHEADER specifier
    
    return STATUS_NO_MATCH;
}

/*************************************************/

BOOL
MultiplyCheckOverflow (
    ULONG a,
    ULONG b,
    ULONG *pab
    )

/*++

Routine Description:

    Perform a 32 bit unsigned multiplication and check for arithmetic overflow.

Arguments:

    a -
        First operand

    b -
        Second operand

    pab -
        Result

Return Value:

    TRUE -
        no overflow

    FALSE -
        overflow occurred

--*/

{
    PAGED_CODE();

    *pab = a * b;
    if ((a == 0) || (((*pab) / a) == b)) {
        return TRUE;
    }
    return FALSE;
}

/*************************************************/


NTSTATUS
CVideoCapturePin::
DispatchSetFormat (
    IN PKSPIN Pin,
    IN PKSDATAFORMAT OldFormat OPTIONAL,
    IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL,
    IN const KSDATARANGE *DataRange,
    IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL
    )

/*++

Routine Description:

    This is the set data format dispatch for the capture pin.  It is called
    in two circumstances.

        1: before Pin's creation dispatch has been made to verify that
           Pin -> ConnectionFormat is an acceptable format for the range
           DataRange.  In this case OldFormat is NULL.

        2: after Pin's creation dispatch has been made and an initial format
           selected in order to change the format for the pin.  In this case,
           OldFormat will not be NULL.

    Validate that the format is acceptable and perform the actions necessary
    to change format if appropriate.

Arguments:

    Pin -
        The pin this format is being set on.  The format itself will be in
        Pin -> ConnectionFormat.

    OldFormat -
        The previous format used on this pin.  If this is NULL, it is an
        indication that Pin's creation dispatch has not yet been made and
        that this is a request to validate the initial format and not to
        change formats.

    OldAttributeList -
        The old attribute list for the prior format

    DataRange -
        A range out of our list of data ranges which was determined to be
        at least a partial match for Pin -> ConnectionFormat.  If the format
        there is unacceptable for the range, STATUS_NO_MATCH should be
        returned.

    AttributeRange -
        The attribute range

Return Value:

    Success / Failure

        STATUS_SUCCESS -
            The format is acceptable / the format has been changed

        STATUS_NO_MATCH -
            The format is not-acceptable / the format has not been changed

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_NO_MATCH;

    const GUID VideoInfoSpecifier = 
        {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)};

    CCapturePin *CapPin = NULL;
    CVideoCapturePin *VidCapPin = NULL;

    //
    // Find the pin, if it exists yet.  OldFormat will be an indication of 
    // this.  If we're changing formats, OldFormat will be non-NULL.
    //
    // You cannot use Pin -> Context to make the determination.  AVStream
    // preinitializes this to the filter's context.
    //
    if (OldFormat) {
        CapPin = reinterpret_cast <CCapturePin *> (Pin -> Context);

        //
        // We know this pin happens to be the video capture pin.  Downcast it.
        //
        VidCapPin = static_cast <CVideoCapturePin *> (CapPin);
    }

    if (IsEqualGUID (Pin -> ConnectionFormat -> Specifier,
            VideoInfoSpecifier) &&
        Pin -> ConnectionFormat -> FormatSize >= 
            sizeof (KS_DATAFORMAT_VIDEOINFOHEADER)
        ) {

        PKS_DATAFORMAT_VIDEOINFOHEADER ConnectionFormat =
            reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> 
                (Pin -> ConnectionFormat);

        //
        // DataRange comes out of OUR data range list.  I know the range
        // is valid as such.
        //
        const KS_DATARANGE_VIDEO *VIRange =
            reinterpret_cast <const KS_DATARANGE_VIDEO *>
                (DataRange);

        //
        // Check that bmiHeader.biSize is valid since we use it later.
        //
        ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
            &ConnectionFormat -> VideoInfoHeader
            );

        ULONG DataFormatSize = FIELD_OFFSET (
            KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader
            ) + VideoHeaderSize;

        if (
            VideoHeaderSize < ConnectionFormat->
                VideoInfoHeader.bmiHeader.biSize ||
            DataFormatSize < VideoHeaderSize ||
            DataFormatSize > ConnectionFormat -> DataFormat.FormatSize
            ) {

            Status = STATUS_INVALID_PARAMETER;

        }

        //
        // Check that the format is a match for the selected range. 
        //
        else if (
            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biWidth !=
                VIRange -> VideoInfoHeader.bmiHeader.biWidth) ||

            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biHeight !=
                VIRange -> VideoInfoHeader.bmiHeader.biHeight) ||

            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biCompression !=
                VIRange -> VideoInfoHeader.bmiHeader.biCompression)
           ) {

            Status = STATUS_NO_MATCH;

        } else {

            //
            // Compute the minimum size of our buffers to validate against.
            // The image synthesis routines synthesize |biHeight| rows of
            // biWidth pixels in either RGB24 or UYVY.  In order to ensure
            // safe synthesis into the buffer, we need to know how large an
            // image this will produce.
            //
            // I do this explicitly because of the method that the data is
            // synthesized.  A variation of this may or may not be necessary
            // depending on the mechanism the driver in question fills the 
            // capture buffers.  The important thing is to ensure that they
            // aren't overrun during capture.
            //
            ULONG ImageSize;

            if (!MultiplyCheckOverflow (
                (ULONG)ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth,
                (ULONG)abs (ConnectionFormat->
                    VideoInfoHeader.bmiHeader.biHeight),
                &ImageSize
                )) {

                Status = STATUS_INVALID_PARAMETER;
            }

            //
            // We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so
            // this is valid for those formats.
            //
            else if (!MultiplyCheckOverflow (
                ImageSize,
                (ULONG)(ConnectionFormat->
                    VideoInfoHeader.bmiHeader.biBitCount / 8),
                &ImageSize
                )) {

                Status = STATUS_INVALID_PARAMETER;

            }

            //
            // Valid for the formats we use.  Otherwise, this would be
            // checked later.
            //
            else if (ConnectionFormat->VideoInfoHeader.bmiHeader.biSizeImage <
                    ImageSize) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                //
                // We can accept the format. 
                //
                Status = STATUS_SUCCESS;
    
                //
                // OldFormat is an indication that this is a format change.  
                // Since I do not implement the 
                // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do 
                // not handle dynamic format changes.
                //
                // If something changes while we're in the stop state, we're 
                // fine to handle it since we haven't "configured the hardware"
                // yet.
                //
                if (OldFormat) {
                    //
                    // If we're in the stop state, we can handle just about any
                    // change.  We don't support dynamic format changes. 
                    //
                    if (Pin -> DeviceState == KSSTATE_STOP) {
                        if (!VidCapPin -> CaptureVideoInfoHeader ()) {
                            Status = STATUS_INSUFFICIENT_RESOURCES;
                        }
                    } else {
                        //
                        // Because we don't accept dynamic format changes, we
                        // should never get here.  Just being over-protective.
                        //
                        Status = STATUS_INVALID_DEVICE_STATE;
                    }
    
                }
    
            }
    
        }

    }
    
    return Status;

}

/*************************************************/


NTSTATUS
CVideoCapturePin::
Pause (
    IN KSSTATE FromState
    )

/*++

Routine Description:

    Called when the pin transitions into the pause state.  If we're in an 
    upward transition, start the capture DPC.  Note that we do not actually
    trigger capture in the pause state, but we start up our DPC.

Arguments:

    FromState -
        The state that the pin is transitioning away from.  This is either
        KSSTATE_ACQUIRE, indicating an upward transition, or KSSTATE_RUN,
        indicating a downward transition.

Return Value:

    STATUS_SUCCESS

--*/

{

    PAGED_CODE();

    //
    // On the transition from acquire -> pause, start the timer DPC running.
    //
    if (FromState == KSSTATE_ACQUIRE) {
        m_ParentFilter -> StartDPC (m_VideoInfoHeader -> AvgTimePerFrame);
    }

    return STATUS_SUCCESS;

}

/*************************************************/


NTSTATUS
CVideoCapturePin::
Acquire (
    IN KSSTATE FromState
    )

/*++

Routine Description:

    This is called from the base class when the video capture pin transitions
    into the acquire state (from either Stop or Pause).  The state the pin
    transitioned from is passed in.

    During this phase, the video capture pin creates the image synthesizer
    and initializes it.

Arguments:

    FromState -
        The state transitioning from (KSSTATE_STOP or KSSTATE_PAUSE)

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    NT_ASSERT (m_VideoInfoHeader);

    NTSTATUS Status = STATUS_SUCCESS;

    if (FromState == KSSTATE_STOP) {

        m_SynthesisBuffer = reinterpret_cast <PUCHAR> (
            ExAllocatePoolZero (
                NonPagedPool, 
                m_VideoInfoHeader -> bmiHeader.biSizeImage,
                AVSSMP_POOLTAG
                )
            );

        if (!m_SynthesisBuffer) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {
            //
            // Determine the necessary type of image synthesizer to create 
            // based on the format that has been set on this pin.
            //
            if (m_VideoInfoHeader -> bmiHeader.biBitCount == 24 &&
                m_VideoInfoHeader -> bmiHeader.biCompression == KS_BI_RGB) {
        
                //
                // If we're RGB24, create a new RGB24 synth.  RGB24 surfaces
                // can be in either orientation.  The origin is lower left if
                // height < 0.  Otherwise, it's upper left.
                //
                m_ImageSynth = new (NonPagedPool, 'RysI')
                    CRGB24Synthesizer (
                        m_VideoInfoHeader -> bmiHeader.biHeight >= 0,
                        m_VideoInfoHeader -> bmiHeader.biWidth,
                        ABS (m_VideoInfoHeader -> bmiHeader.biHeight)
                        );
        
            } else
            if (m_VideoInfoHeader -> bmiHeader.biBitCount == 16 &&
                m_VideoInfoHeader -> bmiHeader.biCompression == FOURCC_YUV422) {
        
                //
                // If we're UYVY, create the YUV synth.
                //
                m_ImageSynth = new (NonPagedPool, 'YysI') CYUVSynthesizer (
                    m_VideoInfoHeader -> bmiHeader.biWidth,
                    m_VideoInfoHeader -> bmiHeader.biHeight
                    );
        
            } else
                //
                // We don't synthesize anything but RGB 24 and UYVY.
                //
                Status = STATUS_INVALID_PARAMETER;
        
            if (NT_SUCCESS (Status) && !m_ImageSynth) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }

        }
    
        //
        // Bag the image synthesizer.
        //
        if (NT_SUCCESS (Status)) {
            
            Status = KsAddItemToObjectBag (
                m_Pin -> Bag,
                m_ImageSynth,
                reinterpret_cast <PFNKSFREE> (CVideoCapturePin::CleanupSynth)
                );
    
        }

        //
        // If everything is okay at this point, inform the synthesizer of
        // the scratch buffer.
        //
        if (NT_SUCCESS (Status)) {
            m_ImageSynth -> SetBuffer (m_SynthesisBuffer);
        }

    } else {

        //
        // The only other state we can come from is pause.  If we're in a
        // downward state transition below pause, tell the filter to stop the
        // capture DPC.
        //
        m_ParentFilter -> StopDPC ();

    }

    return Status;

}

/*************************************************/


NTSTATUS
CVideoCapturePin::
Stop (
    IN KSSTATE FromState
    )

/*++

Routine Description:

    Called when the video capture pin transitions from acquire to stop.
    This function will clean up the image synth and any data structures
    that we need to clean up on stop.

Arguments:

    FromState -
        The state the pin is transitioning away from.  This should
        always be KSSTATE_ACQUIRE for this call.

Return Value:

    STATUS_SUCCESS

--*/

{
    PAGED_CODE();

    NT_ASSERT (FromState == KSSTATE_ACQUIRE);

    //
    // Remove the image synthesizer from the object bag and free it.
    //
    KsRemoveItemFromObjectBag (
        m_Pin -> Bag,
        m_ImageSynth,
        TRUE
        );

    m_ImageSynth = NULL;

    if (m_SynthesisBuffer) {
        ExFreePool (m_SynthesisBuffer);
        m_SynthesisBuffer = NULL;
    }

    return STATUS_SUCCESS;

}

/**************************************************************************

    LOCKED CODE

**************************************************************************/

#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA


NTSTATUS
CVideoCapturePin::
CaptureFrame (
    IN PKSPROCESSPIN ProcessPin,
    IN ULONG Tick
    )

/*++

Routine Description:

    This routine is called from the filter processing function to capture
    a frame for the video capture pin.  The process pin to capture to is
    passed.

Arguments:

    ProcessPin -
        The process pin associated with this pin.

    Tick -
        The tick count on the filter.  This is the number of timer DPC's that
        have fired since the timer DPC started.

Return Value:

    STATUS_SUCCESS

--*/

{

    NT_ASSERT (ProcessPin -> Pin == m_Pin);

    //
    // Increment the frame number.  This is the total count of frames which
    // have attempted capture.
    //
    m_FrameNumber++;

    //
    // Since this pin is KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING, it
    // means that we do not require frames available in order to process.
    // This means that this routine can get called from our DPC with no
    // buffers available to capture into.  In this case, we increment our
    // dropped frame counter and do nothing.
    //
    if (ProcessPin -> BytesAvailable) {

        //
        // Because we adjusted the allocator framing, each frame should be
        // sufficient to trigger capture of the appropriate buffer size.
        //
        NT_ASSERT (ProcessPin -> BytesAvailable >= 
            m_VideoInfoHeader -> bmiHeader.biSizeImage);

        //
        // If we get an invalid buffer, kick it out.
        //
        if (ProcessPin -> BytesAvailable < 
            m_VideoInfoHeader -> bmiHeader.biSizeImage) {

            ProcessPin -> BytesUsed = 0;
            ProcessPin -> Terminate = TRUE;
            m_DroppedFrames++;
            return STATUS_SUCCESS;
        }

        //
        // Generate a synthesized image.
        //
        m_ImageSynth -> SynthesizeBars ();

        //
        // Overlay some activity onto the bars.
        //
        ULONG DropLength = (Tick * 2) % 
            (ABS (m_VideoInfoHeader -> bmiHeader.biHeight));
    
        //
        // Create a drop flowing down DropLength lines from the top of the 
        // image.
        //
        m_ImageSynth -> Fill (
            0, 0, 
            m_VideoInfoHeader -> bmiHeader.biWidth - 1, DropLength, 
            GREEN
            );

        //
        // Overlay the dropped frame count over the image.
        //
        char Text [256];
        Text[0] = '\0';
        RtlStringCbPrintfA(Text, sizeof(Text), "Video Skipped: %ld", m_DroppedFrames);

        m_ImageSynth -> OverlayText (
            10,
            10,
            1,
            Text,
            TRANSPARENT,
            BLUE
            );

        //
        // This is used to indicate that there is no audio pin.
        //
        if (m_NotifyAudDrop != (ULONG)-1) {
            RtlStringCbPrintfA(Text, sizeof(Text), "Audio Skipped: %ld", m_NotifyAudDrop);

            m_ImageSynth -> OverlayText (
                10,
                20,
                1,
                Text,
                TRANSPARENT,
                BLUE
                );
        }

        //
        // Copy the synthesized image into the buffer.
        //
        RtlCopyMemory (
            ProcessPin -> Data,
            m_SynthesisBuffer,
            m_VideoInfoHeader -> bmiHeader.biSizeImage
            );
        
        ProcessPin -> BytesUsed = m_VideoInfoHeader -> bmiHeader.biSizeImage;
        ProcessPin -> Terminate = TRUE;


        PKSSTREAM_HEADER StreamHeader = 
            ProcessPin -> StreamPointer -> StreamHeader;

        //
        // If there is a clock assigned to the pin, time stamp the sample.
        //
        if (m_Clock) {

            StreamHeader -> PresentationTime.Time = GetTime ();
            StreamHeader -> Duration = m_VideoInfoHeader -> AvgTimePerFrame;

            StreamHeader -> OptionsFlags =
                KSSTREAM_HEADER_OPTIONSF_TIMEVALID |
                KSSTREAM_HEADER_OPTIONSF_DURATIONVALID;

        }

        //
        // Update the extended header info.
        //
        NT_ASSERT (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) +
            sizeof (KS_FRAME_INFO));

        //
        // Double check the Stream Header size.  AVStream makes no guarantee
        // that because StreamHeaderSize is set to a specific size that you
        // will get that size.  If the proper data type handlers are not 
        // installed, the stream header will be of default size.
        //
        if (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) +
            sizeof (KS_FRAME_INFO)) {

            PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (
                StreamHeader + 1
                );
    
            FrameInfo -> ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
            FrameInfo -> PictureNumber = (LONGLONG)m_FrameNumber;
            FrameInfo -> DropCount = (LONGLONG)m_DroppedFrames;

        }
    
    } else {
        m_DroppedFrames++;
    }

    return STATUS_SUCCESS;
        
}

/**************************************************************************

    DESCRIPTOR AND DISPATCH LAYOUT

**************************************************************************/

#define D_X 320
#define D_Y 240

//
// FormatRGB24Bpp_Capture:
//
// This is the data range description of the RGB24 capture format we support.
//
const 
KS_DATARANGE_VIDEO 
FormatRGB24Bpp_Capture = {

    //
    // KSDATARANGE
    //
    {   
        sizeof (KS_DATARANGE_VIDEO),                // FormatSize
        0,                                          // Flags
        D_X * D_Y * 3,                              // SampleSize
        0,                                          // Reserved

        STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO),     // aka. MEDIATYPE_Video
        0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 
            0xaf, 0x0b, 0xa7, 0x70,                 // aka. MEDIASUBTYPE_RGB24,
        STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo
    },

    TRUE,               // BOOL,  bFixedSizeSamples (all samples same size?)
    TRUE,               // BOOL,  bTemporalCompression (all I frames?)
    0,                  // Reserved (was StreamDescriptionFlags)
    0,                  // Reserved (was MemoryAllocationFlags   
                        //           (KS_VIDEO_ALLOC_*))

    //
    // _KS_VIDEO_STREAM_CONFIG_CAPS  
    //
    {
        STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID
        KS_AnalogVideo_NTSC_M |
        KS_AnalogVideo_PAL_B,                    // AnalogVideoStandard
        720,480,        // InputSize, (the inherent size of the incoming signal
                        //             with every digitized pixel unique)
        160,120,        // MinCroppingSize, smallest rcSrc cropping rect allowed
        720,480,        // MaxCroppingSize, largest  rcSrc cropping rect allowed
        8,              // CropGranularityX, granularity of cropping size
        1,              // CropGranularityY
        8,              // CropAlignX, alignment of cropping rect 
        1,              // CropAlignY;
        160, 120,       // MinOutputSize, smallest bitmap stream can produce
        720, 480,       // MaxOutputSize, largest  bitmap stream can produce
        8,              // OutputGranularityX, granularity of output bitmap size
        1,              // OutputGranularityY;
        0,              // StretchTapsX  (0 no stretch, 1 pix dup, 2 interp...)
        0,              // StretchTapsY
        0,              // ShrinkTapsX 
        0,              // ShrinkTapsY 
        333667,         // MinFrameInterval, 100 nS units
        640000000,      // MaxFrameInterval, 100 nS units
        8 * 3 * 30 * 160 * 120,  // MinBitsPerSecond;
        8 * 3 * 30 * 720 * 480   // MaxBitsPerSecond;
    }, 
        
    //
    // KS_VIDEOINFOHEADER (default format)
    //
    {
        0,0,0,0,                            // RECT  rcSource; 
        0,0,0,0,                            // RECT  rcTarget; 
        D_X * D_Y * 3 * 30,                 // DWORD dwBitRate;
        0L,                                 // DWORD dwBitErrorRate; 
        333667,                             // REFERENCE_TIME  AvgTimePerFrame;   
        sizeof (KS_BITMAPINFOHEADER),       // DWORD biSize;
        D_X,                                // LONG  biWidth;
        -D_Y,                               // LONG  biHeight;
        1,                                  // WORD  biPlanes;
        24,                                 // WORD  biBitCount;
        KS_BI_RGB,                          // DWORD biCompression;
        D_X * D_Y * 3,                      // DWORD biSizeImage;
        0,                                  // LONG  biXPelsPerMeter;
        0,                                  // LONG  biYPelsPerMeter;
        0,                                  // DWORD biClrUsed;
        0                                   // DWORD biClrImportant;
    }
}; 

#undef D_X
#undef D_Y

#define D_X 320
#define D_Y 240

//
// FormatUYU2_Capture:
//
// This is the data range description of the UYVY format we support.
//
const 
KS_DATARANGE_VIDEO 
FormatUYU2_Capture = {

    //
    // KSDATARANGE
    //
    {   
        sizeof (KS_DATARANGE_VIDEO),            // FormatSize
        0,                                      // Flags
        D_X * D_Y * 2,                          // SampleSize
        0,                                      // Reserved
        STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video
        0x59565955, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 
            0x00, 0x38, 0x9b, 0x71,             // aka. MEDIASUBTYPE_UYVY,
        STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo
    },

    TRUE,               // BOOL,  bFixedSizeSamples (all samples same size?)
    TRUE,               // BOOL,  bTemporalCompression (all I frames?)
    0,                  // Reserved (was StreamDescriptionFlags)
    0,                  // Reserved (was MemoryAllocationFlags   
                        //           (KS_VIDEO_ALLOC_*))

    //
    // _KS_VIDEO_STREAM_CONFIG_CAPS  
    //
    {
        STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID
        KS_AnalogVideo_NTSC_M |
        KS_AnalogVideo_PAL_B,                    // AnalogVideoStandard
        720,480,        // InputSize, (the inherent size of the incoming signal
                    //             with every digitized pixel unique)
        160,120,        // MinCroppingSize, smallest rcSrc cropping rect allowed
        720,480,        // MaxCroppingSize, largest  rcSrc cropping rect allowed
        8,              // CropGranularityX, granularity of cropping size
        1,              // CropGranularityY
        8,              // CropAlignX, alignment of cropping rect 
        1,              // CropAlignY;
        160, 120,       // MinOutputSize, smallest bitmap stream can produce
        720, 480,       // MaxOutputSize, largest  bitmap stream can produce
        8,              // OutputGranularityX, granularity of output bitmap size
        1,              // OutputGranularityY;
        0,              // StretchTapsX  (0 no stretch, 1 pix dup, 2 interp...)
        0,              // StretchTapsY
        0,              // ShrinkTapsX 
        0,              // ShrinkTapsY 
        333667,         // MinFrameInterval, 100 nS units
        640000000,      // MaxFrameInterval, 100 nS units
        8 * 2 * 30 * 160 * 120,  // MinBitsPerSecond;
        8 * 2 * 30 * 720 * 480   // MaxBitsPerSecond;
    }, 
        
    //
    // KS_VIDEOINFOHEADER (default format)
    //
    {
        0,0,0,0,                            // RECT  rcSource; 
        0,0,0,0,                            // RECT  rcTarget; 
        D_X * D_Y * 2 * 30,                 // DWORD dwBitRate;
        0L,                                 // DWORD dwBitErrorRate; 
        333667,                             // REFERENCE_TIME  AvgTimePerFrame;   
        sizeof (KS_BITMAPINFOHEADER),       // DWORD biSize;
        D_X,                                // LONG  biWidth;
        D_Y,                                // LONG  biHeight;
        1,                                  // WORD  biPlanes;
        16,                                 // WORD  biBitCount;
        FOURCC_YUV422,                      // DWORD biCompression;
        D_X * D_Y * 2,                      // DWORD biSizeImage;
        0,                                  // LONG  biXPelsPerMeter;
        0,                                  // LONG  biYPelsPerMeter;
        0,                                  // DWORD biClrUsed;
        0                                   // DWORD biClrImportant;
    }
}; 

//
// VideoCapturePinDispatch:
//
// This is the dispatch table for the capture pin.  It provides notifications
// about creation, closure, processing, data formats, etc...
//
const
KSPIN_DISPATCH
VideoCapturePinDispatch = {
    CVideoCapturePin::DispatchCreate,       // Pin Create
    NULL,                                   // Pin Close
    NULL,                                   // Pin Process
    NULL,                                   // Pin Reset
    CVideoCapturePin::DispatchSetFormat,    // Pin Set Data Format
    CCapturePin::DispatchSetState,          // Pin Set Device State
    NULL,                                   // Pin Connect
    NULL,                                   // Pin Disconnect
    NULL,                                   // Clock Dispatch
    NULL                                    // Allocator Dispatch
};

//
// VideoCapturePinAllocatorFraming:
//
// This is the simple framing structure for the capture pin.  Note that this
// will be modified via KsEdit when the actual capture format is determined.
//
DECLARE_SIMPLE_FRAMING_EX (
    VideoCapturePinAllocatorFraming,
    STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED),
    KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
        KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY,
    2,
    0,
    2 * PAGE_SIZE,
    2 * PAGE_SIZE
    );

//
// VideoCapturePinDataRanges:
//
// This is the list of data ranges supported on the capture pin.  We support
// two: one RGB24, and one UYVY.
//
const 
PKSDATARANGE 
VideoCapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = {
    (PKSDATARANGE) &FormatRGB24Bpp_Capture,
    (PKSDATARANGE) &FormatUYU2_Capture
    };



<|start_filename|>general/SimpleMediaSource/MediaSource/pch.cpp<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//

// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"

// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

namespace winrt
{
    template<> bool is_guid_of<IMFMediaSourceEx>(guid const& id) noexcept
    {
        return is_guid_of<IMFMediaSourceEx, IMFMediaSource, IMFMediaEventGenerator>(id);
    }

    template<> bool is_guid_of<IMFMediaStream2>(guid const& id) noexcept
    {
        return is_guid_of<IMFMediaStream2, IMFMediaStream, IMFMediaEventGenerator>(id);
    }

    template<> bool is_guid_of<IMFActivate>(guid const& id) noexcept
    {
        return is_guid_of<IMFActivate, IMFAttributes>(id);
    }
}

<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXNotificationFrameworkTests.h<|end_filename|>
#include <stdafx.h>
#include <memory>
#include <wil\resultmacros.h>
#include <wil\com.h>
#include <audioclient.h>
#include <audioclientp.h>
#include <audiosessiontypesp.h>
#include <mmdeviceapi.h>
#include "etwlistener.h"
#include <functiondiscoverykeys.h>

#include <wil\resultmacros.h>
#include <wil\com.h>
#include <audioclient.h>
#include <audioclientp.h>
#include <audiosessiontypesp.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include "etwlistener.h"
#include <functiondiscoverykeys.h>
#include <slpublic.h>

#include <DevPKey.h>
#include <AudioEngineBaseAPOP.h>
#include "TestMediaType.h"
#include <audioenginebaseapo.h>

#include <WexTestClass.h>

#include <list>
#include <memory>

#include "CAPXHelper.h"

#include <initguid.h>
#include <guiddef.h>
DEFINE_GUID(CLSID_TestCapxAPO, 0xA43110A0,0x4D2B,0x4C5B,0x99,0xB0,0xC6,0xF8,0x62,0x08,0x28,0xB1);

using namespace std;
using namespace wil;
using namespace WEX::Logging;
using namespace WEX::Common;

#define POLICY_KERNEL_ONECORE_DEVICE_FAMILY_ID      L"Kernel-OneCore-DeviceFamilyID"

class CAPONotificationFrameworkTest
{
    CAPONotificationFrameworkTest(){};
    ~CAPONotificationFrameworkTest(){};

    BEGIN_TEST_CLASS(CAPONotificationFrameworkTest)
        START_APPVERIFIFER
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioenginebaseapop.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"Elevated")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
        END_APPVERIFIER
    END_TEST_CLASS()

public:
    TEST_METHOD_SETUP(setUpMethod);
    TEST_METHOD_CLEANUP(tearDownMethod);

protected:
    BEGIN_TEST_METHOD(NotificationFrameworkTest)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"30F6BEE7-2AC9-4434-B1AD-642055B2E871")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"C53BE546-1188-4167-B426-E291524BCFD9")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO CAPX - Test the Notification Framework - NotificationFrameworkTest")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"30")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"60")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party CAPX APO Test: NotificationFrameworkTest")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

private:
    DWORD m_dwPlatform = DEVICEFAMILYINFOENUM_UAP;
    std::vector<wil::com_ptr<IMMDevice>> m_capxEndpoints;
};


<|start_filename|>network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.cpp<|end_filename|>
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   Copyright (c) 2014 Microsoft Corporation.  All Rights Reserved.
//
//   Module Name:
//      Framework_WFPSamplerCalloutDriver.cpp
//
//   Abstract:
//
//   Author:
//      <NAME>      (DHarper)
//
//   Revision History:
//
//      [ Month ][Day] [Year] - [Revision]-[ Comments ]
//      May       01,   2010  -     1.0   -  Creation
//      December  13,   2013  -     1.1   -  Add support for multiple injectors and redirectors, and 
//                                              add support for serializing asynchronous 
//                                              FWPM_LAYER_STREAM injections
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Framework_WFPSamplerCalloutDriver.h"   /// .
#include "Framework_Include.h"                   /// .
#include "Framework_WFPSamplerCalloutDriver.tmh" /// $(OBJ_PATH)\$(O)\

extern "C"
{
   _IRQL_requires_(PASSIVE_LEVEL)
   _IRQL_requires_same_
   _Function_class_(DRIVER_INITIALIZE)
   DRIVER_INITIALIZE DriverEntry;
};

PDEVICE_OBJECT         g_pWDMDevice                 = 0;
PIO_WORKITEM           g_pPowerStateEnterIOWorkItem = 0;
PIO_WORKITEM           g_pPowerStateExitIOWorkItem  = 0;
NDIS_POOL_DATA*        g_pNDISPoolData              = 0;
BOOLEAN                g_calloutsRegistered         = FALSE;
HANDLE                 g_bfeSubscriptionHandle      = 0;
SERIALIZATION_LIST     g_bsiSerializationList       = {0};
WFPSAMPLER_DEVICE_DATA g_WFPSamplerDeviceData       = {0};
DEVICE_EXTENSION       g_deviceExtension            = {0};
KSPIN_LOCK             g_bpeSpinLock;
WDFDRIVER              g_WDFDriver;
WDFDEVICE              g_WDFDevice;

/**
 @private_function="PrvBasicStreamInjectionSerializationListInitialization"

   Purpose:  Initialize Stream Injection's global serailization list.                           <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552160.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551171.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF547799.aspx             <br>
*/
_Success_(return == STATUS_SUCCESS)
NTSTATUS PrvBasicStreamInjectionSerializationListInitialization()
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PrvBasicStreamInjectionSerializationListInitialization()\n");

#endif /// DBG

   NTSTATUS status = STATUS_SUCCESS;

   KeInitializeSpinLock(&(g_bsiSerializationList.spinLock));

   status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES,
                              &(g_bsiSerializationList.waitLock));
   HLPR_BAIL_ON_FAILURE(status);

   InitializeListHead(&(g_bsiSerializationList.listHead));

   g_bsiSerializationList.numEntries = 0;

   HLPR_BAIL_LABEL:

#if DBG

      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_INFO_LEVEL,
                 " <--- PrvBasicStreamInjectionSerializationListInitialization() [status: %#x]",
                 status);

#endif /// DBG

   return status;
}

/**
 @private_function="PrvFwpmBfeStateSubscribeChanges"

   Purpose:  Subscribe to notifications of changes to the BFE service's state.                  <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550062.aspx             <br>
*/
_Success_(return == STATUS_SUCCESS)
NTSTATUS PrvFwpmBfeStateSubscribeChanges()
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PrvFwpmBfeStateSubscribeChanges()\n");

#endif /// DBG

   NTSTATUS status = STATUS_SUCCESS;

   status = FwpmBfeStateSubscribeChanges(g_pWDMDevice,
                                         SubscriptionBFEStateChangeCallback,
                                         &g_WFPSamplerDeviceData,
                                         &g_bfeSubscriptionHandle);
   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PrvFwpmBfeStateSubscribeChanges : FwpmBfeStateSubscribeChanges() [status: %#x]\n",
                 status);

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PrvFwpmBfeStateSubscribeChanges() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PrvWFPSamplerDeviceDataPopulate"
 
   Purpose:  Register the  global WFP objects such as:                                          <br>
                Callouts                                                                        <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref:                                                                                    <br>
*/
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
_Check_return_
_Success_(return == STATUS_SUCCESS)
NTSTATUS PrvWFPSamplerFwpObjectsAddGlobal(_In_ WFPSAMPLER_DEVICE_DATA* pDeviceData)
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PrvWFPSamplerFwpObjectsAddGlobal()\n");

#endif /// DBG
   
   UNREFERENCED_PARAMETER(pDeviceData);

   NT_ASSERT(pDeviceData);

   NTSTATUS status = STATUS_SUCCESS;

   if(g_calloutsRegistered == FALSE)
   {
      status = KrnlHlprExposedCalloutsRegister();
      HLPR_BAIL_ON_FAILURE(status);

      g_calloutsRegistered = TRUE;
   }

   HLPR_BAIL_LABEL:

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PrvWFPSamplerFwpObjectsAddGlobal() [status: %#x]\n",
              status);

#endif /// DBG
   
   return status;
}

/**
 @private_function="PrvWFPSamplerDeviceDataPopulate"
 
   Purpose:  Populate the deviceData with the BFE engineHandle and various injection handles.   <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550061.aspx             <br>
*/
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
_Check_return_
_Success_(return == STATUS_SUCCESS)
NTSTATUS PrvWFPSamplerDeviceDataPopulate(_Inout_ WFPSAMPLER_DEVICE_DATA* pDeviceData)
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PrvWFPSamplerDeviceDataPopulate()\n");

#endif /// DBG
   
   NT_ASSERT(pDeviceData);

   NTSTATUS status = STATUS_SUCCESS;

   if(FwpmBfeStateGet() == FWPM_SERVICE_RUNNING)
   {
      status = KrnlHlprFwpmSessionCreateEngineHandle(&(pDeviceData->pEngineHandle));
      HLPR_BAIL_ON_FAILURE(status);
   }

   /// There are injection handles for each of the supported sublayers that allow injection.  This 
   /// allows us to do multiple injectors in the layer provided that the callouts are in different
   /// sublayers.
   for(UINT32 index = 0;
       index < MAX_INDEX;
       index++)
   {

#pragma warning(push)
#pragma warning(disable: 6388) /// all injection Handles will be 0 initially

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv4InboundInjectionHandles[index]),
                                                 AF_INET,
                                                 FWP_DIRECTION_INBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv4OutboundInjectionHandles[index]),
                                                 AF_INET,
                                                 FWP_DIRECTION_OUTBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv6InboundInjectionHandles[index]),
                                                 AF_INET6,
                                                 FWP_DIRECTION_INBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv6OutboundInjectionHandles[index]),
                                                 AF_INET6,
                                                 FWP_DIRECTION_OUTBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppInboundInjectionHandles[index]),
                                                 AF_UNSPEC,
                                                 FWP_DIRECTION_INBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

      status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppOutboundInjectionHandles[index]),
                                                 AF_UNSPEC,
                                                 FWP_DIRECTION_OUTBOUND,
                                                 index);
      HLPR_BAIL_ON_FAILURE(status);

#if(NTDDI_VERSION >= NTDDI_WIN8)

      pDeviceData->ppRedirectionHandles[index] = &g_pRedirectionHandles[index];

      status = KrnlHlprFwpsRedirectHandleCreate(pDeviceData->ppRedirectionHandles[index]);
      HLPR_BAIL_ON_FAILURE(status);

#endif /// (NTDDI_VERSION >= NTDDI_WIN8)

#pragma warning(pop)

   }

   HLPR_BAIL_LABEL:

   if(status != STATUS_SUCCESS)
   {
      for(UINT32 index = 0;
          index < MAX_INDEX;
          index++)
      {

#if(NTDDI_VERSION >= NTDDI_WIN8)

         if(pDeviceData->ppRedirectionHandles[index])
            KrnlHlprFwpsRedirectHandleDestroy(pDeviceData->ppRedirectionHandles[index]);

#endif /// (NTDDI_VERSION >= NTDDI_WIN8)


         if(pDeviceData->ppOutboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppOutboundInjectionHandles[index]));

         if(pDeviceData->ppInboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppInboundInjectionHandles[index]));

         if(pDeviceData->ppIPv6OutboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv6OutboundInjectionHandles[index]));

         if(pDeviceData->ppIPv6InboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv6InboundInjectionHandles[index]));

         if(pDeviceData->ppIPv4OutboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv4OutboundInjectionHandles[index]));

         if(pDeviceData->ppIPv4InboundInjectionHandles[index])
            KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv4InboundInjectionHandles[index]));
      }

      if(pDeviceData->pEngineHandle &&
         *(pDeviceData->pEngineHandle))
         KrnlHlprFwpmSessionReleaseHandle(pDeviceData->pEngineHandle);

   }

#if DBG

      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_INFO_LEVEL,
                 " <--- PrvWFPSamplerDeviceDataPopulate()\n");
   
#endif /// DBG

   return status;
}

/**
 @private_function="PrvDriverDeviceAdd"
 
   Purpose:  Add the device and configure initial WFP callout driver settings.                  <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545841.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546090.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545926.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546942.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545854.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546050.aspx             <br>
*/
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
_Check_return_
_Success_(return == STATUS_SUCCESS)
NTSTATUS PrvDriverDeviceAdd(_In_ WDFDRIVER* pWDFDriver)
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PrvDriverDeviceAdd()\n");

#endif /// DBG
   
   NT_ASSERT(pWDFDriver);

   NTSTATUS              status         = STATUS_SUCCESS;
   PWDFDEVICE_INIT       pWDFDeviceInit = 0;
   WDF_OBJECT_ATTRIBUTES attributes     = {0};

   WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

   attributes.EvtCleanupCallback = EventCleanupDeviceObject;

   pWDFDeviceInit = WdfControlDeviceInitAllocate(*pWDFDriver,
                                                 &SDDL_DEVOBJ_KERNEL_ONLY);
   if(pWDFDeviceInit == 0)
   {
      status = STATUS_UNSUCCESSFUL;

      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PrvDriverDeviceAdd : WdfControlDeviceInitAllocate() [status: %#x]\n",
                 status);

      HLPR_BAIL;      
   }

   WdfDeviceInitSetDeviceType(pWDFDeviceInit,
                              FILE_DEVICE_NETWORK);

   status = WdfDeviceCreate(&pWDFDeviceInit,
                            &attributes,
                            &g_WDFDevice);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PrvDriverDeviceAdd : WdfDeviceCreate() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   g_pWDMDevice = WdfDeviceWdmGetDeviceObject(g_WDFDevice);
   HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(g_pWDMDevice,
                                         status);

   g_pPowerStateEnterIOWorkItem = IoAllocateWorkItem(g_pWDMDevice);
   HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(g_pPowerStateEnterIOWorkItem,
                                         status);

   g_pPowerStateExitIOWorkItem = IoAllocateWorkItem(g_pWDMDevice);
   HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(g_pPowerStateExitIOWorkItem,
                                         status);

   status = RegisterPowerStateChangeCallback(&g_deviceExtension);
   HLPR_BAIL_ON_FAILURE(status);

#pragma warning(push)
#pragma warning(disable: 6388) /// g_pNDISPoolData will be 0

   status = KrnlHlprNDISPoolDataCreate(&g_pNDISPoolData);
   HLPR_BAIL_ON_FAILURE(status);

#pragma warning(pop)

   PrvFwpmBfeStateSubscribeChanges();

   status = PrvWFPSamplerDeviceDataPopulate(&g_WFPSamplerDeviceData);
   HLPR_BAIL_ON_FAILURE(status);

   KeInitializeSpinLock(&g_bpeSpinLock);

   status = PrvBasicStreamInjectionSerializationListInitialization();
   HLPR_BAIL_ON_FAILURE(status);


   status = PrvWFPSamplerFwpObjectsAddGlobal(&g_WFPSamplerDeviceData);
   HLPR_BAIL_ON_FAILURE(status);

   WdfControlFinishInitializing(g_WDFDevice);

   HLPR_BAIL_LABEL:

   if(status != STATUS_SUCCESS)
   {
      if(pWDFDeviceInit)
          WdfDeviceInitFree(pWDFDeviceInit);
   }

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PrvDriverDeviceAdd() [status: %#x]\n",
              status);

#endif /// DBG
   
   return status;
}

/**
 @framework_function="DriverEntry"
 
   Purpose:  Entry point for the driver.                                                        <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF540807.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF547175.aspx             <br>
*/
_IRQL_requires_same_
_Function_class_(DRIVER_INITIALIZE)
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject,
                     _In_ PUNICODE_STRING pRegistryPath)
{
   WPP_INIT_TRACING(pDriverObject,
                    pRegistryPath);

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> DriverEntry()\n");

#endif /// DBG
   
   NT_ASSERT(pDriverObject);
   NT_ASSERT(pRegistryPath);

   NTSTATUS              status = STATUS_SUCCESS;
   WDF_DRIVER_CONFIG     driverConfig;
   WDF_OBJECT_ATTRIBUTES attributes;

   WDF_DRIVER_CONFIG_INIT(&driverConfig,
                          WDF_NO_EVENT_CALLBACK);

   ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

   driverConfig.DriverInitFlags |= WdfDriverInitNonPnpDriver;
   driverConfig.EvtDriverUnload  = EventDriverUnload;

   WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

   attributes.EvtCleanupCallback = EventCleanupDriverObject;

   status = WdfDriverCreate(pDriverObject,
                            pRegistryPath,
                            &attributes,
                            &driverConfig,
                            &g_WDFDriver);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! DriverEntry : WdfDriverCreate() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   status = PrvDriverDeviceAdd(&g_WDFDriver);
   HLPR_BAIL_ON_FAILURE(status);

   HLPR_BAIL_LABEL:

#if DBG

      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_INFO_LEVEL,
                 " <--- DriverEntry() [status: %#x]\n",
                 status);
   
#endif /// DBG

   if(status != STATUS_SUCCESS)
      WPP_CLEANUP(pDriverObject);

   return status;
}


<|start_filename|>avstream/sampledevicemft/contosodevice.h<|end_filename|>
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//  The Contoso device comes with an SDK
//  that defines various Contoso-specific types,
//  all of which have CONTOSO in their names.

#pragma once

//  This payload is used when sending a KSPROPERTY_CONTOSODEVICE_PROCESSBUFFER.
struct CONTOSODEVICE_PROCESSBUFFER_PAYLOAD
{
    GUID        identifier;
    DWORD       size;
};

//  Define a custom property set for driver communications.
//  {C5175EE2-583E-43A2-8C26-765287BCEB9D}
#define STATIC_PROPSETID_CONTOSODEVICE\
    0xc5175ee2, 0x583e, 0x43a2, 0x8c, 0x26, 0x76, 0x52, 0x87, 0xbc, 0xeb, 0x9d
DEFINE_GUIDSTRUCT("C5175EE2-583E-43A2-8C26-765287BCEB9D", PROPSETID_CONTOSODEVICE);
#define PROPSETID_CONTOSODEVICE DEFINE_GUIDNAMED(PROPSETID_CONTOSODEVICE)
#define KSPROPERTY_CONTOSODEVICE_PROCESSBUFFER 0       // Send a frame buffer ID to the driver.



<|start_filename|>general/SimpleMediaSource/MediaSource/SimpleMediaSourceActivate.h<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
#pragma once
#ifndef SIMPLEMEDIASOURCEACTIVATE_H
#define SIMPLEMEDIASOURCEACTIVATE_H

namespace winrt::WindowsSample::implementation
{
    struct __declspec(uuid("9812588D-5CE9-4E4C-ABC1-049138D10DCE"))
        SimpleMediaSourceActivate : winrt::implements<SimpleMediaSourceActivate, IMFActivate>
    {
        SimpleMediaSourceActivate() = default;

    public:
        // IMFActivate
        IFACEMETHODIMP ActivateObject(REFIID riid, void** ppv) override;
        IFACEMETHODIMP ShutdownObject() override;
        IFACEMETHODIMP DetachObject() override;

        // IMFAttributes (inherits by IMFActivate)
        IFACEMETHODIMP GetItem(_In_ REFGUID guidKey, _Inout_opt_  PROPVARIANT* pValue) override;
        IFACEMETHODIMP GetItemType(_In_ REFGUID guidKey, _Out_ MF_ATTRIBUTE_TYPE* pType) override;
        IFACEMETHODIMP CompareItem(_In_ REFGUID guidKey, _In_ REFPROPVARIANT Value, _Out_ BOOL* pbResult) override;
        IFACEMETHODIMP Compare(_In_ IMFAttributes* pTheirs, _In_ MF_ATTRIBUTES_MATCH_TYPE MatchType, _Out_ BOOL* pbResult) override;
        IFACEMETHODIMP GetUINT32(_In_ REFGUID guidKey, _Out_ UINT32* punValue) override;
        IFACEMETHODIMP GetUINT64(_In_ REFGUID guidKey, _Out_ UINT64* punValue) override;
        IFACEMETHODIMP GetDouble(_In_ REFGUID guidKey, _Out_ double* pfValue) override;
        IFACEMETHODIMP GetGUID(_In_ REFGUID guidKey, _Out_ GUID* pguidValue) override;
        IFACEMETHODIMP GetStringLength(_In_ REFGUID guidKey, _Out_ UINT32* pcchLength) override;
        IFACEMETHODIMP GetString(_In_ REFGUID guidKey, _Out_writes_(cchBufSize) LPWSTR pwszValue, _In_ UINT32 cchBufSize, _Inout_opt_ UINT32* pcchLength) override;
        IFACEMETHODIMP GetAllocatedString(_In_ REFGUID guidKey, _Out_writes_(*pcchLength + 1) LPWSTR* ppwszValue, _Inout_  UINT32* pcchLength) override;
        IFACEMETHODIMP GetBlobSize(_In_ REFGUID guidKey, _Out_ UINT32* pcbBlobSize) override;
        IFACEMETHODIMP GetBlob(_In_ REFGUID  guidKey, _Out_writes_(cbBufSize) UINT8* pBuf, UINT32 cbBufSize, _Inout_  UINT32* pcbBlobSize) override;
        IFACEMETHODIMP GetAllocatedBlob(__RPC__in REFGUID guidKey, __RPC__deref_out_ecount_full_opt(*pcbSize) UINT8** ppBuf, __RPC__out UINT32* pcbSize) override;
        IFACEMETHODIMP GetUnknown(__RPC__in REFGUID guidKey, __RPC__in REFIID riid, __RPC__deref_out_opt LPVOID* ppv) override;
        IFACEMETHODIMP SetItem(_In_ REFGUID guidKey, _In_ REFPROPVARIANT Value) override;
        IFACEMETHODIMP DeleteItem(_In_ REFGUID guidKey) override;
        IFACEMETHODIMP DeleteAllItems() override;
        IFACEMETHODIMP SetUINT32(_In_ REFGUID guidKey, _In_ UINT32  unValue) override;
        IFACEMETHODIMP SetUINT64(_In_ REFGUID guidKey, _In_ UINT64  unValue) override;
        IFACEMETHODIMP SetDouble(_In_ REFGUID guidKey, _In_ double  fValue) override;
        IFACEMETHODIMP SetGUID(_In_ REFGUID guidKey, _In_ REFGUID guidValue) override;
        IFACEMETHODIMP SetString(_In_ REFGUID guidKey, _In_ LPCWSTR wszValue) override;
        IFACEMETHODIMP SetBlob(_In_ REFGUID guidKey, _In_reads_(cbBufSize) const UINT8* pBuf, UINT32 cbBufSize) override;
        IFACEMETHODIMP SetUnknown(_In_ REFGUID guidKey, _In_ IUnknown* pUnknown) override;
        IFACEMETHODIMP LockStore() override;
        IFACEMETHODIMP UnlockStore() override;
        IFACEMETHODIMP GetCount(_Out_ UINT32* pcItems) override;
        IFACEMETHODIMP GetItemByIndex(UINT32 unIndex, _Out_ GUID* pguidKey, _Inout_ PROPVARIANT* pValue) override;
        IFACEMETHODIMP CopyAllItems(_In_ IMFAttributes* pDest) override;

    private:
        wil::com_ptr_nothrow <IMFAttributes> m_spActivateAttributes;
        winrt::com_ptr<winrt::WindowsSample::implementation::SimpleMediaSource> m_spSimpleMediaSrc;
    };
}

//
// cocreatable class
//
struct SimpleMediaSourceActivateFactory : public winrt::implements<SimpleMediaSourceActivateFactory, IClassFactory>
{
public:
    STDMETHODIMP CreateInstance(_In_ IUnknown*, _In_ REFIID riid, _COM_Outptr_ void** result) noexcept final try
    {
        RETURN_HR_IF_NULL(E_POINTER, result);
        *result = nullptr;

        winrt::com_ptr<winrt::WindowsSample::implementation::SimpleMediaSourceActivate> ptr = winrt::make_self<winrt::WindowsSample::implementation::SimpleMediaSourceActivate>();
        return ptr->QueryInterface(riid, result);
    } CATCH_RETURN()


    STDMETHODIMP LockServer(BOOL) noexcept final
    {
        return S_OK;
    }
};


#endif


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/DeviceControl.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

--*/

#pragma once
#pragma warning(disable : 4201) // disable nameless struct/union warning

//
// {573E8C73-0CB4-4471-A1BF-FAB26C31D384}
//
DEFINE_GUID(GUID_DEVINTERFACE_OSRUSBFX2,
    0x573e8c73, 0xcb4, 0x4471, 0xa1, 0xbf, 0xfa, 0xb2, 0x6c, 0x31, 0xd3, 0x84);

//
// Define the structures that will be used by the IOCTL interface to the driver
//

// BAR_GRAPH_STATE is a bit field structure with each bit corresponding to one
// of the bar graph on the OSRFX2 Development Board
//
typedef struct _BAR_GRAPH_STATE {

    union {

        struct {
            //
            // Individual bars starting from the top of the stack of bars
            //
            // NOTE: There are actually 10 bars, but the very top two do not
            // light and are not counted here
            //
            UCHAR Bar1 : 1;
            UCHAR Bar2 : 1;
            UCHAR Bar3 : 1;
            UCHAR Bar4 : 1;
            UCHAR Bar5 : 1;
            UCHAR Bar6 : 1;
            UCHAR Bar7 : 1;
            UCHAR Bar8 : 1;
        };

        //
        // The state of all the bar graph as a single
        // UCHAR
        //
        UCHAR BarsAsUChar;
    };

} BAR_GRAPH_STATE, *PBAR_GRAPH_STATE;

//
// SWITCH_STATE is a bit field structure with each bit corresponding to one of
// the switches on the OSRFX2 Development Board
//
typedef struct _SWITCH_STATE {

    union {
        struct {
            //
            // Individual switches starting from the left of the set of switches
            //
            UCHAR Switch1 : 1;
            UCHAR Switch2 : 1;
            UCHAR Switch3 : 1;
            UCHAR Switch4 : 1;
            UCHAR Switch5 : 1;
            UCHAR Switch6 : 1;
            UCHAR Switch7 : 1;
            UCHAR Switch8 : 1;
        };

        //
        // The state of all the switches as a single
        // UCHAR
        //
        UCHAR SwitchesAsUChar;
    };

} SWITCH_STATE, *PSWITCH_STATE;

DWORD
OsrFx2ControlDevice(
    _In_ PDEVICE_CONTEXT Context
    );

VOID
OsrFx2InitializeDevice(
    VOID
    );

#define IOCTL_INDEX             0x800
#define FILE_DEVICE_OSRUSBFX2   65500U

#define IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                       IOCTL_INDEX,           \
                                                       METHOD_BUFFERED,       \
                                                       FILE_READ_ACCESS)

#define IOCTL_OSRUSBFX2_RESET_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                              IOCTL_INDEX + 1,       \
                                              METHOD_BUFFERED,       \
                                              FILE_WRITE_ACCESS)

#define IOCTL_OSRUSBFX2_REENUMERATE_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                    IOCTL_INDEX  + 3,      \
                                                    METHOD_BUFFERED,       \
                                                    FILE_WRITE_ACCESS)

#define IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                       IOCTL_INDEX  + 4,      \
                                                       METHOD_BUFFERED,       \
                                                       FILE_READ_ACCESS)

#define IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                       IOCTL_INDEX + 5,       \
                                                       METHOD_BUFFERED,       \
                                                       FILE_WRITE_ACCESS)

#define IOCTL_OSRUSBFX2_READ_SWITCHES CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                               IOCTL_INDEX + 6,       \
                                               METHOD_BUFFERED,       \
                                               FILE_READ_ACCESS)

#define IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                       IOCTL_INDEX + 7,       \
                                                       METHOD_BUFFERED,       \
                                                       FILE_READ_ACCESS)

#define IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \
                                                       IOCTL_INDEX + 8,       \
                                                       METHOD_BUFFERED,       \
                                                       FILE_WRITE_ACCESS)

#define IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE CTL_CODE(FILE_DEVICE_OSRUSBFX2,\
                                                       IOCTL_INDEX + 9,      \
                                                       METHOD_OUT_DIRECT,    \
                                                       FILE_READ_ACCESS)


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/TestWasapiUseFromAudioDg.cpp<|end_filename|>
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//

#include <stdafx.h>
#include <memory>
#include <wil\resultmacros.h>
#include <wil\com.h>
#include <audioclient.h>
#include <audioclientp.h>
#include <audiosessiontypesp.h>
#include <mmdeviceapi.h>
#include "etwlistener.h"
#include <functiondiscoverykeys.h>

class CTestWasapiUsageFromAudioDg
{
    BEGIN_TEST_CLASS(CTestWasapiUsageFromAudioDg)
        START_APPVERIFIFER
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioenginebaseapop.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"Elevated")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
        END_APPVERIFIER
    END_TEST_CLASS()

protected:
    BEGIN_TEST_METHOD(TestStreamCreation)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"50B80D35-5E61-4D91-B201-5F89D67E4A5A")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"216ADA42-25DC-4504-9032-16D63704FB08")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Test WASAPI usage from APOs - TestStreamCreation")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"30")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"60")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: Test WASAPI usage")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()
};

using malloca_deleter = wil::function_deleter<decltype(&::_freea), ::_freea>;
template <typename T>
using unique_malloca_ptr = wistd::unique_ptr<T, malloca_deleter>;

class CEventParser : public IEtwEventHandler
{
public:
    int GetCountOfAudioClientInits() { return m_countOfAudioClientInits; }
    int GetCountOfAudioClientInitsFromAudioDg() { return m_countOfAudioClientInitsFromAudioDg; }

private:

    int m_countOfAudioClientInits = 0;
    int m_countOfAudioClientInitsFromAudioDg = 0;

    std::wstring QueryProcessName(const DWORD pid)
    {
        wil::unique_handle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
        if(NULL == hProcess)
        {
            return L"err " + std::to_wstring(GetLastError());
        }
        else
        {
            WCHAR szProcessName[1024];
            DWORD cchSize = ARRAYSIZE(szProcessName);
            if (0 == QueryFullProcessImageNameW(hProcess.get(), 0, szProcessName, &cchSize))
            {
                return L"err " + std::to_wstring(GetLastError());
            }

            PWSTR fileName = szProcessName + cchSize;
            while (fileName != szProcessName && *fileName != L'\\') fileName--;
            
            return (*fileName == L'\\') ? fileName + 1 : fileName;
        }
    }

    void OnEvent(PEVENT_RECORD pEventRecord)
    {
        WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
        [&]() {
            ULONG bufferSize = 0;
            if (TdhGetEventInformation(pEventRecord, 0, nullptr, nullptr, &bufferSize) == ERROR_INSUFFICIENT_BUFFER)
            {
                unique_malloca_ptr<TRACE_EVENT_INFO> eventInfo(reinterpret_cast<PTRACE_EVENT_INFO>(_malloca(bufferSize)));
                VERIFY_IS_NOT_NULL(eventInfo);
                RETURN_IF_WIN32_ERROR(TdhGetEventInformation(pEventRecord, 0, nullptr, eventInfo.get(), &bufferSize));
     
                // LOG_OUTPUT(L"Received event: %s", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE*>(eventInfo.get()) + eventInfo->EventNameOffset));
                if (_wcsicmp(L"AudioClientInitialize", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE*>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    m_countOfAudioClientInits++;
                    
                    try
                    {
                        // LOG_OUTPUT(L"Process name: %s", QueryProcessName(pEventRecord->EventHeader.ProcessId).c_str());
                        if (0 == _wcsicmp(QueryProcessName(pEventRecord->EventHeader.ProcessId).c_str(), L"audiodg.exe"))
                        {
                            m_countOfAudioClientInitsFromAudioDg++;
                        }
                    }
                    CATCH_LOG()
                }
            }
            return S_OK;
        }();
    }
};

// {6e7b1892-5288-5fe5-8f34-e3b0dc671fd2}
static const GUID AudioSesTelemetryProvider =
{ 0x6e7b1892, 0x5288, 0x5fe5, 0x8f, 0x34, 0xe3, 0xb0, 0xdc, 0x67, 0x1f, 0xd2 };

void CTestWasapiUsageFromAudioDg::TestStreamCreation()
{
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);

    CEtwListener listener;
    CEventParser parser;

    VERIFY_SUCCEEDED(listener.StartTraceSession(L"TestWasapiUsageFromAudioDg", static_cast<IEtwEventHandler *>(&parser)));

    // Enable Microsoft.Windows.Audio.Client
    VERIFY_SUCCEEDED(listener.EnableProvider(AudioSesTelemetryProvider, TRACE_LEVEL_INFORMATION, 8));

    wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));

    // Test Render endpoints - all stream categories are valid except FarFieldSpeech, UniformSpeech, and VoiceTyping
    {
        wil::com_ptr_nothrow<IMMDeviceCollection> spDevices;
        VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spDevices));

        UINT cDevices = 0;
        VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

        for (UINT i = 0; i < cDevices; i++)
        {
            wil::com_ptr_nothrow<IMMDevice> pEndpoint;
            VERIFY_SUCCEEDED(spDevices->Item(i, &pEndpoint));

            wil::unique_prop_variant var;
            wil::com_ptr_nothrow<IPropertyStore> spPropertyStore;
            VERIFY_SUCCEEDED(pEndpoint->OpenPropertyStore(STGM_READ, &spPropertyStore));
            VERIFY_SUCCEEDED(spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var));
            VERIFY_ARE_EQUAL(VT_LPWSTR, var.vt);
            LOG_OUTPUT(L"Testing endpoint: %s", var.pwszVal);

            for (int category = ExtendedAudioCategory_Other; category < ExtendedAudioCategory_enum_count; category++)
            {
                if (!(category == ExtendedAudioCategory_FarFieldSpeech ||
                      category == ExtendedAudioCategory_UniformSpeech ||
                      category == ExtendedAudioCategory_VoiceTyping))
                {
                    wil::com_ptr_nothrow<IAudioClient2> ac;
                    VERIFY_SUCCEEDED(pEndpoint->Activate(__uuidof(IAudioClient2), CLSCTX_ALL, NULL, (void **)&ac));

                    AudioClientPropertiesPrivate acpp = {0};
                    acpp.ClientProps.cbSize = sizeof(acpp);
                    acpp.extCategory = (AUDIO_STREAM_EXTENDED_CATEGORY)category;

                    VERIFY_SUCCEEDED(ac->SetClientProperties(&acpp.ClientProps));

                    wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;
                    VERIFY_SUCCEEDED(ac->GetMixFormat(wil::out_param(mixFormat)));

                    LOG_OUTPUT(L"Testing category: %d", acpp.extCategory);
                    VERIFY_SUCCEEDED(ac->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, mixFormat.get(), nullptr));

                    Sleep(1000);
                }
            }
        }
    }

    // Test Capture endpoints - only some stream categories are valid
    {
        constexpr AUDIO_STREAM_EXTENDED_CATEGORY captureCategories[] = {ExtendedAudioCategory_Other, 
                                                                        ExtendedAudioCategory_Communications,
                                                                        ExtendedAudioCategory_Media,
                                                                        ExtendedAudioCategory_GameChat,
                                                                        ExtendedAudioCategory_Speech,
                                                                        ExtendedAudioCategory_PersonalAssistant,
                                                                        ExtendedAudioCategory_FarFieldSpeech,
                                                                        ExtendedAudioCategory_UniformSpeech,
                                                                        ExtendedAudioCategory_VoiceTyping,
                                                                        };

        wil::com_ptr_nothrow<IMMDeviceCollection> spDevices;
        VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &spDevices));

        UINT cDevices = 0;
        VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

        for (UINT i = 0; i < cDevices; i++)
        {
            wil::com_ptr_nothrow<IMMDevice> pEndpoint;
            VERIFY_SUCCEEDED(spDevices->Item(i, &pEndpoint));

            wil::unique_prop_variant var;
            wil::com_ptr_nothrow<IPropertyStore> spPropertyStore;
            VERIFY_SUCCEEDED(pEndpoint->OpenPropertyStore(STGM_READ, &spPropertyStore));
            VERIFY_SUCCEEDED(spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var));
            VERIFY_ARE_EQUAL(VT_LPWSTR, var.vt);
            LOG_OUTPUT(L"Testing endpoint: %s", var.pwszVal);

            for (int idx = 0; idx < ARRAYSIZE(captureCategories); idx++)
            {
                wil::com_ptr_nothrow<IAudioClient2> ac;
                VERIFY_SUCCEEDED(pEndpoint->Activate(__uuidof(IAudioClient2), CLSCTX_ALL, NULL, (void**)&ac));

                AudioClientPropertiesPrivate acpp = { 0 };
                acpp.ClientProps.cbSize = sizeof(acpp);
                acpp.extCategory = captureCategories[idx];

                VERIFY_SUCCEEDED(ac->SetClientProperties(&acpp.ClientProps));

                wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;
                VERIFY_SUCCEEDED(ac->GetMixFormat(wil::out_param(mixFormat)));

                LOG_OUTPUT(L"Testing category: %d", acpp.extCategory);
                VERIFY_SUCCEEDED(ac->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, mixFormat.get(), nullptr));

                Sleep(1000);
            }
        }
    }

    // Sleep a while to ensure we receive all the events from the above streams
    Sleep(5000);

    LOG_OUTPUT(L"Number of audio streams created: %d", parser.GetCountOfAudioClientInits());
    LOG_OUTPUT(L"Number of audio streams created from AudioDg: %d", parser.GetCountOfAudioClientInitsFromAudioDg());
    VERIFY_ARE_EQUAL(0, parser.GetCountOfAudioClientInitsFromAudioDg());

    // Stop the trace
    listener.StopTraceSession();

}


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/DeviceContext.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

--*/

#pragma once

//
// Device list
//
typedef struct _DEVICE_LIST_ENTRY {
    struct _DEVICE_LIST_ENTRY *Flink;
    struct _DEVICE_LIST_ENTRY *Blink;
} DEVICE_LIST_ENTRY, *PDEVICE_LIST_ENTRY;

FORCEINLINE
VOID
InitializeDeviceListHead(
    _Out_ PDEVICE_LIST_ENTRY Head
    )
{
    Head->Blink = Head->Flink = Head;
}

FORCEINLINE
BOOL
IsDeviceListEmpty(
    _In_ const PDEVICE_LIST_ENTRY ListHead
    )

{
    return (ListHead->Flink == ListHead);
}

FORCEINLINE
VOID
RemoveDeviceListEntry(
    _In_ PDEVICE_LIST_ENTRY Entry
    )
{
    PDEVICE_LIST_ENTRY Prev = Entry->Blink;
    PDEVICE_LIST_ENTRY Next = Entry->Flink;

    Prev->Flink = Next;
    Next->Blink = Prev;
}

FORCEINLINE
VOID
InsertTailDeviceListEntry(
    _Inout_ PDEVICE_LIST_ENTRY Head,
    _Inout_ PDEVICE_LIST_ENTRY Entry
    )
{
    PDEVICE_LIST_ENTRY Tail = Head->Blink;

    Tail->Flink = Entry;
    Entry->Blink = Tail;
    Entry->Flink = Head;
    Head->Blink = Entry;
}

//
// Context for device handle
//
typedef struct _DEVICE_CONTEXT {
    PWSTR             SymbolicLink;
    HANDLE            DeviceHandle;
    HCMNOTIFICATION   DeviceNotificationHandle;
    BOOL              Unregistered;
    CRITICAL_SECTION  ContextLock;
    DEVICE_LIST_ENTRY ListEntry;
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;

#define CONTAINING_DEVICE_RECORD(address) ((PDEVICE_CONTEXT)( \
                                            (PCHAR)(address) - \
                                            (ULONG_PTR)(&((PDEVICE_CONTEXT)0)->ListEntry)))

//
// Device interface context
//
DWORD
SetupDeviceInterfaceContext(
    VOID
    );

VOID
CleanupDeviceInterfaceContext(
    VOID
    );

//
// Device notifications related
//
VOID
DeviceQueryRemoveAction(
    _In_ PDEVICE_CONTEXT Context
    );

VOID
DeviceQueryRemoveFailedAction(
    _In_ PDEVICE_CONTEXT Context
    );

VOID
DeviceRemoveCompleteAction(
    _In_ PDEVICE_CONTEXT Context
    );

DWORD
WINAPI
DeviceCallback(
    _In_ HCMNOTIFICATION       hNotify,
    _In_ PVOID                 hContext,
    _In_ CM_NOTIFY_ACTION      Action,
    _In_ PCM_NOTIFY_EVENT_DATA EventData,
    _In_ DWORD                 EventDataSize
    );

DWORD
RegisterDeviceNotifications(
    _In_ PCWSTR DeviceInterfacePath
    );

VOID
UnregisterDeviceNotifications(
    _Inout_ PDEVICE_CONTEXT Context
    );

DWORD
WINAPI
InterfaceCallback(
    _In_ HCMNOTIFICATION       hNotify,
    _In_ PVOID                 hContext,
    _In_ CM_NOTIFY_ACTION      Action,
    _In_ PCM_NOTIFY_EVENT_DATA EventData,
    _In_ DWORD                 EventDataSize
    );

DWORD
RegisterInterfaceNotifications(
    _Out_ PHCMNOTIFICATION pInterfaceNotificationHandle
    );

DWORD
WINAPI
UnregisterDeviceNotificationsWorkerThread(
    _In_ PVOID lpThreadParameter
    );


<|start_filename|>nfc/NfcCxSample/windows-drivertemplate-nfc/Device.h<|end_filename|>
#pragma once

#include <windows.h>
#include <wdf.h>
#include <NfcCx.h>

class DeviceContext
{
public:
    // Implementation of `EvtDriverDeviceAdd`.
    static NTSTATUS AddDevice(
        _In_ WDFDRIVER Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit);

private:
    DeviceContext(_In_ WDFDEVICE Device);
    ~DeviceContext() = default;

    // Implementation of `EvtDestroyCallback`.
    static void Destroy(
        _In_ WDFOBJECT Object);

    // Implementation of `EvtDevicePrepareHardware`.
    static NTSTATUS PrepareHardware(
        _In_ WDFDEVICE Device,
        _In_ WDFCMRESLIST ResourcesRaw,
        _In_ WDFCMRESLIST ResourcesTranslated);

    // Implementation of `EvtDeviceReleaseHardware`.
    static NTSTATUS ReleaseHardware(
        _In_ WDFDEVICE Device,
        _In_ WDFCMRESLIST ResourcesTranslated);

    // Implementation of `EvtDeviceD0Entry`.
    static NTSTATUS D0Entry(
        _In_ WDFDEVICE Device,
        _In_ WDF_POWER_DEVICE_STATE PreviousState);

    // Implementation of `EvtDeviceD0Exit`.
    static NTSTATUS D0Exit(
        _In_ WDFDEVICE Device,
        _In_ WDF_POWER_DEVICE_STATE TargetState);

    // Implementation of `EvtNfcCxSequenceHandler`.
    static void SequenceHandler(
        _In_ WDFDEVICE Device,
        _In_ NFC_CX_SEQUENCE Sequence,
        _In_ PFN_NFC_CX_SEQUENCE_COMPLETION_ROUTINE CompletionRoutine,
        _In_opt_ WDFCONTEXT CompletionContext);

    static void IoControl(
        _In_ WDFDEVICE Device,
        _In_ WDFREQUEST Request,
        _In_ size_t OutputBufferLength,
        _In_ size_t InputBufferLength,
        _In_ ULONG IoControlCode);

    // Implementation of `EvtNfcCxWriteNciPacket`.
    static void WriteNciPacket(
        _In_ WDFDEVICE Device,
        _In_ WDFREQUEST Request);

    NTSTATUS ReadNciPacket(
        _In_reads_bytes_(nciPacketLength) void* nciPacket,
        _In_ size_t nciPacketLength);

    const WDFDEVICE _Device = nullptr;
    WDFMEMORY _NciReadMemory = nullptr;
};

// Define the DeviceGetContext() function which can be used to get a pointer the DeviceContext from a WDFDEVICE object.
// Equivalent to:
//   DeviceContext* DeviceGetContext(WDFOBJECT Handle) { ... }
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DeviceContext, DeviceGetContext);


<|start_filename|>nfc/NfcCxSample/windows-drivertemplate-nfc/Driver.cpp<|end_filename|>
#include <windows.h>
#include <wdf.h>

#include "Device.h"

extern "C" DRIVER_INITIALIZE DriverEntry;

// [Required]
// The entry point for the driver.
//
// In general, almost nothing should be done during driver initialization. Instead, almost all resources should
// be associated with a specific device.
//
// https://docs.microsoft.com/windows-hardware/drivers/wdf/driverentry-for-kmdf-drivers
extern "C" NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status;

    // Specify `DeviceContext::AddDevice` as the `EvtDriverDeviceAdd` callback function
    // for the driver.
    WDF_DRIVER_CONFIG driverConfig;
    WDF_DRIVER_CONFIG_INIT(&driverConfig, DeviceContext::AddDevice);

    // Initialize WDF.
    status = WdfDriverCreate(
        DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES,
        &driverConfig,
        WDF_NO_HANDLE
        );
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    return STATUS_SUCCESS;
}


<|start_filename|>filesys/fastfat/devctrl.c<|end_filename|>
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    DevCtrl.c

Abstract:

    This module implements the File System Device Control routines for Fat
    called by the dispatch driver.


--*/

#include "FatProcs.h"

//
//  The local debug trace level
//

#define Dbg                              (DEBUG_TRACE_DEVCTRL)

//
//  Local procedure prototypes
//

//
//  Tell prefast this is an IO_COMPLETION_ROUTINE
//
IO_COMPLETION_ROUTINE FatDeviceControlCompletionRoutine;

NTSTATUS
FatDeviceControlCompletionRoutine(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Contxt
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCommonDeviceControl)
#pragma alloc_text(PAGE, FatFsdDeviceControl)
#endif


_Function_class_(IRP_MJ_DEVICE_CONTROL)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdDeviceControl (
    _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of Device control operations

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the
        file exists

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The FSD status for the IRP

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "FatFsdDeviceControl\n", 0);

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ));

        Status = FatCommonDeviceControl( IrpContext, Irp );

    } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

        //
        //  We had some trouble trying to perform the requested
        //  operation, so we'll abort the I/O request with
        //  the error status that we get back from the
        //  exception code
        //

        Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
    }

    if (TopLevel) { IoSetTopLevelIrp( NULL ); }

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "FatFsdDeviceControl -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatCommonDeviceControl (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for doing Device control operations called
    by both the fsd and fsp threads

Arguments:

    Irp - Supplies the Irp to process

    InFsp - Indicates if this is the fsp thread or someother thread

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;
    KEVENT WaitEvent;
    PVOID CompletionContext = NULL;

    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    PAGED_CODE();

    //
    //  Get a pointer to the current Irp stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonDeviceControl\n", 0);
    DebugTrace( 0, Dbg, "Irp           = %p\n", Irp);
    DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction);

    //
    //  Decode the file object, the only type of opens we accept are
    //  user volume opens.
    //

    if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {

        FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );

        DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", STATUS_INVALID_PARAMETER);
        return STATUS_INVALID_PARAMETER;
    }

    //
    //  A few IOCTLs actually require some intervention on our part
    //

    switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {

    case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES:

        //
        //  This is sent by the Volume Snapshot driver (Lovelace).
        //  We flush the volume, and hold all file resources
        //  to make sure that nothing more gets dirty. Then we wait
        //  for the IRP to complete or cancel.
        //

        SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
        FatAcquireExclusiveVolume( IrpContext, Vcb );

        FatFlushAndCleanVolume( IrpContext,
                                Irp,
                                Vcb,
                                FlushWithoutPurge );

        KeInitializeEvent( &WaitEvent, NotificationEvent, FALSE );
        CompletionContext = &WaitEvent;

        //
        //  Get the next stack location, and copy over the stack location
        //

        IoCopyCurrentIrpStackLocationToNext( Irp );

        //
        //  Set up the completion routine
        //

        IoSetCompletionRoutine( Irp,
                                FatDeviceControlCompletionRoutine,
                                CompletionContext,
                                TRUE,
                                TRUE,
                                TRUE );
        break;

    case IOCTL_DISK_COPY_DATA:

        //
        //  We cannot allow this IOCTL to be sent unless the volume is locked,
        //  since this IOCTL allows direct writing of data to the volume.
        //  We do allow kernel callers to force access via a flag.  A handle that
        //  issued a dismount can send this IOCTL as well.
        //

        if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) &&
            !FlagOn( IrpSp->Flags, SL_FORCE_DIRECT_WRITE ) &&
            !FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) {

            FatCompleteRequest( IrpContext,
                                Irp,
                                STATUS_ACCESS_DENIED );

            DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", STATUS_ACCESS_DENIED);
            return STATUS_ACCESS_DENIED;
        }

        break;

    case IOCTL_SCSI_PASS_THROUGH:
    case IOCTL_SCSI_PASS_THROUGH_DIRECT:
    case IOCTL_SCSI_PASS_THROUGH_EX:
    case IOCTL_SCSI_PASS_THROUGH_DIRECT_EX:

        //
        //  If someone is issuing a format unit command underneath us, then make
        //  sure we mark the device as needing verification when they close their
        //  handle.
        //

        if ((!FlagOn( IrpSp->FileObject->Flags, FO_FILE_MODIFIED ) ||
             !FlagOn( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT )) &&
             (Irp->AssociatedIrp.SystemBuffer != NULL)) {

            PCDB  Cdb = NULL;

            //
            //  If this is a 32 bit application running on 64 bit then thunk the
            //  input structures to grab the Cdb.
            //

#if defined (_WIN64) && defined(BUILD_WOW64_ENABLED)
            if (IoIs32bitProcess(Irp)) {

                if ( (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) ||
                     (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT )) {

                    if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH32 )) {

                        Cdb = (PCDB)((PSCSI_PASS_THROUGH32)(Irp->AssociatedIrp.SystemBuffer))->Cdb;
                    }
                } else {

                    if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH32_EX )) {

                        Cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)(Irp->AssociatedIrp.SystemBuffer))->Cdb;
                    }
                }

            } else {
#endif
                if ( (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) ||
                     (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT )) {

                    if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH )) {

                        Cdb = (PCDB)((PSCSI_PASS_THROUGH)(Irp->AssociatedIrp.SystemBuffer))->Cdb;
                    }
                } else {

                    if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH_EX )) {

                        Cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)(Irp->AssociatedIrp.SystemBuffer))->Cdb;
                    }
                }

#if defined (_WIN64) && defined(BUILD_WOW64_ENABLED)
            }
#endif

            if ((Cdb != NULL) && (Cdb->AsByte[0] == SCSIOP_FORMAT_UNIT)) {

                SetFlag( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT );
                SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED );
            }
        }

        //
        //  Fall through as we do not need to know the outcome of this operation.
        //

    default:

        //
        //  FAT doesn't need to see this on the way back, so skip ourselves.
        //

        IoSkipCurrentIrpStackLocation( Irp );
        break;
    }

    //
    //  Send the request.
    //

    Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);

    if (Status == STATUS_PENDING && CompletionContext) {

        KeWaitForSingleObject( &WaitEvent,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL );

        Status = Irp->IoStatus.Status;
    }

    //
    //  If we had a context, the IRP remains for us and we will complete it.
    //  Handle it appropriately.
    //

    if (CompletionContext) {

        //
        //  Release all the resources that we held because of a
        //  VOLSNAP_FLUSH_AND_HOLD. 
        //

        NT_ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES );

        FatReleaseVolume( IrpContext, Vcb );

        //
        //  If we had no context, the IRP will complete asynchronously.
        //

    } else {

        Irp = NULL;
    }

    FatCompleteRequest( IrpContext, Irp, Status );

    DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", Status);

    return Status;
}


//
//  Local support routine
//

NTSTATUS
FatDeviceControlCompletionRoutine(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
    )

{
    PKEVENT Event = (PKEVENT) Contxt;
    
    //
    //  If there is an event, this is a synch request. Signal and
    //  let I/O know this isn't done yet.
    //

    if (Event) {

        KeSetEvent( Event, 0, FALSE );
        return STATUS_MORE_PROCESSING_REQUIRED;
    }

    UNREFERENCED_PARAMETER( DeviceObject );
    UNREFERENCED_PARAMETER( Irp );

    return STATUS_SUCCESS;
}



<|start_filename|>avstream/sampledevicemft/multipinmftutils.cpp<|end_filename|>
//*@@@+++@@@@******************************************************************
//
// Microsoft Windows Media Foundation
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//*@@@---@@@@******************************************************************
//

#include "stdafx.h"
#include "common.h"
#include "multipinmft.h"
#include "basepin.h"

#pragma comment(lib, "d2d1") 
#ifdef MF_WPP
#include "multipinmftutils.tmh"    //--REF_ANALYZER_DONT_REMOVE--
#endif

// Critical sections

CCritSec::CCritSec()
{
    InitializeCriticalSection(&m_criticalSection);
}

CCritSec::~CCritSec()
{
    DeleteCriticalSection(&m_criticalSection);
}

_Requires_lock_not_held_(m_criticalSection) _Acquires_lock_(m_criticalSection)
void CCritSec::Lock()
{
    EnterCriticalSection(&m_criticalSection);
}

_Requires_lock_held_(m_criticalSection) _Releases_lock_(m_criticalSection)
void CCritSec::Unlock()
{
    LeaveCriticalSection(&m_criticalSection);
}


_Acquires_lock_(this->m_pCriticalSection->m_criticalSection)
CAutoLock::CAutoLock(CCritSec& crit)
{
    m_pCriticalSection = &crit;
    m_pCriticalSection->Lock();
}
_Acquires_lock_(this->m_pCriticalSection->m_criticalSection)
CAutoLock::CAutoLock(CCritSec* crit)
{
    m_pCriticalSection = crit;
    m_pCriticalSection->Lock();
}
_Releases_lock_(this->m_pCriticalSection->m_criticalSection)
CAutoLock::~CAutoLock()
{
    m_pCriticalSection->Unlock();
}

//
//Some utility functions..
//

/*++
    Description:
    Used to check if the input and the output Image types are optinmized or not
--*/

STDMETHODIMP   IsOptimizedPlanarVideoInputImageOutputPair(
    _In_ IMFMediaType *inMediaType,
    _In_ IMFMediaType *outMediaType,
    _Out_ bool *optimized,
    _Out_ bool *optimizedxvpneeded )
{
    HRESULT         hr                  = S_OK;
    GUID            guidInputSubType    = GUID_NULL;
    GUID            guidOutputSubType   = GUID_NULL;
    UINT32          uWidthIn = 0, uHeightIn = 0, uWidthOut = 0, uHeightOut = 0;


    DMFTCHECKHR_GOTO(inMediaType->GetGUID(MF_MT_SUBTYPE, &guidInputSubType), done);
    
    DMFTCHECKHR_GOTO(outMediaType->GetGUID(MF_MT_SUBTYPE, &guidOutputSubType), done);

    *optimized = false;         //Assume we aren't optimized . Optimized = (ip = YU12|NV12 and  op = JPEG) 
    *optimizedxvpneeded = true; //Assume we need xvps

    if (IsEqualGUID(guidInputSubType, MFVideoFormat_YV12) || IsEqualGUID(guidInputSubType, MFVideoFormat_NV12))
    {
        if (IsEqualGUID(guidOutputSubType, GUID_ContainerFormatJpeg))
        {
            *optimized = true;
        }
    }

    if (!*optimized)
    {
        goto done;
    }

    DMFTCHECKHR_GOTO(MFGetAttributeSize(inMediaType, MF_MT_FRAME_SIZE, &uWidthIn, &uHeightIn), done);
    DMFTCHECKHR_GOTO(MFGetAttributeSize(outMediaType, MF_MT_FRAME_SIZE, &uWidthOut, &uHeightOut), done);

    if ((uWidthIn == uWidthOut) && (uHeightIn == uHeightOut))
    {
        *optimizedxvpneeded = false;
    }
    if (!*optimizedxvpneeded)
    {
        UINT32 nominalRange;
        hr = inMediaType->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominalRange);

        if (FAILED(hr) || nominalRange != MFNominalRange_0_255)
        {
            //XVP needed since nominal range is not 0-255 for YV12 or NV12 fed into WIC
            *optimizedxvpneeded = true;
        }
        hr = S_OK;
    }

done:
    return hr;
}


/*
Description:

    This is used whenever there is a media type change on an output pin and the 
    Output queue is being reconfigured.
    The possible return values for the function are as follows
    
    DeviceMftTransformXVPIllegal        -> If either of the mediatypes or both are NULL
    DeviceMftTransformXVPDisruptiveIn   -> If the mediatype  at the output pin is greater than the input pin. This will result in change of the media type on the input
    DeviceMftTransformXVPDisruptiveOut  -> This is a reconfiguration or addition of the XVP in the Output pin queue
    DeviceMftTransformXVPCurrent        -> No XVP needed at all
*/
STDMETHODIMP CompareMediaTypesForConverter(
    _In_opt_ IMFMediaType *inMediaType,
    _In_    IMFMediaType  *newMediaType,
    _Inout_ PDMFT_conversion_type  operation
    )
{
    HRESULT hr          = S_OK;
    GUID    guidTypeA   = GUID_NULL;
    GUID    guidTypeB   = GUID_NULL;
    
    *operation = DeviceMftTransformTypeIllegal;
    if ((!inMediaType) || (!newMediaType))
    {
       goto done;
    }

      if ( SUCCEEDED( inMediaType->GetGUID(  MF_MT_MAJOR_TYPE, &guidTypeA ) ) &&
         SUCCEEDED( newMediaType->GetGUID( MF_MT_MAJOR_TYPE, &guidTypeB ) ) &&
        IsEqualGUID( guidTypeA, guidTypeB ) )
    {
          if ( SUCCEEDED( inMediaType->GetGUID ( MF_MT_SUBTYPE, &guidTypeA ) ) &&
              SUCCEEDED( newMediaType->GetGUID( MF_MT_SUBTYPE, &guidTypeB ) ) &&
              IsEqualGUID( guidTypeA, guidTypeB ) )
          {
              BOOL passThrough = TRUE;
              DMFTCHECKHR_GOTO(CheckPassthroughMediaType(inMediaType, newMediaType, passThrough), done);
              *operation = passThrough?DeviceMftTransformTypeEqual: DeviceMftTransformTypeXVP;
          }
          else
          {
              //This is a disruptive operation. Actually a decoder operation!
              *operation = DeviceMftTransformTypeDecoder;
          }
      }
  done:
    return hr;
}

/*++
Description: Used to test if the sample passed is a DX sample or not?
--*/
HRESULT IsInputDxSample(
    _In_ IMFSample* pSample,
    _Inout_ BOOL *isDxSample
    )
{
    HRESULT hr = S_OK;
    ComPtr<IMFMediaBuffer> spBuffer = nullptr;
    DMFTCHECKNULL_GOTO( pSample, done, E_INVALIDARG );

    *isDxSample = false;
    if ( SUCCEEDED( pSample->GetBufferByIndex( 0, &spBuffer ) ) )
    {
        ComPtr<IMFDXGIBuffer> spDXGIBuffer;
        ComPtr<ID3D11Texture2D> spResourceTexture;
        if ( SUCCEEDED( spBuffer.As(&spDXGIBuffer  ) ) &&
            SUCCEEDED( spDXGIBuffer-> GetResource( IID_PPV_ARGS( &spResourceTexture ) ) ) )
        {
            *isDxSample = true;
        }
    }
done:
    return hr;
}


/*++
    Description:
        Helper function to return back if the Pin is in stopped stateo or not
--*/
STDMETHODIMP_(BOOL) IsPinStateInActive( _In_ DeviceStreamState state)
{
    if ((state == DeviceStreamState_Disabled) ||
        (state == DeviceStreamState_Stop))
    {
        return TRUE;
    }
    return FALSE;
}

/*
Description:
    A Helper function to return if the Mediatype is a compressed video type or not!
*/
STDMETHODIMP_(BOOL) IsKnownUncompressedVideoType(_In_ GUID guidSubType)
{
    if (guidSubType == MFVideoFormat_ARGB32 ||
        guidSubType == MFVideoFormat_RGB24 ||
        guidSubType == MFVideoFormat_RGB555 ||
        guidSubType ==  MFVideoFormat_RGB565 ||  
        guidSubType ==  MFVideoFormat_RGB32 ||          
        guidSubType ==  MFVideoFormat_RGB8 ||  
        guidSubType ==  MFVideoFormat_AI44 ||  
        guidSubType ==  MFVideoFormat_AYUV ||  
        guidSubType ==  MFVideoFormat_YUY2 ||  
        guidSubType ==  MFVideoFormat_YVYU ||  
        guidSubType ==  MFVideoFormat_YVU9 ||  
        guidSubType ==  MFVideoFormat_UYVY ||  
        guidSubType ==  MFVideoFormat_NV11 ||  
        guidSubType ==  MFVideoFormat_NV12 ||  
        guidSubType ==  MFVideoFormat_YV12 ||  
        guidSubType ==  MFVideoFormat_I420 ||  
        guidSubType ==  MFVideoFormat_IYUV ||  
        guidSubType ==  MFVideoFormat_Y210 ||  
        guidSubType ==  MFVideoFormat_Y216 ||  
        guidSubType ==  MFVideoFormat_Y410 ||  
        guidSubType ==  MFVideoFormat_Y416 ||  
        guidSubType ==  MFVideoFormat_Y41P ||  
        guidSubType ==  MFVideoFormat_Y41T ||  
        guidSubType ==  MFVideoFormat_Y42T ||  
        guidSubType ==  MFVideoFormat_P210 ||  
        guidSubType ==  MFVideoFormat_P216 ||  
        guidSubType ==  MFVideoFormat_P010 ||  
        guidSubType ==  MFVideoFormat_P016 ||  
        guidSubType ==  MFVideoFormat_v210 ||  
        guidSubType ==  MFVideoFormat_v216 ||  
        guidSubType ==  MFVideoFormat_v410 ||
        guidSubType ==  MFVideoFormat_L8   ||
        guidSubType ==  MFVideoFormat_L16  ||
        guidSubType == MFVideoFormat_D16)
    {  
        return( TRUE );  
    }  

    return( FALSE ); 
}

#ifndef IF_EQUAL_RETURN
#define IF_EQUAL_RETURN(param, val) if(val == param) return #val
#endif

#define checkAdjustBufferCap(a,len){\
    char* tStore = NULL; \
if (a && strlen(a) > ((len * 7) / 10)){\
    tStore = a; \
    len *= 2; \
    a = new (std::nothrow) char[len]; \
if (!a){\
goto done;}\
    a[0] = 0; \
    strcat_s(a, len, tStore); \
    delete(tStore); }\
}


/*++
Description:
    returns an ascii buffer for the GUID passed. The Caller should release the memory allocated
--*/
LPSTR DumpGUIDA(_In_ REFGUID guid)
{
    LPOLESTR lpszGuidString = NULL;
    char *ansiguidStr = NULL;
    if (SUCCEEDED(StringFromCLSID(guid, &lpszGuidString)))
    {
        int mbGuidLen = 0;
        mbGuidLen = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, lpszGuidString, -1, NULL, 0, NULL, NULL);
        if (mbGuidLen > 0)
        {
            mf_assert(mbGuidLen == (int)wcslen(lpszGuidString));
            ansiguidStr = new (std::nothrow) char[mbGuidLen];
            if (ansiguidStr)
            {
                WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpszGuidString, -1, ansiguidStr, mbGuidLen, NULL, NULL);
                CoTaskMemFree(lpszGuidString);
                ansiguidStr[mbGuidLen - 1] = 0;
            }
        }
    }
    return ansiguidStr;
}

//
//Borrrowed from MDSN sample
//
LPCSTR GetGUIDNameConst(const GUID& guid)
{
    IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE);
    IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE);
    IF_EQUAL_RETURN(guid, MF_MT_SUBTYPE);
    IF_EQUAL_RETURN(guid, MF_MT_ALL_SAMPLES_INDEPENDENT);
    IF_EQUAL_RETURN(guid, MF_MT_FIXED_SIZE_SAMPLES);
    IF_EQUAL_RETURN(guid, MF_MT_COMPRESSED);
    IF_EQUAL_RETURN(guid, MF_MT_SAMPLE_SIZE);
    IF_EQUAL_RETURN(guid, MF_MT_WRAPPED_TYPE);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_NUM_CHANNELS);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_SECOND);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_AVG_BYTES_PER_SECOND);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BLOCK_ALIGNMENT);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BITS_PER_SAMPLE);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_VALID_BITS_PER_SAMPLE);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_BLOCK);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_CHANNEL_MASK);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FOLDDOWN_MATRIX);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKREF);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKTARGET);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGREF);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGTARGET);
    IF_EQUAL_RETURN(guid, MF_MT_AUDIO_PREFER_WAVEFORMATEX);
    IF_EQUAL_RETURN(guid, MF_MT_AAC_PAYLOAD_TYPE);
    IF_EQUAL_RETURN(guid, MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION);
    IF_EQUAL_RETURN(guid, MF_MT_FRAME_SIZE);
    IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE);
    IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MAX);
    IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MIN);
    IF_EQUAL_RETURN(guid, MF_MT_PIXEL_ASPECT_RATIO);
    IF_EQUAL_RETURN(guid, MF_MT_DRM_FLAGS);
    IF_EQUAL_RETURN(guid, MF_MT_PAD_CONTROL_FLAGS);
    IF_EQUAL_RETURN(guid, MF_MT_SOURCE_CONTENT_HINT);
    IF_EQUAL_RETURN(guid, MF_MT_VIDEO_CHROMA_SITING);
    IF_EQUAL_RETURN(guid, MF_MT_INTERLACE_MODE);
    IF_EQUAL_RETURN(guid, MF_MT_TRANSFER_FUNCTION);
    IF_EQUAL_RETURN(guid, MF_MT_VIDEO_PRIMARIES);
    IF_EQUAL_RETURN(guid, MF_MT_CUSTOM_VIDEO_PRIMARIES);
    IF_EQUAL_RETURN(guid, MF_MT_YUV_MATRIX);
    IF_EQUAL_RETURN(guid, MF_MT_VIDEO_LIGHTING);
    IF_EQUAL_RETURN(guid, MF_MT_VIDEO_NOMINAL_RANGE);
    IF_EQUAL_RETURN(guid, MF_MT_GEOMETRIC_APERTURE);
    IF_EQUAL_RETURN(guid, MF_MT_MINIMUM_DISPLAY_APERTURE);
    IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_APERTURE);
    IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_ENABLED);
    IF_EQUAL_RETURN(guid, MF_MT_AVG_BITRATE);
    IF_EQUAL_RETURN(guid, MF_MT_AVG_BIT_ERROR_RATE);
    IF_EQUAL_RETURN(guid, MF_MT_MAX_KEYFRAME_SPACING);
    IF_EQUAL_RETURN(guid, MF_MT_DEFAULT_STRIDE);
    IF_EQUAL_RETURN(guid, MF_MT_PALETTE);
    IF_EQUAL_RETURN(guid, MF_MT_USER_DATA);
    IF_EQUAL_RETURN(guid, MF_MT_AM_FORMAT_TYPE);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG_START_TIME_CODE);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG2_PROFILE);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG2_LEVEL);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG2_FLAGS);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG_SEQUENCE_HEADER);
    IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_0);
    IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_0);
    IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_1);
    IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_1);
    IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_SRC_PACK);
    IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_CTRL_PACK);
    IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_HEADER);
    IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_FORMAT);
    IF_EQUAL_RETURN(guid, MF_MT_IMAGE_LOSS_TOLERANT);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG4_SAMPLE_DESCRIPTION);
    IF_EQUAL_RETURN(guid, MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY);
    IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_4CC);
    IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_WAVE_FORMAT_TAG);

    // Media types

    IF_EQUAL_RETURN(guid, MFMediaType_Audio);
    IF_EQUAL_RETURN(guid, MFMediaType_Video);
    IF_EQUAL_RETURN(guid, MFMediaType_Protected);
    IF_EQUAL_RETURN(guid, MFMediaType_SAMI);
    IF_EQUAL_RETURN(guid, MFMediaType_Script);
    IF_EQUAL_RETURN(guid, MFMediaType_Image);
    IF_EQUAL_RETURN(guid, MFMediaType_HTML);
    IF_EQUAL_RETURN(guid, MFMediaType_Binary);
    IF_EQUAL_RETURN(guid, MFMediaType_FileTransfer);

    IF_EQUAL_RETURN(guid, MFVideoFormat_AI44); //     FCC('AI44')
    IF_EQUAL_RETURN(guid, MFVideoFormat_ARGB32); //   D3DFMT_A8R8G8B8 
    IF_EQUAL_RETURN(guid, MFVideoFormat_AYUV); //     FCC('AYUV')
    IF_EQUAL_RETURN(guid, MFVideoFormat_DV25); //     FCC('dv25')
    IF_EQUAL_RETURN(guid, MFVideoFormat_DV50); //     FCC('dv50')
    IF_EQUAL_RETURN(guid, MFVideoFormat_DVH1); //     FCC('dvh1')
    IF_EQUAL_RETURN(guid, MFVideoFormat_DVSD); //     FCC('dvsd')
    IF_EQUAL_RETURN(guid, MFVideoFormat_DVSL); //     FCC('dvsl')
    IF_EQUAL_RETURN(guid, MFVideoFormat_H264); //     FCC('H264')
    IF_EQUAL_RETURN(guid, MFVideoFormat_I420); //     FCC('I420')
    IF_EQUAL_RETURN(guid, MFVideoFormat_IYUV); //     FCC('IYUV')
    IF_EQUAL_RETURN(guid, MFVideoFormat_M4S2); //     FCC('M4S2')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MJPG);
    IF_EQUAL_RETURN(guid, MFVideoFormat_MP43); //     FCC('MP43')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MP4S); //     FCC('MP4S')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MP4V); //     FCC('MP4V')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MPG1); //     FCC('MPG1')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MSS1); //     FCC('MSS1')
    IF_EQUAL_RETURN(guid, MFVideoFormat_MSS2); //     FCC('MSS2')
    IF_EQUAL_RETURN(guid, MFVideoFormat_NV11); //     FCC('NV11')
    IF_EQUAL_RETURN(guid, MFVideoFormat_NV12); //     FCC('NV12')
    IF_EQUAL_RETURN(guid, MFVideoFormat_P010); //     FCC('P010')
    IF_EQUAL_RETURN(guid, MFVideoFormat_P016); //     FCC('P016')
    IF_EQUAL_RETURN(guid, MFVideoFormat_P210); //     FCC('P210')
    IF_EQUAL_RETURN(guid, MFVideoFormat_P216); //     FCC('P216')
    IF_EQUAL_RETURN(guid, MFVideoFormat_RGB24); //    D3DFMT_R8G8B8 
    IF_EQUAL_RETURN(guid, MFVideoFormat_RGB32); //    D3DFMT_X8R8G8B8 
    IF_EQUAL_RETURN(guid, MFVideoFormat_RGB555); //   D3DFMT_X1R5G5B5 
    IF_EQUAL_RETURN(guid, MFVideoFormat_RGB565); //   D3DFMT_R5G6B5 
    IF_EQUAL_RETURN(guid, MFVideoFormat_RGB8);
    IF_EQUAL_RETURN(guid, MFVideoFormat_UYVY); //     FCC('UYVY')
    IF_EQUAL_RETURN(guid, MFVideoFormat_v210); //     FCC('v210')
    IF_EQUAL_RETURN(guid, MFVideoFormat_v410); //     FCC('v410')
    IF_EQUAL_RETURN(guid, MFVideoFormat_WMV1); //     FCC('WMV1')
    IF_EQUAL_RETURN(guid, MFVideoFormat_WMV2); //     FCC('WMV2')
    IF_EQUAL_RETURN(guid, MFVideoFormat_WMV3); //     FCC('WMV3')
    IF_EQUAL_RETURN(guid, MFVideoFormat_WVC1); //     FCC('WVC1')
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y210); //     FCC('Y210')
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y216); //     FCC('Y216')
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y410); //     FCC('Y410')
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y416); //     FCC('Y416')
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y41P);
    IF_EQUAL_RETURN(guid, MFVideoFormat_Y41T);
    IF_EQUAL_RETURN(guid, MFVideoFormat_YUY2); //     FCC('YUY2')
    IF_EQUAL_RETURN(guid, MFVideoFormat_YV12); //     FCC('YV12')
    IF_EQUAL_RETURN(guid, MFVideoFormat_YVYU);

    IF_EQUAL_RETURN(guid, MFAudioFormat_PCM); //              WAVE_FORMAT_PCM 
    IF_EQUAL_RETURN(guid, MFAudioFormat_Float); //            WAVE_FORMAT_IEEE_FLOAT 
    IF_EQUAL_RETURN(guid, MFAudioFormat_DTS); //              WAVE_FORMAT_DTS 
    IF_EQUAL_RETURN(guid, MFAudioFormat_Dolby_AC3_SPDIF); //  WAVE_FORMAT_DOLBY_AC3_SPDIF 
    IF_EQUAL_RETURN(guid, MFAudioFormat_DRM); //              WAVE_FORMAT_DRM 
    IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV8); //        WAVE_FORMAT_WMAUDIO2 
    IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV9); //        WAVE_FORMAT_WMAUDIO3 
    IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudio_Lossless); // WAVE_FORMAT_WMAUDIO_LOSSLESS 
    IF_EQUAL_RETURN(guid, MFAudioFormat_WMASPDIF); //         WAVE_FORMAT_WMASPDIF 
    IF_EQUAL_RETURN(guid, MFAudioFormat_MSP1); //             WAVE_FORMAT_WMAVOICE9 
    IF_EQUAL_RETURN(guid, MFAudioFormat_MP3); //              WAVE_FORMAT_MPEGLAYER3 
    IF_EQUAL_RETURN(guid, MFAudioFormat_MPEG); //             WAVE_FORMAT_MPEG 
    IF_EQUAL_RETURN(guid, MFAudioFormat_AAC); //              WAVE_FORMAT_MPEG_HEAAC 
    IF_EQUAL_RETURN(guid, MFAudioFormat_ADTS); //             WAVE_FORMAT_MPEG_ADTS_AAC 

    return NULL;
}


LPSTR DumpAttribute( _In_ const MF_ATTRIBUTE_TYPE& type,
    _In_ REFPROPVARIANT var)
{
    CHAR *tempStr = NULL;
    tempStr = new (std::nothrow) CHAR[256];
    switch (type)
    {
    case MF_ATTRIBUTE_UINT32:
        if (var.vt == VT_UI4)
        {
            sprintf_s(tempStr, 256, "%u", var.ulVal);
        }
        break;
    case MF_ATTRIBUTE_UINT64:
        if (var.vt == VT_UI8)
        {
            sprintf_s(tempStr, 256, "%I64d  (high: %d low: %d)", var.uhVal.QuadPart, var.uhVal.HighPart, var.uhVal.LowPart);
        }
        break;
    case MF_ATTRIBUTE_DOUBLE:
        if (var.vt == VT_R8)
        {
            sprintf_s(tempStr, 256, "%.4f", var.dblVal);
        }
        break;
    case MF_ATTRIBUTE_GUID:
        if (var.vt == VT_CLSID)
        {
            return DumpGUIDA(*var.puuid);
        }
        break;
    case MF_ATTRIBUTE_STRING:
        if (var.vt == VT_LPWSTR)
        {
            sprintf_s(tempStr, 256, "%S", var.pwszVal);
        }
        break;
    case MF_ATTRIBUTE_IUNKNOWN:
        break;
    default:
        printf("(Unknown Attribute Type = %d) ", type);
        break;
    }
    return tempStr;
}

CMediaTypePrinter::CMediaTypePrinter( 
    _In_ IMFMediaType *_pMediaType  )
    :   pMediaType(_pMediaType),
        m_pBuffer(NULL)
{
}

CMediaTypePrinter::~CMediaTypePrinter()
{
    if (m_pBuffer)
    {
        delete(m_pBuffer);
    }
}

/*++
Description:
Rudimentary function to print the complete Media type
--*/
PCHAR CMediaTypePrinter::ToCompleteString( )
{
    HRESULT             hr          = S_OK;
    UINT32              attrCount   = 0;
    GUID                attrGuid    = { 0 };
    char                *tempStore  = nullptr;
    PROPVARIANT var;
    LPSTR pTempBaseStr;
    MF_ATTRIBUTE_TYPE   pType;
    
    if ( pMediaType && !m_pBuffer )
    {
        DMFTCHECKHR_GOTO(pMediaType->GetCount(&attrCount), done);
        buffLen = MEDIAPRINTER_STARTLEN;
        m_pBuffer       = new char[buffLen];
        DMFTCHECKNULL_GOTO(m_pBuffer, done, E_OUTOFMEMORY);
        m_pBuffer[0]    = 0;
        for ( UINT32 ulIndex = 0; ulIndex < attrCount; ulIndex++ )
        {
            PropVariantInit( &var );
            checkAdjustBufferCap( m_pBuffer, buffLen );
            DMFTCHECKHR_GOTO( pMediaType->GetItemByIndex( ulIndex, &attrGuid, &var ), done );
            DMFTCHECKHR_GOTO( pMediaType->GetItemType( attrGuid, &pType ), done );
            if ( ulIndex > 0 )
                strcat_s(m_pBuffer, MEDIAPRINTER_STARTLEN, " : ");
            strcat_s( m_pBuffer, buffLen, GetGUIDNameConst( attrGuid ) );
            strcat_s( m_pBuffer, buffLen, "=" );
            pTempBaseStr = DumpAttribute( pType, var );
            strcat_s( m_pBuffer, buffLen, pTempBaseStr );
            delete( pTempBaseStr );
            PropVariantClear( &var );
        }
    done:
        if ( tempStore )
        {
            delete( tempStore );
        }
    }
    return m_pBuffer;
}

/*++
Description:
Rudimentary function to print the Media type
--*/

PCHAR CMediaTypePrinter::ToString()
{
    //
    //Following are the important ones of Mediatype attributes
    //

    HRESULT     hr = S_OK;
    PROPVARIANT var;
    LPSTR pTempBaseStr;
    MF_ATTRIBUTE_TYPE   pType;
    GUID                attrGuid;
    GUID impGuids[] = {
        MF_MT_SUBTYPE,
        MF_MT_FRAME_SIZE,
        MF_MT_SAMPLE_SIZE,
        MF_MT_FRAME_RATE,
        MF_MT_DEFAULT_STRIDE,
        MF_XVP_DISABLE_FRC
    };

    if (pMediaType && !m_pBuffer)
    {
        buffLen = MEDIAPRINTER_STARTLEN;
        m_pBuffer = new (std::nothrow) char[buffLen];
        DMFTCHECKNULL_GOTO(m_pBuffer, done, E_OUTOFMEMORY);
        m_pBuffer[0] = 0;
        for (UINT32 ulIndex = 0; ulIndex < ARRAYSIZE(impGuids); ulIndex++)
        {
            PropVariantInit(&var);
            checkAdjustBufferCap(m_pBuffer, buffLen);
            attrGuid = impGuids[ulIndex];
            DMFTCHECKHR_GOTO(pMediaType->GetItemType(attrGuid, &pType), done);
            DMFTCHECKHR_GOTO(pMediaType->GetItem(attrGuid, &var), done);
            if (ulIndex > 0)
                strcat_s(m_pBuffer, MEDIAPRINTER_STARTLEN, " : ");
            strcat_s(m_pBuffer, buffLen, GetGUIDNameConst(attrGuid));
            strcat_s(m_pBuffer, buffLen, "=");
            pTempBaseStr = DumpAttribute(pType, var);
            strcat_s(m_pBuffer, buffLen, pTempBaseStr);
            delete(pTempBaseStr);
            PropVariantClear(&var);
        }
    }
done:
    return m_pBuffer;
}
/*++
Description:
    Debug message printer to print the message passed through WPP
--*/
void printMessageEvent(MFT_MESSAGE_TYPE msg)
{
    switch (msg)
    {
    case MFT_MESSAGE_COMMAND_FLUSH:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_FLUSH");
        break;
    case MFT_MESSAGE_COMMAND_DRAIN:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_DRAIN");
        break;
    case MFT_MESSAGE_COMMAND_MARKER:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_MARKER");
        break;
    case MFT_MESSAGE_COMMAND_TICK:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_TICK");
        break;
    case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_END_OF_STREAM");
        break;
    case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_BEGIN_STREAMING");
        break;
    case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_START_OF_STREAM");
        break;
    case  MFT_MESSAGE_DROP_SAMPLES:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_DROP_SAMPLES");
        break;
    case MFT_MESSAGE_SET_D3D_MANAGER:
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_SET_D3D_MANAGER");
        break;

    }
}

//
// Used to check if the pin is a custom pin or not!!
//
STDMETHODIMP CheckCustomPin(
    _In_ CInPin * pPin,
    _Inout_ PBOOL  pIsCustom 
    )
{
    HRESULT hr = S_OK;
    DMFTCHECKNULL_GOTO( pPin, done, E_INVALIDARG);
    DMFTCHECKNULL_GOTO( pIsCustom, done, E_INVALIDARG);
    
    *pIsCustom = false;

#if defined _CPPRTTI
    if (dynamic_cast<CCustomPin*>(pPin) != nullptr)
    {
        *pIsCustom = true;
    }
    else
#endif
    {
        GUID categoryGUID = GUID_NULL;
        if (SUCCEEDED(pPin->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &categoryGUID))
            && IsEqualCLSID(categoryGUID, AVSTREAM_CUSTOM_PIN_IMAGE))
        {
            *pIsCustom = true;
        }
    }
done:
    return hr;
}


#ifdef MF_DEVICEMFT_ADD_GRAYSCALER_
//
// Please remove the below functions if you don't need them.
// Helper functions for the gray scaler
// 

void TransformImage_UYVY(
    const RECT &rcDest,
    _Inout_updates_(_Inexpressible_(lDestStride * dwHeightInPixels)) BYTE *pDest,
    _In_ LONG lDestStride,
    _In_reads_(_Inexpressible_(lSrcStride * dwHeightInPixels)) const BYTE *pSrc,
    _In_ LONG lSrcStride,
    _In_ DWORD dwWidthInPixels,
    _In_ DWORD dwHeightInPixels)
{
    DWORD y = 0;
    // Round down to the even value.
    const UINT32 left = rcDest.left & ~(1);
    const UINT32 right = rcDest.right & ~(1);
    const DWORD y0 = min((DWORD)rcDest.bottom, dwHeightInPixels);

    // Lines above the destination rectangle.
    for (; y < (DWORD)rcDest.top; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels * 2);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }

    // Lines within the destination rectangle.
    for (; y < y0; y++)
    {
        const WORD *pSrc_Pixel = reinterpret_cast<const WORD*>(pSrc);
        WORD *pDest_Pixel = reinterpret_cast<WORD*>(pDest);

        CopyMemory(pDest, pSrc, left * 2);
        for (DWORD x = left; (x + 1) < right; x += 2)
        {
            // Byte order is Y0 U0 Y1 V0
            // Each WORD is a byte pair (Y, U/V)
            // Windows is little-endian so the order appears reversed.

            DWORD tmp = *reinterpret_cast<const DWORD*>(&pSrc_Pixel[x]);
            *reinterpret_cast<DWORD*>(&pDest_Pixel[x]) = (tmp & 0xFF00FF00) | 0x00800080;
        }
        CopyMemory(pDest + (right * 2), pSrc + (right * 2), (dwWidthInPixels - right) * 2);

        pDest += lDestStride;
        pSrc += lSrcStride;
    }

    // Lines below the destination rectangle.
    for (; y < dwHeightInPixels; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels * 2);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }
}


// Convert YUY2 image.

void TransformImage_YUY2(
    const RECT &rcDest,
    _Inout_updates_(_Inexpressible_(lDestStride * dwHeightInPixels)) BYTE *pDest,
    _In_ LONG lDestStride,
    _In_reads_(_Inexpressible_(lSrcStride * dwHeightInPixels)) const BYTE *pSrc,
    _In_ LONG lSrcStride,
    _In_ DWORD dwWidthInPixels,
    _In_ DWORD dwHeightInPixels)
{
    DWORD y = 0;
    // Round down to the even value.
    const UINT32 left = rcDest.left & ~(1);
    const UINT32 right = rcDest.right & ~(1);
    const DWORD y0 = min((DWORD)rcDest.bottom, dwHeightInPixels);

    // Lines above the destination rectangle.
    for (; y < (DWORD)rcDest.top; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels * 2);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }

    // Lines within the destination rectangle.
    for (; y < y0; y++)
    {
        const WORD *pSrc_Pixel = reinterpret_cast<const WORD*>(pSrc);
        WORD *pDest_Pixel = reinterpret_cast<WORD*>(pDest);

        CopyMemory(pDest, pSrc, left * 2);
        for (DWORD x = left; (x + 1) < right; x += 2)
        {
            // Byte order is Y0 U0 Y1 V0
            // Each WORD is a byte pair (Y, U/V)
            // Windows is little-endian so the order appears reversed.

            DWORD tmp = *reinterpret_cast<const DWORD*>(&pSrc_Pixel[x]);
            *reinterpret_cast<DWORD*>(&pDest_Pixel[x]) = (tmp & 0x00FF00FF) | 0x80008000;
        }
        CopyMemory(pDest + (right * 2), pSrc + (right * 2), (dwWidthInPixels - right) * 2);

        pDest += lDestStride;
        pSrc += lSrcStride;
    }

    // Lines below the destination rectangle.
    for (; y < dwHeightInPixels; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels * 2);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }
}

// Convert NV12 image

void TransformImage_NV12(
    const RECT &rcDest,
    _Inout_updates_(_Inexpressible_(2 * lDestStride * dwHeightInPixels)) BYTE *pDest,
    _In_ LONG lDestStride,
    _In_reads_(_Inexpressible_(2 * lSrcStride * dwHeightInPixels)) const BYTE *pSrc,
    _In_ LONG lSrcStride,
    _In_ DWORD dwWidthInPixels,
    _In_ DWORD dwHeightInPixels)
{
    // NV12 is planar: Y plane, followed by packed U-V plane.

    // Y plane
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels);
        pDest += lDestStride;
        pSrc += lSrcStride;
    }

    // U-V plane

    // NOTE: The U-V plane has 1/2 the number of lines as the Y plane.

    // Lines above the destination rectangle.
    DWORD y = 0;
    const DWORD y0 = min((DWORD)rcDest.bottom, dwHeightInPixels);

    for (; y < (DWORD)rcDest.top / 2; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }

    // Lines within the destination rectangle.
    for (; y < y0 / 2; y++)
    {
        CopyMemory(pDest, pSrc, rcDest.left);
        FillMemory(pDest + rcDest.left, rcDest.right - rcDest.left, 128);
        CopyMemory(pDest + rcDest.right, pSrc + rcDest.right, dwWidthInPixels - rcDest.right);
        pDest += lDestStride;
        pSrc += lSrcStride;
    }

    // Lines below the destination rectangle.
    for (; y < dwHeightInPixels / 2; y++)
    {
        CopyMemory(pDest, pSrc, dwWidthInPixels);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }
}



void TransformImage_RGB32(
    const RECT &rcDest,
    _Inout_updates_(_Inexpressible_(2 * lDestStride * dwHeightInPixels)) BYTE *pDest,
    _In_ LONG lDestStride,
    _In_reads_(_Inexpressible_(2 * lSrcStride * dwHeightInPixels)) const BYTE *pSrc,
    _In_ LONG lSrcStride,
    _In_ DWORD dwWidthInPixels,
    _In_ DWORD dwHeightInPixels)
{
    DWORD y = 0;
    const DWORD y0 = min((DWORD)rcDest.bottom, dwHeightInPixels);

    // Lines above the destination rectangle.
    for (; y < (DWORD)rcDest.top; y++)
    {
        memcpy(pDest, pSrc, dwWidthInPixels * 4);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }

    // Lines within the destination rectangle.
    // Use the grayscale conversion Red = 30%, Green = 59%, Blue = 11%
    for (; y < y0; y++)
    {
        DWORD *pSrc_Pixel = (DWORD*)pSrc;
        DWORD *pDest_Pixel = (DWORD*)pDest;

        for (DWORD x = 0; (x + 1) < dwWidthInPixels; x++)
        {
            // Byte order is X8 R8 G8 B8

            if (x >= (DWORD)rcDest.left && x < (DWORD)rcDest.right)
            {
                WORD color;
                color = (pSrc_Pixel[x] & 0x000000FF);
                int r = (int)(color * 0.3);
                color = (pSrc_Pixel[x] & 0x0000FF00) >> 8;
                int g = (int)(color * 0.59);
                color = (pSrc_Pixel[x] & 0x00FF0000) >> 16;
                int b = (int)(color * 0.11);
                int gray = r + g + b;

                pDest_Pixel[x] = (gray & 0x000000FF) << 16 |
                    (gray & 0x000000FF) << 8 |
                    (gray & 0x000000FF);
            }
            else
            {
#pragma warning(push)
#pragma warning(disable: 6385)
#pragma warning(disable: 6386)
                pDest_Pixel[x] = pSrc_Pixel[x];
#pragma warning(pop)
            }
        }

        pDest += lDestStride;
        pSrc += lSrcStride;
    }

    // Lines below the destination rectangle.
    for (; y < dwHeightInPixels; y++)
    {
        memcpy(pDest, pSrc, dwWidthInPixels * 4);
        pSrc += lSrcStride;
        pDest += lDestStride;
    }
}

#endif

HRESULT GetDXGIAdapterLuid(_In_ IMFDXGIDeviceManager *pDXManager, _Out_ LUID &AdapterLuid)
{
    HRESULT hr = S_OK;
    ComPtr<ID3D11Device> spDevice;
    ComPtr<IDXGIDevice> spDXGIDevice;
    ComPtr<IDXGIAdapter> spDXGIAdapter;
    DXGI_ADAPTER_DESC adapterDesc = { 0 };
    HANDLE hDevice = NULL;

    DMFTCHECKNULL_GOTO(pDXManager, done, E_INVALIDARG);
  
    DMFTCHECKHR_GOTO(pDXManager->OpenDeviceHandle(&hDevice), done);
    DMFTCHECKHR_GOTO(pDXManager->GetVideoService(hDevice, IID_PPV_ARGS(&spDevice)), done);
    DMFTCHECKHR_GOTO(spDevice->QueryInterface(IID_PPV_ARGS(&spDXGIDevice)), done);
    DMFTCHECKHR_GOTO(spDXGIDevice->GetAdapter(&spDXGIAdapter), done);
    DMFTCHECKHR_GOTO(spDXGIAdapter->GetDesc(&adapterDesc), done);

    CopyMemory(&AdapterLuid, &adapterDesc.AdapterLuid, sizeof(LUID));

done:
    if (NULL != hDevice)
    {
        pDXManager->CloseDeviceHandle(hDevice);
    }

    return hr;
}

HRESULT EnumSWDecoder(_Outptr_ IMFTransform** ppTransform, _In_ GUID subType)
{
    HRESULT hr = S_OK;
    IMFActivate** ppActivate = nullptr;
    UINT32 count = 0;
    ComPtr<IMFTransform> spTransform;
    LPWSTR pszFriendlyName = nullptr;

    MFT_REGISTER_TYPE_INFO info = { MFMediaType_Video, subType };
    UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT |
        MFT_ENUM_FLAG_LOCALMFT |
        MFT_ENUM_FLAG_SORTANDFILTER;

    DMFTCHECKNULL_GOTO(ppTransform, done, E_INVALIDARG);
    *ppTransform = nullptr;

    DMFTCHECKHR_GOTO(MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, unFlags,
        &info,      // Input type
        NULL,       // Output type
        &ppActivate,
        &count
    ), done);


    if (count == 0)
    {
        DMFTCHECKHR_GOTO(MF_E_TOPO_CODEC_NOT_FOUND, done);
    }

    if (SUCCEEDED(MFGetAttributeString(ppActivate[0], MFT_FRIENDLY_NAME_Attribute, &pszFriendlyName)))
    {
        //
        // Log the friendly name of the decoder
        //
        DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! decoder name: %S ", (pszFriendlyName ? pszFriendlyName : L"UnknownCodec"));
        SAFE_COTASKMEMFREE(pszFriendlyName);
    }

    DMFTCHECKHR_GOTO(ppActivate[0]->ActivateObject(IID_PPV_ARGS(spTransform.GetAddressOf())), done);

    *ppTransform = spTransform.Detach();

done:
    if (ppActivate)
    {
        for (UINT32 uiIndex = 0; uiIndex < count; uiIndex++)
        {
            ppActivate[uiIndex]->Release();
        }
        CoTaskMemFree(ppActivate);
    }
    return hr;
}
//
// @@@@ README Create the decoder
//
HRESULT CreateDecoderFromLuid( _In_ LUID ullAdapterLuidRunningOn,
    _In_ IMFMediaType* pInputType,
    _In_ IMFMediaType* pOutputType,
    _Inout_ BOOL fHwMft,
    _Outptr_ IMFTransform** ppDecoder)
{
    HRESULT hr = S_OK;
    ComPtr<IMFAttributes> spAttribs;
    ComPtr<IMFTransform> spMJPGDecoder;
    IMFActivate **ppActivates = nullptr;
    DWORD dwFlags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_HARDWARE | 0x10000000 /*Internal attribute*/;
    UINT32 cMFTActivate = 0;
    MFT_REGISTER_TYPE_INFO InputType;
    MFT_REGISTER_TYPE_INFO OutputType;
    GUID                   compressedGUID;
    ComPtr<ID3D11Device>  spD3D11Device;

    fHwMft = FALSE;

    
    DMFTCHECKNULL_GOTO(pInputType, done, E_INVALIDARG);
    DMFTCHECKNULL_GOTO(pOutputType, done, E_INVALIDARG);
    DMFTCHECKNULL_GOTO(ppDecoder, done, E_INVALIDARG);
    *ppDecoder = nullptr;
    DMFTCHECKHR_GOTO(pInputType->GetGUID(MF_MT_SUBTYPE, &compressedGUID), done);

    InputType.guidMajorType = MFMediaType_Video;
    InputType.guidSubtype = compressedGUID;

    DMFTCHECKHR_GOTO(pOutputType->GetMajorType(&OutputType.guidMajorType), done);
    DMFTCHECKHR_GOTO(pOutputType->GetGUID(MF_MT_SUBTYPE, &OutputType.guidSubtype), done);

    DMFTCHECKHR_GOTO(MFCreateAttributes(&spAttribs, 1), done);
    DMFTCHECKHR_GOTO(spAttribs->SetBlob(MFT_ENUM_ADAPTER_LUID, (byte*)&ullAdapterLuidRunningOn, sizeof(ullAdapterLuidRunningOn)), done);
    DMFTCHECKHR_GOTO(MFTEnum2(MFT_CATEGORY_VIDEO_DECODER, dwFlags, &InputType, &OutputType, spAttribs.Get(), &ppActivates, &cMFTActivate), done);

    for (DWORD i = 0; i < cMFTActivate; i++)
    {
        HRESULT hrTemp = ppActivates[i]->ActivateObject(IID_PPV_ARGS(spMJPGDecoder.GetAddressOf()));
        fHwMft = (MFT_ENUM_FLAG_HARDWARE & MFGetAttributeUINT32(ppActivates[i], MF_TRANSFORM_FLAGS_Attribute, 0));
        // Pickup the first MFT enumerated. Normally, it should be a HW MFT, if HWMFT is available.
        if (SUCCEEDED(hrTemp))
        {
            break;
        }
        else
        {
            spMJPGDecoder = nullptr;
        }
    }
    if (spMJPGDecoder == nullptr)
    {
        hr = MF_E_NOT_FOUND;
    }
    *ppDecoder = spMJPGDecoder.Detach();
done:

    for (UINT32 i = 0; i < cMFTActivate; i++)
    {
        if (nullptr != ppActivates[i])
        {
            ppActivates[i]->Release();
        }
    }
    SAFE_COTASKMEMFREE(ppActivates);
    return hr;
}

HRESULT SetDX11BindFlags( _In_  IUnknown *pUnkManager,
    _In_ GUID guidPinCategory,
    _Inout_ DWORD &dwBindFlags)
{
    HRESULT hr = S_OK;
    ComPtr<IUnknown> spDeviceManager;
    BOOL fFL10 = FALSE;
    
    auto
        IsFeatureLevel10OrBetter = [&]() -> BOOL 
    {
        ComPtr<IMFDXGIDeviceManager> spDXGIManager;
        ComPtr<ID3D11Device> spDevice;
        HANDLE hDevice = 0;
        BOOL fRet = FALSE;
        UINT level = 0, result = 0;
        
        if (SUCCEEDED(pUnkManager->QueryInterface(IID_PPV_ARGS(&spDXGIManager))) &&
            SUCCEEDED(spDXGIManager->OpenDeviceHandle(&hDevice)) &&
            SUCCEEDED(spDXGIManager->LockDevice(hDevice, IID_PPV_ARGS(&spDevice), FALSE)))
        {

            level = (UINT)spDevice->GetFeatureLevel();
            fRet = (level >= D3D_FEATURE_LEVEL_10_0);
            if (!fRet)
            {
                if (SUCCEEDED(spDevice->CheckFormatSupport(DXGI_FORMAT_NV12, &result)))
                {
                    fRet = (result & D3D11_FORMAT_SUPPORT_TEXTURE2D) && (result & D3D11_FORMAT_SUPPORT_RENDER_TARGET);
                }
            }
            (void)spDXGIManager->UnlockDevice(hDevice, FALSE);
            (void)spDXGIManager->CloseDeviceHandle(hDevice);
        }
        return fRet;
    };

    DMFTCHECKNULL_GOTO(pUnkManager, done, E_INVALIDARG);
    fFL10 = IsFeatureLevel10OrBetter();

    dwBindFlags |= fFL10 ? D3D11_BIND_SHADER_RESOURCE : 0;
    dwBindFlags |= (guidPinCategory != PINNAME_VIDEO_PREVIEW) ? D3D11_BIND_VIDEO_ENCODER : 0;
    if ((dwBindFlags != 0) && ((dwBindFlags & ~(D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_VIDEO_ENCODER)) == 0))
    {
        dwBindFlags |= D3D11_BIND_RENDER_TARGET;
    }
done:
    return hr;
}

// @@@@ README: Create the Decoder
HRESULT CreateDecoderHW(_In_ IMFDXGIDeviceManager* pManager,
    _In_ IMFMediaType* inType,
    _In_ IMFMediaType* outType,
    _Outptr_ IMFTransform** ppTransform,
    _Inout_  BOOL&           fHwMft)
{
    HRESULT hr = S_OK;
    LUID luid;
    ComPtr<IMFTransform> spTransform;
    DMFTCHECKNULL_GOTO(ppTransform, done, E_INVALIDARG);
    *ppTransform = nullptr;

    if (pManager)
    {
        if (SUCCEEDED(hr = GetDXGIAdapterLuid(pManager, luid)))
        {
            hr = CreateDecoderFromLuid(luid, inType, outType, fHwMft, spTransform.GetAddressOf());
            if (FAILED(hr))
            {
                DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Error creating HW Decoder with LUID %d-%d", luid.HighPart, luid.LowPart);
                DMFTCHECKHR_GOTO(hr, done);
            }
        }
    }
    *ppTransform = spTransform.Detach();
done:
    return hr;
}

HRESULT
SubtypeToDXGIFormat(
    _In_    GUID subType,
    _Inout_ DXGI_FORMAT &format
)
{
    HRESULT hr = S_OK;
    typedef struct {
        GUID subType;
        DXGI_FORMAT format;
    }DXGIFormatMap;
    DXGIFormatMap formatMap[]
    {
    { MFVideoFormat_NV12,      DXGI_FORMAT_NV12 },
    { MFVideoFormat_YUY2,      DXGI_FORMAT_YUY2 },
    { MFVideoFormat_RGB32,      DXGI_FORMAT_B8G8R8X8_UNORM },
    { MFVideoFormat_ARGB32,     DXGI_FORMAT_B8G8R8A8_UNORM },
    { MFVideoFormat_AYUV,      DXGI_FORMAT_AYUV },
    { MFVideoFormat_NV11,      DXGI_FORMAT_NV11 },
    { MFVideoFormat_AI44,      DXGI_FORMAT_AI44 },
    { MFVideoFormat_P010,      DXGI_FORMAT_P010 },
    { MFVideoFormat_P016,      DXGI_FORMAT_P016 },
    { MFVideoFormat_Y210,      DXGI_FORMAT_Y210 },
    { MFVideoFormat_Y216,      DXGI_FORMAT_Y216 },
    { MFVideoFormat_Y410,      DXGI_FORMAT_Y410 },
    { MFVideoFormat_Y416,      DXGI_FORMAT_Y416 },
    };
    format = DXGI_FORMAT_UNKNOWN;
    for (UINT32 uiIndex = 0; uiIndex < _countof(formatMap); uiIndex++)
    {
        if (formatMap[uiIndex].subType == subType)
        {
            format = formatMap[uiIndex].format;
            break;
        }
    }
    // compressed media type or unsupported of the range
    hr = (format == DXGI_FORMAT_UNKNOWN) ? E_NOTIMPL : S_OK;
    return hr;
}

HRESULT IsDXFormatSupported(
    _In_ IMFDXGIDeviceManager* pDeviceManager ,
    _In_ GUID subType,
    _Outptr_opt_ ID3D11Device** ppDevice,
    _In_opt_ PUINT32 pSupportedFormat)
{
    HRESULT hr = S_OK;
    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
    UINT32 supportedFormat = 0;
    HANDLE hDevice = NULL;
    ComPtr<ID3D11Device> spDevice;
    DMFTCHECKNULL_GOTO(pDeviceManager, done, E_INVALIDARG);
    DMFTCHECKHR_GOTO(pDeviceManager->OpenDeviceHandle(&hDevice), done);
    DMFTCHECKHR_GOTO(pDeviceManager->GetVideoService(hDevice, IID_PPV_ARGS(&spDevice)), done);
    DMFTCHECKHR_GOTO(SubtypeToDXGIFormat(subType, format), done);
    DMFTCHECKHR_GOTO(spDevice->CheckFormatSupport(format, &supportedFormat), done);
    if (pSupportedFormat)
    {
        *pSupportedFormat = supportedFormat;
    }
    if (ppDevice)
    {
        *ppDevice = spDevice.Detach();
    }
done:
    if (hDevice != NULL)
    {
        pDeviceManager->CloseDeviceHandle(hDevice);
    }
    return hr;
}

HRESULT
UpdateAllocatorAttributes(
    _In_ IMFAttributes *pAttributes,
    _In_ REFGUID guidStreamCategory,
    _In_ GUID subType,
    _In_ IMFDXGIDeviceManager *pDeviceManager
)
{
    HRESULT hr = S_OK;
    UINT32 supportedFormat = 0;
    ComPtr<ID3D11Device> spDevice;
    DWORD dwBindFlags = 0;

    DMFTCHECKHR_GOTO(IsDXFormatSupported(pDeviceManager,subType,
        spDevice.GetAddressOf(),&supportedFormat),done);
 
    // MF_SA_D3D11_USAGE should be D3D11_USAGE_DEFAULT, same as previous release.
    DMFTCHECKHR_GOTO(pAttributes->SetUINT32(MF_SA_D3D11_USAGE, D3D11_USAGE_DEFAULT), done);

    DMFTCHECKHR_GOTO(pAttributes->SetUINT32(MF_SA_D3D11_SHARED_WITHOUT_MUTEX, TRUE), done);
    DMFTCHECKHR_GOTO(pAttributes->SetUINT32(MF_SA_D3D11_SHARED_WITH_NTHANDLE_PRIVATE, TRUE), done);

    // Bind Flags
    dwBindFlags |= ((supportedFormat & D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0 ? D3D11_BIND_RENDER_TARGET : 0);

    if (((supportedFormat & D3D11_FORMAT_SUPPORT_VIDEO_ENCODER) != 0) &&
        (!IsEqualGUID(guidStreamCategory, PINNAME_VIDEO_PREVIEW)) &&
        IsEqualGUID(subType, MFVideoFormat_NV12))
    {
        D3D11_FEATURE_DATA_D3D11_OPTIONS4 Options = { 0 };

        // "D3D11_BIND_VIDEO_ENCODER + sharing" is only supported on those devices that
        // advertise ExtendedNV12SharedTextureSupported.
        DMFTCHECKHR_GOTO(spDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS4, &Options, sizeof(Options)), done);
        if (Options.ExtendedNV12SharedTextureSupported)
        {
            dwBindFlags |= D3D11_BIND_VIDEO_ENCODER;
        }
    }

    // D3D requires one of the flags (D3D11_BIND_RENDER_TARGET, D3D11_PRIVATE_BIND_CAPTURE, or D3D11_BIND_VIDEO_ENCODER) to be set for non-zero bind flags.
    if (dwBindFlags != 0)
    {
        D3D_FEATURE_LEVEL level;

        // D3D11_BIND_SHADER_RESOURCE
        level = spDevice->GetFeatureLevel();
        dwBindFlags |= ((level >= D3D_FEATURE_LEVEL_10_0) ? D3D11_BIND_SHADER_RESOURCE : 0);
    }

    DMFTCHECKHR_GOTO(pAttributes->SetUINT32(MF_SA_D3D11_BINDFLAGS, dwBindFlags), done);

done:
    return hr;
}

const UINT32 ALLOCATOR_MIN_SAMPLES = 10;
const UINT32 ALLOCATOR_MAX_SAMPLES = 50;

HRESULT ConfigureAllocator(
    _In_ IMFMediaType* pOutputMediaType,
    _In_ GUID streamCategory,
    _In_ IUnknown* pDeviceManagerUnk,
    _In_ BOOL &bDxAllocator,
    _In_ IMFVideoSampleAllocator* pAllocator)
{
    HRESULT hr = S_OK;
    wil::com_ptr_nothrow<IMFVideoSampleAllocatorEx> spPrivateAllocator;
#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB))
    wil::com_ptr_nothrow<IMFVideoCaptureSampleAllocator> spDefaultAllocator;
#endif
    wil::com_ptr_nothrow<IMFAttributes> spAllocatorAttributes;
    GUID guidMajorType = GUID_NULL;
    GUID guidSubtype = GUID_NULL;
    BOOL fDXAllocator = FALSE;

    RETURN_HR_IF_NULL(E_INVALIDARG, pAllocator );
    RETURN_HR_IF_NULL(E_INVALIDARG, pOutputMediaType );

    RETURN_IF_FAILED(pOutputMediaType->GetMajorType(&guidMajorType));

    if (!IsEqualGUID(guidMajorType, MFMediaType_Video))
    {
        RETURN_HR(MF_E_INVALIDMEDIATYPE);
    }

    RETURN_IF_FAILED(pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype));
    //
    // Set Attributes on the allocator we need. First get the Bind Flags.
    //
    RETURN_IF_FAILED(MFCreateAttributes(&spAllocatorAttributes, 8));
    RETURN_IF_FAILED(spAllocatorAttributes->SetUINT32(MF_SA_BUFFERS_PER_SAMPLE, 1));

    if (pDeviceManagerUnk != nullptr)
    {
        if (SUCCEEDED(UpdateAllocatorAttributes(spAllocatorAttributes.get(), streamCategory, guidSubtype, (IMFDXGIDeviceManager*)pDeviceManagerUnk)))
        {
            fDXAllocator = TRUE;
        }
    }

    if (fDXAllocator)
    {
        RETURN_IF_FAILED(pAllocator->SetDirectXManager(pDeviceManagerUnk));
    }

    if (SUCCEEDED(pAllocator->QueryInterface(IID_PPV_ARGS(&spPrivateAllocator))))
    {
        RETURN_IF_FAILED(spPrivateAllocator->InitializeSampleAllocatorEx(
            ALLOCATOR_MIN_SAMPLES, 
            ALLOCATOR_MAX_SAMPLES, 
            spAllocatorAttributes.get(), 
            pOutputMediaType));
    }
#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB))
    else if (SUCCEEDED(pAllocator->QueryInterface(IID_PPV_ARGS(&spDefaultAllocator))))
    {
        RETURN_IF_FAILED(spDefaultAllocator->InitializeCaptureSampleAllocator(
            0, /*use sample size by MediaType*/
            0, /*metadata size*/
            0, /*default alignment*/
            ALLOCATOR_MIN_SAMPLES,
            spAllocatorAttributes.get(),
            pOutputMediaType));
    }
#endif
    else
    {
        hr = E_INVALIDARG;
    }

    bDxAllocator = fDXAllocator;

    return hr;
}
//
//@@@@ README: Creating an Allocator.. Please don't allocate samples individually using MFCreateSample as that can lead to fragmentation and is
// extremely inefficent. Instead create an Allocator which will create a fixed number of samples which are recycled when the pipeline returns back
// the buffers. The above has defines will enable to create a circular allocator which will have maximum 20 samples flowing in the pipeline
// This way we can also catch samples leaks if any.
//
HRESULT CreateAllocator( _In_ IMFMediaType* pOutputMediaType,
    _In_ GUID streamCategory,
    _In_ IUnknown* pDeviceManagerUnk,
    _In_ BOOL &bDxAllocator,
    _Outptr_ IMFVideoSampleAllocatorEx** ppAllocator )
{
    HRESULT hr = S_OK;
    ComPtr<IMFVideoSampleAllocatorEx> spVideoSampleAllocator;
    ComPtr<IMFAttributes> spAllocatorAttributes;
    GUID guidMajorType = GUID_NULL;
    GUID guidSubtype = GUID_NULL;
    BOOL fDXAllocator = FALSE;

    DMFTCHECKNULL_GOTO(ppAllocator, done, E_INVALIDARG);
    DMFTCHECKNULL_GOTO(pOutputMediaType, done, E_INVALIDARG);
    DMFTCHECKHR_GOTO(pOutputMediaType->GetMajorType(&guidMajorType), done);

    *ppAllocator = nullptr;
    if (!IsEqualGUID(guidMajorType, MFMediaType_Video))
    {
        DMFTCHECKHR_GOTO(MF_E_INVALIDMEDIATYPE, done);
    }

    DMFTCHECKHR_GOTO(pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype), done);
    //
    // Set Attributes on the allocator we need. First get the Bind Flags.
    //
    DMFTCHECKHR_GOTO(MFCreateAttributes(&spAllocatorAttributes, 8), done);
    DMFTCHECKHR_GOTO(spAllocatorAttributes->SetUINT32(MF_SA_BUFFERS_PER_SAMPLE, 1), done);

    if (pDeviceManagerUnk != nullptr)
    {
         if (SUCCEEDED(UpdateAllocatorAttributes(spAllocatorAttributes.Get(), streamCategory,guidSubtype, (IMFDXGIDeviceManager*)pDeviceManagerUnk)))
         {
             fDXAllocator = TRUE;
         }
    }
    DMFTCHECKHR_GOTO(MFCreateVideoSampleAllocatorEx(IID_PPV_ARGS(spVideoSampleAllocator.ReleaseAndGetAddressOf())), done);
    if (fDXAllocator)
    {
        DMFTCHECKHR_GOTO(spVideoSampleAllocator->SetDirectXManager(pDeviceManagerUnk), done);
    }

    DMFTCHECKHR_GOTO(spVideoSampleAllocator->InitializeSampleAllocatorEx(ALLOCATOR_MIN_SAMPLES, ALLOCATOR_MAX_SAMPLES, spAllocatorAttributes.Get(), pOutputMediaType), done);
    
    *ppAllocator = spVideoSampleAllocator.Detach();
    bDxAllocator = fDXAllocator;
done:
    return hr;
}

HRESULT CheckPassthroughMediaType(_In_ IMFMediaType *pMediaType1,
    _In_ IMFMediaType *pMediaType2,
    _Out_ BOOL& pfPassThrough)
{
    HRESULT hr = S_OK;
    BOOL fPassThrough = FALSE;
    UINT32 uWidth, uHeight;
    UINT32 uWidth2, uHeight2;

    // Compare Width/Height.
    DMFTCHECKHR_GOTO(MFGetAttributeSize(pMediaType1, MF_MT_FRAME_SIZE, &uWidth, &uHeight), done);
    DMFTCHECKHR_GOTO(MFGetAttributeSize(pMediaType2, MF_MT_FRAME_SIZE, &uWidth2, &uHeight2), done);
    if ((uWidth != uWidth2) || (uHeight != uHeight2))
    {
        goto done;
    }

    // Compare rotation
    {
        UINT32 uRotation;
        UINT32 uRotation2;

        uRotation = MFGetAttributeUINT32(pMediaType1, MF_MT_VIDEO_ROTATION, 0);
        uRotation2 = MFGetAttributeUINT32(pMediaType2, MF_MT_VIDEO_ROTATION, 0);
        if (uRotation != uRotation2)
        {
            goto done;
        }
    }

    // Compare Aspect Ratio
    {
        UINT32 uNumerator = 1, uDenominator = 1;
        UINT32 uNumerator2 = 1, uDenominator2 = 1;

        (void)MFGetAttributeRatio(pMediaType1, MF_MT_FRAME_RATE, &uNumerator, &uDenominator);
        (void)MFGetAttributeRatio(pMediaType2, MF_MT_FRAME_RATE, &uNumerator2, &uDenominator2);
        if ((uNumerator * uDenominator2) != (uNumerator2 * uDenominator))
        {
            goto done;
        }
    }

    // Compare Nominal Range
    {
        UINT32 uNorminalRange;
        UINT32 uNorminalRange2;

        uNorminalRange = MFGetAttributeUINT32(pMediaType1, MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Unknown);
        uNorminalRange2 = MFGetAttributeUINT32(pMediaType2, MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Unknown);
        if (uNorminalRange != uNorminalRange2)
        {
            goto done;
        }
    }

    // Compare color primaries
    {
        UINT32 uPrimaries;
        UINT32 uPrimaries2;

        uPrimaries = MFGetAttributeUINT32(pMediaType1, MF_MT_VIDEO_PRIMARIES, MFVideoPrimaries_Unknown);
        uPrimaries2 = MFGetAttributeUINT32(pMediaType2, MF_MT_VIDEO_PRIMARIES, MFVideoPrimaries_Unknown);
        if (uPrimaries != uPrimaries2)
        {
            goto done;
        }
    }


    // Compare InterlaceMode
    {
        UINT32 uInterlaceMode;
        UINT32 uInterlaceMode2;

        uInterlaceMode = MFGetAttributeUINT32(pMediaType1, MF_MT_INTERLACE_MODE, MFVideoInterlace_Unknown);
        uInterlaceMode2 = MFGetAttributeUINT32(pMediaType2, MF_MT_INTERLACE_MODE, MFVideoInterlace_Unknown);
        if (uInterlaceMode != uInterlaceMode2)
        {
            goto done;
        }
    }

    // Compare display aperture
    {
        MFVideoArea VideoArea = { 0 };
        MFVideoArea VideoArea2 = { 0 };
        UINT32 cbBlobSize = 0;

        if (SUCCEEDED(pMediaType1->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&VideoArea, sizeof(MFVideoArea), &cbBlobSize)))
        {
            if ((VideoArea.OffsetX.value == 0 && VideoArea.OffsetX.fract == 0) &&
                (VideoArea.OffsetY.value == 0 && VideoArea.OffsetY.fract == 0) &&
                (VideoArea.Area.cx == (LONG)uWidth) &&
                (VideoArea.Area.cy == (LONG)uHeight))
            {
                // If the display aperture is the whole frame size, ignore it.
                ZeroMemory(&VideoArea, sizeof(VideoArea));
            }
        }

        if (SUCCEEDED(pMediaType2->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&VideoArea2, sizeof(MFVideoArea), &cbBlobSize)))
        {
            if ((VideoArea2.OffsetX.value == 0 && VideoArea2.OffsetX.fract == 0) &&
                (VideoArea2.OffsetY.value == 0 && VideoArea2.OffsetY.fract == 0) &&
                (VideoArea2.Area.cx == (LONG)uWidth) &&
                (VideoArea2.Area.cy == (LONG)uHeight))
            {
                // If the display aperture is the whole frame size, ignore it.
                ZeroMemory(&VideoArea, sizeof(VideoArea));
            }
        }

        if (memcmp(&VideoArea, &VideoArea2, sizeof(MFVideoArea)) != 0)
        {
            goto done;
        }
    }

    fPassThrough = TRUE;

done:
    pfPassThrough = fPassThrough;
    return hr;
}


HRESULT MergeSampleAttributes( _In_ IMFSample* pInSample, _Inout_ IMFSample* pOutSample)
{
    HRESULT hr = S_OK;
    UINT32 cAttributes = 0;
    GUID guidAttribute;
    PROPVARIANT varAttribute;
    PROPVARIANT varAttributeExists;
    PropVariantInit(&varAttribute);
    PropVariantInit(&varAttributeExists);

    DMFTCHECKNULL_GOTO(pInSample, done, E_INVALIDARG);
    DMFTCHECKNULL_GOTO(pOutSample, done, E_INVALIDARG);

    DMFTCHECKHR_GOTO(pInSample->GetCount(&cAttributes), done);
    for (UINT32 i = 0; i < cAttributes; i++)
    {
        PropVariantClear(&varAttribute);
        PropVariantClear(&varAttributeExists);

        DMFTCHECKHR_GOTO(pInSample->GetItemByIndex(i, &guidAttribute, &varAttribute), done);

        if (S_OK == pOutSample->GetItem(guidAttribute, &varAttributeExists))
        {
            // Exists.. dont clobber    
            continue;
        }

        DMFTCHECKHR_GOTO(pOutSample->SetItem(guidAttribute, varAttribute), done);
    }

done:
    PropVariantClear(&varAttribute);
    PropVariantClear(&varAttributeExists);
    return hr;
}


<|start_filename|>audio/sysvad/EndpointsCommon/usbhsspeakertopo.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsspeakertopo.h

Abstract:

    Declaration of topology miniport for the USB Headset speaker (external).

--*/

#ifndef _SYSVAD_USBHSSPEAKERTOPO_H_
#define _SYSVAD_USBHSSPEAKERTOPO_H_

// Function declarations.
NTSTATUS
PropertyHandler_UsbHsSpeakerTopoFilter( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
    );

NTSTATUS
PropertyHandler_UsbHsSpeakerTopoFilterEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

#endif // _SYSVAD_USBHSSPEAKERTOPO_H_



<|start_filename|>audio/sysvad/EndpointsCommon/usbhstopo.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhstopo.h

Abstract:

    Declaration of topology miniport for the mic (external: USB headphone).

--*/

#ifndef _SYSVAD_USBHSTOPO_H_
#define _SYSVAD_USBHSTOPO_H_

NTSTATUS
PropertyHandler_UsbHsVolumeLevel_BasicSupport( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_UsbHsMicVolumeLevel(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_UsbHsMute_BasicSupport( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_UsbHsMicMute(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_UsbHsJackDescription( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest, 
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *    JackDescriptions
    );

NTSTATUS
PropertyHandler_UsbHsJackDescription2( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *    JackDescriptions
    );

NTSTATUS
PropertyHandler_UsbHsJackContainerId( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *     JackDescriptions
    );

NTSTATUS 
PropertyHandler_UsbHsTopoNodeEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

#endif // _SYSVAD_USBHSTOPO_H_



<|start_filename|>network/wlan/WDI/COMMON/ChannelInfo.c<|end_filename|>
#include "Mp_Precomp.h"

#if WPP_SOFTWARE_TRACE
#include "ChannelInfo.tmh"
#endif

u1Byte	
CHNL_GetCenterFrequency(
	IN	u1Byte				Channel,
	IN	CHANNEL_WIDTH		ChnlBW,
	IN	EXTCHNL_OFFSET 		ExtChnlOffset
)
{
	u1Byte 	CenterFrequency = Channel;

	if(ChnlBW == CHANNEL_WIDTH_80)
	{
		if((Channel == 36) || (Channel == 40) || (Channel == 44) || (Channel == 48) )
			CenterFrequency = 42;
		if((Channel == 52) || (Channel == 56) || (Channel == 60) || (Channel == 64) )
			CenterFrequency = 58;
		if((Channel == 100) || (Channel == 104) || (Channel == 108) || (Channel == 112) )
			CenterFrequency = 106;
		if((Channel == 116) || (Channel == 120) || (Channel == 124) || (Channel == 128) )
			CenterFrequency = 122;
		if((Channel == 132) || (Channel == 136) || (Channel == 140) || (Channel == 144) )
			CenterFrequency = 138;
		if((Channel == 149) || (Channel == 153) || (Channel == 157) || (Channel == 161) )
			CenterFrequency = 155;
		else if(Channel <= 14)
			CenterFrequency = 7;
	}
	else if(ChnlBW == CHANNEL_WIDTH_40)
	{
		if(ExtChnlOffset == EXTCHNL_OFFSET_UPPER)
			CenterFrequency = Channel + 2;
		else
			CenterFrequency = Channel - 2;
	}
	return CenterFrequency;
}



EXTCHNL_OFFSET
CHNL_GetExt20OffsetOf5G(
	IN u1Byte		channel
	)
{
	u1Byte			i;
	EXTCHNL_OFFSET	ExtChnlOffset = EXTCHNL_OFFSET_LOWER;
	u1Byte			UpperChannelList_5G[11] = {36,44,52,60,100,108,116,124,132,149,157};

	for(i =0; i < 11;i++)
	{
		if(channel == UpperChannelList_5G[i])
			break;
	}
	if(i < 11)
			ExtChnlOffset = EXTCHNL_OFFSET_UPPER;

	RT_DISP(FCHNL, FCHNL_INFO,  ("%s channel=%d, ExtChnlOffset=%d\n", __FUNCTION__, channel, ExtChnlOffset));
	
	return ExtChnlOffset;
}


u1Byte
CHNL_IsLegalChannel(
	PADAPTER		Adapter,
	u8Byte			freq_channel
)
{
	PRT_CHANNEL_LIST	pChanneList = NULL;
	u2Byte				i;

	RtActChannelList(Adapter, RT_CHNL_LIST_ACTION_GET_CHANNEL_LIST, NULL, &pChanneList);

	//Skip check legal channel when doing fast USB switch, since we only have one channel in channel list, 20150408, Sean. 

		if (pChanneList->ChannelLen == 1)
		{
			return (u1Byte)freq_channel;
		}
	
	for(i = 0; i < pChanneList->ChannelLen; i ++)
	{
		if(freq_channel == pChanneList->ChnlListEntry[i].ChannelNum)
		{
			return pChanneList->ChnlListEntry[i].ChannelNum;
		}// TODO:  ending check 5G first
		else if(freq_channel == pChanneList->ChnlListEntry[i].ChannelNum + 2)
		{
			return (u1Byte)freq_channel;
		}
		if( (pChanneList->ChnlListEntry[i].ChannelNum <= 14) && (freq_channel == DSSS_Freq_Channel[pChanneList->ChnlListEntry[i].ChannelNum]*1000) )
		{
			return pChanneList->ChnlListEntry[i].ChannelNum;
		}
	}
	return 0;
}


BOOLEAN 
CHNL_IsLegal5GChannel(
	IN PADAPTER			Adapter,
	IN u1Byte			channel
)
{
	
	u1Byte i=0;
	u1Byte Channel_5G[45] = {36,38,40,42,44,46,48,50,52,54,56,58,
		60,62,64,100,102,104,106,108,110,112,114,116,118,120,122,
		124,126,128,130,132,134,136,138,140,149,151,153,155,157,159,
		161,163,165};
	for(i=0;i<sizeof(Channel_5G);i++)
		if(channel == Channel_5G[i])
			return TRUE;
	return FALSE;
}



BOOLEAN
CHNL_AcquireOpLock(
	PADAPTER	Adapter,
	CHNLOP		ChnlOp
	)
{
	PMGNT_INFO				pMgntInfo = &Adapter->MgntInfo;
	PRT_CHANNEL_INFO		pChnlInfo = pMgntInfo->pChannelInfo;
	BOOLEAN					bGetLock = FALSE;

	PlatformAcquireSpinLock(Adapter, RT_CHNLOP_SPINLOCK);
	if(CHNL_OP_IN_PROGRESS(pChnlInfo))
	{
		bGetLock = FALSE;
	}
	else
	{
		pChnlInfo->ChnlOp = ChnlOp;
		bGetLock = TRUE;
	}
	PlatformReleaseSpinLock(Adapter, RT_CHNLOP_SPINLOCK);
	return bGetLock;
}

VOID
CHNL_ReleaseOpLock(
	PADAPTER	Adapter
	)
{
	PMGNT_INFO				pMgntInfo = &Adapter->MgntInfo;
	PRT_CHANNEL_INFO		pChnlInfo = pMgntInfo->pChannelInfo;
	
	PlatformAcquireSpinLock(Adapter, RT_CHNLOP_SPINLOCK);
	pChnlInfo->ChnlOp = CHNLOP_NONE;
	PlatformReleaseSpinLock(Adapter, RT_CHNLOP_SPINLOCK);
}


BOOLEAN
CHNL_ValidForWirelessMode(
	u1Byte	channel,		
	u2Byte	wirelessmode
	)
{	

	BOOLEAN ret = FALSE;
	
	do
	{
		if(IS_5G_WIRELESS_MODE(wirelessmode) && channel > 14)
		{
			ret = TRUE;
			break;
		}
		
		if(IS_24G_WIRELESS_MODE(wirelessmode) && channel <= 14)
		{
			ret = TRUE;
			break;
		}
	}while(FALSE);

	return ret;
}


CHANNEL_WIDTH
CHNL_GetRegBWSupport(
	IN	PADAPTER	Adapter
)
{
	PMGNT_INFO				pMgntInfo = &Adapter->MgntInfo;
	PRT_HIGH_THROUGHPUT		pHTInfo = GET_HT_INFO(pMgntInfo);
	PRT_CHANNEL_INFO		pChnlInfo = pMgntInfo->pChannelInfo;
	CHANNEL_WIDTH			retBW = 0;


	if(HAL_Support_BW_Advancesetting(Adapter))
	{
		if(IS_WIRELESS_MODE_24G(Adapter))
			retBW = (pHTInfo->bRegBW40MHzFor2G ? CHANNEL_WIDTH_40:CHANNEL_WIDTH_20) ;
		else if(IS_WIRELESS_MODE_5G(Adapter))
			retBW = (pHTInfo->bRegBW40MHzFor5G ? CHANNEL_WIDTH_40:CHANNEL_WIDTH_20) ;
	}
	else	
		retBW = pChnlInfo->RegBWSetting;
			
	
	return retBW;
}




VOID
chnl_GetBWFrom_IE(
	IN	PADAPTER		Adapter,
	OUT PCHANNEL_WIDTH	pChnlBW,
	OUT PEXTCHNL_OFFSET	pExtChnlOffset
)
{
	PMGNT_INFO					pMgntInfo=&Adapter->MgntInfo;
	PRT_HIGH_THROUGHPUT		pHTInfo = GET_HT_INFO(pMgntInfo);
	PRT_VERY_HIGH_THROUGHPUT	pVHTInfo = GET_VHT_INFO(pMgntInfo);

	BOOLEAN 		bBW40MHz = FALSE, bBW80MHz = FALSE;
	CHANNEL_WIDTH	ChnlBW = CHANNEL_WIDTH_20;
	EXTCHNL_OFFSET	ExtChnlOffset = EXTCHNL_OFFSET_NO_EXT;

	if(pHTInfo->bCurrentHTSupport)
	{
		CHANNEL_WIDTH	PeerChnlBW = (CHANNEL_WIDTH)pHTInfo->bPeer40MHzCap;
		EXTCHNL_OFFSET	PeerExtChnlOffset = pHTInfo->PeerExtChnlOffset;

		ChnlBW = CHNL_GetRegBWSupport(Adapter);
		if(ChnlBW == CHANNEL_WIDTH_20)						// 20 STA -> 40 AP or 20 STA -> 20 AP
			ExtChnlOffset = EXTCHNL_OFFSET_NO_EXT;
		else if(PeerChnlBW == CHANNEL_WIDTH_20)				// 40 STA -> 20 AP
			ChnlBW = CHANNEL_WIDTH_20;
		else if(PeerExtChnlOffset == EXTCHNL_OFFSET_NO_EXT)
			ChnlBW = CHANNEL_WIDTH_20;
		else													// 40 STA -> 40 AP
		{
			if(	PeerExtChnlOffset == EXTCHNL_OFFSET_UPPER && 
				CHNL_IsLegalChannel(Adapter, pMgntInfo->dot11CurrentChannelNumber + 2))
				bBW40MHz = TRUE;
			else if(PeerExtChnlOffset== EXTCHNL_OFFSET_LOWER &&
				 CHNL_IsLegalChannel(Adapter, pMgntInfo->dot11CurrentChannelNumber - 2))
				bBW40MHz = TRUE;
			else
				bBW40MHz = FALSE;

			if(bBW40MHz == TRUE)
			{
				ChnlBW = PeerChnlBW;
				ExtChnlOffset = PeerExtChnlOffset;
			}	
			else
			{
				ChnlBW = CHANNEL_WIDTH_20;
				ExtChnlOffset = EXTCHNL_OFFSET_NO_EXT;
			}
		}
		RT_DISP(FCHNL, FCHNL_INFO, ("%s PeerChnlBW=%d, PeerExtChnlOffset=%d\n", __FUNCTION__, PeerChnlBW, PeerExtChnlOffset));
	}
	else
		ChnlBW = CHANNEL_WIDTH_20;

	
	if(bBW40MHz && pVHTInfo->bCurrentVHTSupport)
	{
		bBW80MHz = (CHNL_GetRegBWSupport(Adapter) >= CHANNEL_WIDTH_80)?(pVHTInfo->PeerChnlBW?1:0):0;

		if(bBW80MHz == 0)
			ChnlBW = CHANNEL_WIDTH_40;
		else
			ChnlBW = CHANNEL_WIDTH_80;
	}

	*pChnlBW = ChnlBW;
	*pExtChnlOffset = ExtChnlOffset;

	RT_DISP(FCHNL, FCHNL_INFO, ("%s ChnlBW=%d, ExtChnlOffset=%d, bEnableVHT = %d, bCurrentHTSupport = %d, bCurrentVHTSupport = %d,\n PeerChnlBW = %d, CHNL_GetRegBWSupport() = %d\n", __FUNCTION__, ChnlBW, ExtChnlOffset, pVHTInfo->bEnableVHT, pHTInfo->bCurrentHTSupport, pVHTInfo->bCurrentVHTSupport, pVHTInfo->PeerChnlBW, CHNL_GetRegBWSupport(Adapter)));
}


VOID
CHNL_ChangeBwChnlFromPeerWorkitemCallBack(
	IN	PVOID			Context
)
{
	PADAPTER			Adapter = (PADAPTER)Context;	
	CHANNEL_WIDTH		ChnlBW;
	EXTCHNL_OFFSET		ExtChnlOffset;
	PMGNT_INFO			pMgntInfo = &Adapter->MgntInfo;
	PRT_CHANNEL_INFO	pChnlInfo = GET_CHNL_INFO(pMgntInfo);
	PADAPTER			pExtAdapter = GetFirstAPAdapter(Adapter);
	PADAPTER			pGoAdapter = GetFirstGOPort(Adapter);

	if(pExtAdapter != NULL)
	{
		if(pExtAdapter->bAPChannelInprogressForNewconnected)
		{
			RT_DISP(FCHNL, FCHNL_ERROR, ("%s: AP change channel in progress , return \n", __FUNCTION__));
			return;

		}
	}

	if(pGoAdapter != NULL)
	{
		if(pGoAdapter->bAPChannelInprogressForNewconnected)
		{
			RT_DISP(FCHNL, FCHNL_ERROR, ("%s: AP change channel in progress , return \n", __FUNCTION__));
			return;
		}
	}

	if(GetDefaultMgntInfo(Adapter)->RegMultiChannelFcsMode != 0)
	{
		if(TRUE == MultiChannel_IsFCSInProgress(Adapter))
		{
			RT_TRACE(COMP_MULTICHANNEL, DBG_TRACE, ("CHNL_ChangeBwChnlFromPeerWorkitemCallBack(): skip change ch & bw due to MCC\n"));
			return;
		}
	}
	
#if 0
	if(GetDefaultAdapter(Adapter)->bInHctTest)
	{
		RT_DISP(FCHNL, FCHNL_ERROR, ("%s: hct test , return \n", __FUNCTION__));
		return;
	
	}
#endif	

	
	chnl_GetBWFrom_IE(Adapter, &ChnlBW, &ExtChnlOffset);

	if(	pChnlInfo->CurrentChannelBandWidth != ChnlBW ||
		pChnlInfo->Ext20MHzChnlOffsetOf40MHz != ExtChnlOffset)
	{
		//
		// Leave LPS and wait until RF is already on before switching channel. 2014.09.25, by tynli.
		//
		LPSLeaveAndCheckReady(Adapter);

		RT_TRACE(COMP_MLME, DBG_LOUD, ("CHNL_ChangeBwChnlFromPeerWorkitemCallBack(): CurrentChannel=%d, ChnlBW=%d, ExtChnlOffset=%d\n",
			pMgntInfo->dot11CurrentChannelNumber, ChnlBW, ExtChnlOffset));

		CHNL_SetBwChnl(Adapter, pMgntInfo->dot11CurrentChannelNumber, ChnlBW, ExtChnlOffset);
		// We should set RA H2C cmd because the BW in TxDesc will be filled again by Fw, we 
		// need to inform Fw the BW changed information. 2012.11.26. by tynli. 
		// Suggested by SD1 <NAME>.
		if(!ACTING_AS_AP(Adapter) && !ACTING_AS_IBSS(Adapter))
		{
			// we only need to set rate mask
			Adapter->HalFunc.UpdateHalRAMaskHandler(Adapter, pMgntInfo->mMacId, NULL, 0);

		}
	}

}


VOID
CHNL_SetBwChnlFromPeer(
	IN	PADAPTER			Adapter
)
{
	PMGNT_INFO		pMgntInfo=&Adapter->MgntInfo;
	CHANNEL_WIDTH	ChnlBW;
	EXTCHNL_OFFSET	ExtChnlOffset;

	chnl_GetBWFrom_IE(Adapter, &ChnlBW, &ExtChnlOffset);

	CHNL_SetBwChnl(Adapter, pMgntInfo->dot11CurrentChannelNumber, ChnlBW, ExtChnlOffset);
}


BOOLEAN
CHNL_GetCurrnetChnlInfo(
	IN		PADAPTER	Adapter,
	OUT 	pu1Byte 		pBuf,
	IN OUT 	pu4Byte		pBufLen
)
{
	PMGNT_INFO			pMgntInfo=&Adapter->MgntInfo;
	PRT_CHANNEL_INFO	pChnlInfo = pMgntInfo->pChannelInfo;

	if(*pBufLen < 4)
	{
		*pBufLen = 4;
		return FALSE;
	}		
	
	*pBufLen = 4;

	*pBuf = pChnlInfo->CurrentChannelBandWidth;
	*(pBuf+1) = pChnlInfo->PrimaryChannelNumber;
	*(pBuf+2) = pChnlInfo->Ext20MHzChnlOffsetOf40MHz;
	*(pBuf+3) = pChnlInfo->CurrentChannelCenterFrequency;

	return TRUE;
}
	


VOID
chnl_HalSwBwChnl(
	PADAPTER	Adapter
	)
{
    PCHANNEL_COMMON_CONTEXT		pChnlCommInfo = Adapter->pPortCommonInfo->pChnlCommInfo;
	PHAL_DATA_TYPE				pHalData = GET_HAL_DATA(Adapter);

	FunctionIn(COMP_SCAN);

	if(HAL_ChipSupport80MHz(Adapter))
	{
		Adapter->HalFunc.SwChnlAndSetBWHandler(
			Adapter,
			TRUE,
			TRUE,
			pChnlCommInfo->CurrentChannelCenterFrequency,
			pChnlCommInfo->CurrentChannelBandWidth,
			pChnlCommInfo->Ext20MHzChnlOffsetOf40MHz,
			pChnlCommInfo->Ext40MHzChnlOffsetOf80MHz,
			pChnlCommInfo->CurrentChannelCenterFrequency
		);
	}
	else
	{
		u1Byte	offset;

		Mgnt_SwChnl(Adapter, pChnlCommInfo->CurrentChannelCenterFrequency, 1);		

		// last bandwidth offset 
		if(pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER)
			offset = EXTCHNL_OFFSET_LOWER;
		else if (pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER)
			offset = EXTCHNL_OFFSET_UPPER;
		else
			offset = EXTCHNL_OFFSET_NO_EXT;
		// 1.20M to 40M or 40M to 20M
		// 2.40M to 40M as offset different or bandtype different	
		if(	(pHalData->CurrentChannelBW != pChnlCommInfo->CurrentChannelBandWidth )||
			(pHalData->CurrentChannelBW == CHANNEL_WIDTH_40 && pChnlCommInfo->Ext20MHzChnlOffsetOf40MHz !=offset) ||
			(pHalData->LastBandType !=pHalData->CurrentBandType))
		{
			Adapter->HalFunc.SetBWModeHandler(
				Adapter,
				pChnlCommInfo->CurrentChannelBandWidth,
				pChnlCommInfo->Ext20MHzChnlOffsetOf40MHz
				);
		}	
	}

	
	FunctionOut(COMP_SCAN);
}	


VOID
CHNL_SetBwChnlCallback(
	IN	PRT_TIMER		pTimer
	)
{
	PADAPTER			Adapter=(PADAPTER)pTimer->Adapter;
	PMGNT_INFO			pMgntInfo=&Adapter->MgntInfo;
	PRT_CHANNEL_INFO	pChnlInfo = pMgntInfo->pChannelInfo;
	PHAL_DATA_TYPE	   	pHalData = GET_HAL_DATA(Adapter);
	BOOLEAN				bSwBW = TRUE;

	RT_DISP(FCHNL, FCHNL_FUN, ("===>%s \n", __FUNCTION__));

	if(RT_DRIVER_HALT(Adapter))
	{
		RT_DISP(FCHNL, FCHNL_ERROR, ("<===%s bDriverStopped %d bSurpriseRemoved %d bDriverIsGoingToUnload %d\n", __FUNCTION__, Adapter->bDriverStopped, Adapter->bSurpriseRemoved, Adapter->bDriverIsGoingToUnload));	
		return;
	}

	if(Adapter->bInSetPower && RT_CANNOT_IO(Adapter))
	
	{
		RT_DISP(FCHNL, FCHNL_ERROR, ("<===%s can NOT IO\n", __FUNCTION__));
		bSwBW = FALSE;
	}

	if(MgntResetOrPnPInProgress(Adapter))
	{
		RT_DISP(FCHNL, FCHNL_ERROR, ("<===%s MgntResetOrPnPInProgress\n", __FUNCTION__));			
		bSwBW = FALSE;
	}
	
	if(bSwBW == FALSE)
	{
		PlatformAcquireSpinLock(Adapter, RT_BW_SPINLOCK);
		pChnlInfo->bSwBwInProgress = FALSE;
		PlatformReleaseSpinLock(Adapter, RT_BW_SPINLOCK);
		return;
	}
	
	if(MgntInitAdapterInProgress(pMgntInfo))
	{
		PlatformSetTimer(Adapter, &pChnlInfo->SwBwTimer, 100);
		return;
	}
	
   if(RT_IsSwChnlAndBwInProgress(Adapter))
   { 
  		RT_DISP(FCHNL, FCHNL_FUN, ("<===%s pHalData->SwChnlInProgress: %d, pHalData->SetBWModeInProgress: %d, pHalData->bSwChnlAndSetBWInProgress: %d\n", 
				__FUNCTION__, pHalData->SwChnlInProgress, pHalData->SetBWModeInProgress, pHalData->bSwChnlAndSetBWInProgress));
	
       	PlatformSetTimer(Adapter, &pChnlInfo->SwBwTimer, 10);
		return;
   }

	if(pMgntInfo->bScanInProgress)
	{
		RT_DISP(FCHNL, FCHNL_FUN, ("<===%s bScanInProgress\n", __FUNCTION__));
		PlatformSetTimer(Adapter, &pChnlInfo->SwBwTimer, 10);
		return;
	}
	   
	if(!CHNL_AcquireOpLock(Adapter, CHNLOP_SWBW))
	{
		PlatformSetTimer(Adapter, &pChnlInfo->SwBwTimer, 10);
		return;
	}

	if(IsDefaultAdapter(Adapter))
	{
		RT_DISP(FCHNL, FCHNL_FUN, ("%s  Def Adapter \n", __FUNCTION__));
	}
	else
	{
		RT_DISP(FCHNL, FCHNL_FUN, ("%s  Ext Adapter \n", __FUNCTION__));
	}

	PlatformAcquireSpinLock(Adapter, RT_BW_SPINLOCK);
	pChnlInfo->bSwBwInProgress = TRUE;	
	PlatformReleaseSpinLock(Adapter, RT_BW_SPINLOCK);

	chnl_HalSwBwChnl(Adapter);
		
	// bandtype different only occur in 92d ; for 92c/92s  last band type is the same as current band type		
	pHalData->LastBandType = pHalData->CurrentBandType;  

	PlatformAcquireSpinLock(Adapter, RT_BW_SPINLOCK);
	pChnlInfo->bSwBwInProgress = FALSE;
	PlatformReleaseSpinLock(Adapter, RT_BW_SPINLOCK);

	CHNL_ReleaseOpLock(Adapter);	
	
	RT_DISP(FCHNL, FCHNL_FUN, ("<===%s\n", __FUNCTION__));
}

CHANNEL_WIDTH
CHNL_CheckChnlPlanWithBW(
	IN	PADAPTER			pAdapter,
	IN	u1Byte				PrimaryChnl,	
	IN	CHANNEL_WIDTH		Bandwidth,	
	IN	EXTCHNL_OFFSET		BwOffset	
)
{
	CHANNEL_WIDTH finalBw = CHANNEL_WIDTH_20;
	PRT_CHANNEL_LIST	pChanneList = NULL;
	u2Byte				i;
	u1Byte				CentralChnl=PrimaryChnl;
	u1Byte				anotherPrimaryChnl=PrimaryChnl;
	u1Byte				findcnt=0;
	BOOLEAN				bFind=FALSE;



	if(Bandwidth == CHANNEL_WIDTH_40)
	{
		if(BwOffset == EXTCHNL_OFFSET_UPPER)
		{
			CentralChnl = PrimaryChnl + 2;
			anotherPrimaryChnl = CentralChnl+2;
		}
		else
		{
			CentralChnl = PrimaryChnl - 2;
			anotherPrimaryChnl = CentralChnl-2;
		}
	}


	RtActChannelList(pAdapter, RT_CHNL_LIST_ACTION_GET_CHANNEL_LIST, NULL, &pChanneList);

	if(PrimaryChnl>13)
	{
		if(!(pChanneList->WirelessMode & (WIRELESS_MODE_A|WIRELESS_MODE_AC_5G|WIRELESS_MODE_N_5G)))
		{
			finalBw =  Bandwidth;
			return finalBw;
		}
	}
	else
	{
		if(!(pChanneList->WirelessMode & (WIRELESS_MODE_B|WIRELESS_MODE_N_24G|WIRELESS_MODE_G|WIRELESS_MODE_AC_24G)))
		{
			finalBw =  Bandwidth;
			return finalBw;
		}
	}

	for(i=0;i<pChanneList->ChannelLen;i++)
	{
		if((anotherPrimaryChnl == pChanneList->ChnlListEntry[i].ChannelNum) ||
			(PrimaryChnl == pChanneList->ChnlListEntry[i].ChannelNum))
		{
			findcnt++;
		}

		if(findcnt==2)
		{
			bFind =TRUE;
			findcnt =0;
			break;			
		}
	}

	if(Bandwidth == CHANNEL_WIDTH_80)
		finalBw = CHANNEL_WIDTH_80;
	else
	{
		// remove, advised by Bryant, this will cause IOT issue with TP link Ap
		// TP link will be 40MHz, but NIC is 20MHz => problem.
#if 0	
		if( BT_1Ant(pAdapter) && BT_IsBtLinkExist(pAdapter) &&
			IS_WIRELESS_MODE_24G(pAdapter) )
		{
			finalBw = CHANNEL_WIDTH_20;
		}
		else
#endif
		{
			if(bFind)
				finalBw = CHANNEL_WIDTH_40;
			else 
				finalBw = CHANNEL_WIDTH_20;
		}
	}			

	return finalBw;
}


//
// This function set bandwidth mode in protocol layer.
//
VOID
CHNL_SetBwChnl(
	IN	PADAPTER			pAdapter,
	IN	u1Byte				PrimaryChnl,
	IN	CHANNEL_WIDTH		Bandwidth,
	IN	EXTCHNL_OFFSET		BwOffset
	)
{
	PMGNT_INFO					pMgntInfo = &pAdapter->MgntInfo;
	PRT_CHANNEL_INFO			pChnlInfo = GET_CHNL_INFO(pMgntInfo);
	
	PADAPTER					DefAdapter = GetDefaultAdapter(pAdapter);
	PMGNT_INFO					pDefMgntInfo = &DefAdapter->MgntInfo;
	PRT_CHANNEL_INFO			pDefChnlInfo = GET_CHNL_INFO(pDefMgntInfo);
	PCHANNEL_COMMON_CONTEXT		pDefChnlCommInfo = DefAdapter->pPortCommonInfo->pChnlCommInfo;

	CHANNEL_WIDTH				ToSetBandWidth = CHANNEL_WIDTH_20;
	EXTCHNL_OFFSET				ToSTAExtChnlOffsetof40MHz= EXTCHNL_OFFSET_NO_EXT;
	EXTCHNL_OFFSET				ToSTAExtChnlOffsetof80MHz= EXTCHNL_OFFSET_NO_EXT;
	u1Byte						ToSTACenterFrequency;

	RT_TRACE_F(COMP_SCAN, DBG_LOUD, ("PrimaryChnl %d\n", PrimaryChnl));

	if(ACTING_AS_IBSS(DefAdapter))
	{
		{
			// 2013/10/15 MH Add for higher adhoc and VHT 2.4G mode.
			if (!pMgntInfo->bReg11nAdhoc && !pMgntInfo->bRegVht24g)
			{
				Bandwidth= CHANNEL_WIDTH_20;
				BwOffset = EXTCHNL_OFFSET_NO_EXT;
			}
		}
	}

	ToSetBandWidth = Bandwidth;
	ToSTAExtChnlOffsetof40MHz = BwOffset;
	ToSTACenterFrequency = CHNL_GetCenterFrequency(PrimaryChnl, Bandwidth, BwOffset);
	
	ToSetBandWidth = CHNL_CheckChnlPlanWithBW(pAdapter,PrimaryChnl,Bandwidth,BwOffset);

	if((ToSetBandWidth != Bandwidth) &&  ToSetBandWidth==CHANNEL_WIDTH_20)
		ToSTACenterFrequency =PrimaryChnl;
	
	if(Bandwidth == CHANNEL_WIDTH_80)
	{
		if(ToSTACenterFrequency > pMgntInfo->dot11CurrentChannelNumber)
			ToSTAExtChnlOffsetof80MHz = EXTCHNL_OFFSET_UPPER;
		else if(ToSTACenterFrequency < pMgntInfo->dot11CurrentChannelNumber)
			ToSTAExtChnlOffsetof80MHz = EXTCHNL_OFFSET_LOWER;
		else
			ToSTAExtChnlOffsetof80MHz = EXTCHNL_OFFSET_NO_EXT;		
	}

	RT_DISP(FCHNL, FCHNL_FUN,  ("Set ToSetBandWidth = %d, ToSTAExtChnlOffset = %d,ToSTAExtChnlOffsetof80MHz %d, ToSTACenterFrequency= %d \n", ToSetBandWidth, ToSTAExtChnlOffsetof40MHz,ToSTAExtChnlOffsetof80MHz,ToSTACenterFrequency));

	pMgntInfo->dot11CurrentChannelBandWidth = ToSetBandWidth;
	pChnlInfo->PrimaryChannelNumber = pDefChnlCommInfo->PrimaryChannelNumber = PrimaryChnl;
	pChnlInfo->CurrentChannelBandWidth = pDefChnlCommInfo->CurrentChannelBandWidth = ToSetBandWidth;
	pChnlInfo->CurrentChannelCenterFrequency = pDefChnlCommInfo->CurrentChannelCenterFrequency = ToSTACenterFrequency;
	pChnlInfo->Ext20MHzChnlOffsetOf40MHz = pDefChnlCommInfo->Ext20MHzChnlOffsetOf40MHz = ToSTAExtChnlOffsetof40MHz;
	pChnlInfo->Ext40MHzChnlOffsetOf80MHz = pDefChnlCommInfo->Ext40MHzChnlOffsetOf80MHz = ToSTAExtChnlOffsetof80MHz;

	// TODO: 2007.7.13 by Emily Wait 2000ms  in order to garantee that switching
	//   bandwidth is executed after scan is finished. It is a temporal solution
	//   because software should ganrantee the last operation of switching bandwidth
	//   is executed properlly. 
	if(!(RT_DRIVER_HALT(DefAdapter)))
		PlatformSetTimer(DefAdapter, &pDefChnlInfo->SwBwTimer, 0);

	FunctionOut(COMP_SCAN);
}



VOID
CHNL_SetChnlInfoFromDestPort(
	IN	PADAPTER			pDestAdapter,
	IN	PADAPTER			pSrcAdapter
)
{
	PMGNT_INFO			pDestMgntInfo = &pDestAdapter->MgntInfo;
	PRT_CHANNEL_INFO	pDestChnlInfo = GET_CHNL_INFO(pDestMgntInfo);
	PMGNT_INFO			pSrcMgntInfo = &pSrcAdapter->MgntInfo;
	PRT_CHANNEL_INFO 	pSrcChnlInfo = GET_CHNL_INFO(pSrcMgntInfo);

	pDestMgntInfo->dot11CurrentChannelNumber = pSrcMgntInfo->dot11CurrentChannelNumber;
	pDestMgntInfo->dot11CurrentChannelBandWidth = pSrcMgntInfo->dot11CurrentChannelBandWidth;

	// Best way is restart AP mode. 2012.01.08 lanhsin. 
	//pDestMgntInfo->CurrentBssWirelessMode = pDestMgntInfo->dot11CurrentWirelessMode = pSrcMgntInfo->dot11CurrentWirelessMode;
	
	pDestChnlInfo->PrimaryChannelNumber = pSrcChnlInfo->PrimaryChannelNumber;
	pDestChnlInfo->CurrentChannelBandWidth =  pSrcChnlInfo->CurrentChannelBandWidth;
	pDestChnlInfo->CurrentChannelCenterFrequency = pSrcChnlInfo->CurrentChannelCenterFrequency;

	pDestChnlInfo->Ext20MHzChnlOffsetOf40MHz = pSrcChnlInfo->Ext20MHzChnlOffsetOf40MHz;
	pDestChnlInfo->Ext40MHzChnlOffsetOf80MHz = pSrcChnlInfo->Ext40MHzChnlOffsetOf80MHz;

	RT_DISP(FCHNL, FCHNL_FUN, ("%s: pDestChnlInfo->CurrentChannelCenterFrequency=%d, pSrcChnlInfo->CurrentChannelCenterFrequency=%d\n", 
		__FUNCTION__, pDestChnlInfo->CurrentChannelCenterFrequency, pSrcChnlInfo->CurrentChannelCenterFrequency));
}


//sherry added 20130207
//to check switch channel and bandwidth in progress
BOOLEAN
CHNL_SwChnlAndSetBwInProgress(
	PADAPTER	Adapter
)
{
	PADAPTER			pDefAdapter = GetDefaultAdapter(Adapter);
	PMGNT_INFO			pDefMgntInfo = &pDefAdapter->MgntInfo;
	PRT_CHANNEL_INFO	pDefChnlInfo = GET_CHNL_INFO(pDefMgntInfo);

	if(pDefChnlInfo->bSwBwInProgress)
	{
		RT_DISP(FCHNL, FCHNL_INFO, ("%s: return TRUE \n", __FUNCTION__));
		return TRUE;
	}
	else
	{
		RT_DISP(FCHNL, FCHNL_INFO, ("%s: return FALSE \n", __FUNCTION__));
		return FALSE;
	}
}

//
// Description:
//	Get the primary channel from the channel information and hw configurations.
// Note:
//	There are two kinds of channel, one is kept in mgnt/channel info and another one is kept in hal.
//	They may be mismatched when the channel switching is in progress or scan in progress.
//	When the scan is performing, the channel in mgnt/channel is not updated.
//
//	If channels are different between HW and ChannelInfo, this function returns HW channel.
//
u1Byte
RT_GetChannelNumber(
	IN	PADAPTER	pAdapter
	)
{
	u1Byte			hwCenterChannel = 0;
	EXTCHNL_OFFSET	hwBW40MOffset = EXTCHNL_OFFSET_NO_EXT;
	EXTCHNL_OFFSET	hwBW80MOffset = EXTCHNL_OFFSET_NO_EXT;

	pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_CUR_CENTER_CHNL, (pu1Byte)(&hwCenterChannel));
	pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_BW40MHZ_EXTCHNL, (pu1Byte)(&hwBW40MOffset));
	pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_BW80MHZ_EXTCHNL, (pu1Byte)(&hwBW80MOffset));

	if(0 == hwCenterChannel)
	{ // Please fix this case
		hwCenterChannel = GET_HAL_DATA(pAdapter)->CurrentChannel;
		RT_TRACE_F(COMP_SCAN, DBG_WARNING, ("[WARNING] Get hw channel = 0 by HW_VAR_CUR_CENTER_CHNL, access hal directly = %d\n",
			hwCenterChannel));
	}
	
	if(((GetDefaultAdapter(pAdapter)->pPortCommonInfo->pChnlCommInfo->CurrentChannelCenterFrequency) == hwCenterChannel))
	{
		return (GetDefaultAdapter(pAdapter)->pPortCommonInfo->pChnlCommInfo->PrimaryChannelNumber);
	}
	else
	{ // In this case, the returned channel may not be the right one because channel switching is in progress.
		if((GetDefaultAdapter(pAdapter)->pPortCommonInfo->pChnlCommInfo->CurrentChannelCenterFrequency) != 0)
		{
			RT_TRACE_F(COMP_MLME, DBG_WARNING, ("[WARNING] Mismatched channel info ceter = %d, primary = %d, hwCenterChannel = %d (40M = %d, 80M = %d), SwChnlInProgress = %d, ScanInProgress = %d\n",
				(GetDefaultAdapter(pAdapter)->pPortCommonInfo->pChnlCommInfo->CurrentChannelCenterFrequency),
				(GetDefaultAdapter(pAdapter)->pPortCommonInfo->pChnlCommInfo->PrimaryChannelNumber),
				hwCenterChannel, hwBW40MOffset, hwBW80MOffset, (RT_IsSwChnlAndBwInProgress(pAdapter) | CHNL_SwChnlAndSetBwInProgress(pAdapter)), MgntScanInProgress(&(pAdapter->MgntInfo))));
		}
		return hwCenterChannel;
	}
}



<|start_filename|>gnss/gnssUmdf/Queue.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    queue.h

Abstract:

    This file contains the queue definitions for the Umdf Gnss Driver.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

class CFixHandler;

//
//This class sets up IO queue, performs the IOCTL operations, and implements the type definitions for the driver's device callback.
//
class CQueue
{
public:
    static NTSTATUS AddQueueToDevice(_In_ WDFDEVICE device, _Outptr_ CQueue** queue);

private:
    enum
    {
        AGNSS_TIME_IDX = GNSS_AGNSS_TimeInjection,
        AGNSS_POS_IDX = GNSS_AGNSS_PositionInjection,
        AGNSS_BLOB_IDX = GNSS_AGNSS_BlobInjection,
        AGNSS_MAX_IDX
    };

    typedef struct {
        DWORD OperationMode;
        BOOL SetLocationNiRequestAllowed;
        BOOL SetLocationServiceEnabled;
    } GnssInternalState;

    WDFQUEUE _Queue;
    CRITICAL_SECTION _Lock;
    CFixHandler _FixHandler;
    ULONG _ForcedDriverVersion = GNSS_DRIVER_DDK_VERSION;
    GnssInternalState _InternalState = {};
    BOOL _AgnssNeeded[AGNSS_MAX_IDX];
    
    CQueue(WDFQUEUE Queue);
    ~CQueue();

    static EVT_WDF_OBJECT_CONTEXT_CLEANUP OnCleanup;
    static EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OnIoDeviceControl;

    template <class DataT>
    BOOL GetDataFromCommand(_In_ PGNSS_DRIVERCOMMAND_PARAM Command,
                            _Out_ DataT* Data);

    NTSTATUS Initialize();
    void OnIoDeviceControl(_In_ WDFREQUEST Request,
                           _In_ ULONG IoControlCode,
                           _In_ size_t InputBufferLength,
                           _In_ size_t OutputBufferLength);
    NTSTATUS GetDeviceCapability(_In_ WDFREQUEST Request);
    NTSTATUS GetChipsetInfo(_In_ WDFREQUEST Request);
    NTSTATUS HandleDriverCommand(_In_ WDFREQUEST Request);
    
    NTSTATUS ResetEngine(_In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam);
    NTSTATUS ClearAgnssData(_In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam);
    NTSTATUS SetLocationServiceStatus(_In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam);
    NTSTATUS SetOperationMode(_In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam);
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CQueue, GetQueueObject);


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/audioconnectionutil.h<|end_filename|>
#pragma once 

#define IS_2_POW_N(X)           (((X)&(X-1)) == 0)

enum class aligned_size_t : size_t {};

void * AllocateAligned(size_t cb, aligned_size_t AlignmentMask)
{
    ASSERT(0 != cb);
    //ASSERT(IS_2_POW_N(AlignmentMask + 1));
    
    void *res = _aligned_malloc(cb, static_cast<size_t>(AlignmentMask) + 1);

    return res;
}

void DeallocateAligned(void * p, aligned_size_t /* AlignmentMask */)
{
    if (NULL != p)
    {
        _aligned_free(p);
    }
}

UINT32 CalculateAlignmentMask(APO_CONNECTION_DESCRIPTOR * /* pDescriptor */, UINT32 u32BytesPerFrame)
{
    const UINT32 u32AudioAlignment = sizeof(PVOID);
    UINT32 u32Alignment = u32AudioAlignment - 1;

    // Calculate the new alignment requirement for this buffer.
    if (u32BytesPerFrame > u32AudioAlignment)
    {
        // Use the frame size directly if it is a power of two.
        if (IS_2_POW_N(u32BytesPerFrame))
        {
            u32Alignment = u32BytesPerFrame - 1;
        }
        // The new alignment size should be greater than frame 
        // size and should be a power of 2.
        else
        {
            u32Alignment = u32AudioAlignment;
            while (u32BytesPerFrame / u32Alignment)
            {
                u32Alignment *= 2;
            }
            u32Alignment = u32Alignment - 1;
        }
    }

    // Use default alignment if the frame size is less than the default 
    // alignment.
    return u32Alignment; 
} // CalculateAlignmentMask

HRESULT
CreateConnection(APO_CONNECTION_DESCRIPTOR* pDescriptor, APO_CONNECTION_PROPERTY* pProperty)
{
    RETURN_HR_IF_NULL(E_POINTER, pDescriptor);
    RETURN_HR_IF_NULL(E_POINTER, pDescriptor->pFormat);
    RETURN_HR_IF_NULL(E_POINTER, pProperty);

    UINT32 u32BytesPerFrame;
    unsigned byteAlignmentMask;
    BYTE*  pAllocatedBuffer;
    UNCOMPRESSEDAUDIOFORMAT Format;

    RETURN_IF_FAILED(pDescriptor->pFormat->GetUncompressedAudioFormat(&Format));

    u32BytesPerFrame = 
        Format.dwSamplesPerFrame * Format.dwBytesPerSampleContainer;

    // Allocate buffer internally.
    if (NULL == pDescriptor->pBuffer)
    {
        byteAlignmentMask = CalculateAlignmentMask(pDescriptor, u32BytesPerFrame);

        pAllocatedBuffer = (BYTE *)AllocateAligned(pDescriptor->u32MaxFrameCount * u32BytesPerFrame, static_cast<aligned_size_t>(byteAlignmentMask));
        RETURN_HR_IF_NULL(E_OUTOFMEMORY, pAllocatedBuffer);

        pDescriptor->pBuffer = reinterpret_cast<UINT_PTR>(pAllocatedBuffer);
        pDescriptor->Type = APO_CONNECTION_BUFFER_TYPE_ALLOCATED;
    }
    // Use the given buffer.
    else
    {
        pDescriptor->Type = APO_CONNECTION_BUFFER_TYPE_EXTERNAL;
    }
    
    pProperty->pBuffer = pDescriptor->pBuffer;
    pProperty->u32ValidFrameCount = 0;
    pProperty->u32Signature = APO_CONNECTION_PROPERTY_SIGNATURE;

    return S_OK;
} // CreateConnection

HRESULT
DestroyConnection(APO_CONNECTION_DESCRIPTOR *pDescriptor)
{
    HRESULT hResult;
    UINT32 u32BytesPerFrame;
    size_t byteAlignmentMask;
    BYTE*  pAllocatedBuffer;
    UNCOMPRESSEDAUDIOFORMAT Format;

    hResult = pDescriptor->pFormat->GetUncompressedAudioFormat(&Format);
    //IF_FAILED_JUMP(hResult, Exit);

    if (APO_CONNECTION_BUFFER_TYPE_ALLOCATED == pDescriptor->Type)
    {
        u32BytesPerFrame = 
            Format.dwSamplesPerFrame * Format.dwBytesPerSampleContainer;

        byteAlignmentMask = CalculateAlignmentMask(pDescriptor, u32BytesPerFrame);

        if (pDescriptor->pBuffer)
        {
            pAllocatedBuffer = reinterpret_cast<BYTE*>(pDescriptor->pBuffer);
        	RETURN_HR_IF_NULL(E_OUTOFMEMORY, pAllocatedBuffer);

            DeallocateAligned((void *) pAllocatedBuffer, static_cast<aligned_size_t>(byteAlignmentMask));
        }
    }

    pDescriptor->pFormat->Release();
    pDescriptor->pFormat = NULL;

    return hResult;
} // DestroyConnection


<|start_filename|>avstream/avscamera/sys/VideoHwSim.h<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2014, Microsoft Corporation.

    File:

        VideoHwSim.h

    Abstract:

        A fake h/w simulation for the video pin.  Provides Video pin specific 
        metadata.

    History:

        created 5/28/2014

**************************************************************************/

#pragma once
class CVideoHardwareSimulation :
    public CHardwareSimulation
{
public:
    CVideoHardwareSimulation(
        _Inout_ CSensor *Sensor,
        _In_    LONG PinID
    );

    virtual ~CVideoHardwareSimulation();

    //
    //  Emit metadata here for video pin.
    //
    void
    EmitMetadata(
        _Inout_ PKSSTREAM_HEADER    pStreamHeader
    );

private:
    BOOLEAN m_Illuminated;

};



<|start_filename|>avstream/avscamera/sys/AvsCamera.cpp<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2014, Microsoft Corporation.

    File:

        AvsCamera.cpp

    Abstract:

        Sample Camera driver initialization.

    History:

        created 5/15/2014

**************************************************************************/

#include "Common.h"

PVOID operator new
(
    size_t          iSize,
    _When_((poolType & NonPagedPoolMustSucceed) != 0,
       __drv_reportError("Must succeed pool allocations are forbidden. "
             "Allocation failures cause a system crash"))
    POOL_TYPE       poolType
)
{
    return ExAllocatePoolZero(poolType,iSize,'wNCK');
}

PVOID operator new
(
    size_t          iSize,
    _When_((poolType & NonPagedPoolMustSucceed) != 0,
       __drv_reportError("Must succeed pool allocations are forbidden. "
             "Allocation failures cause a system crash"))
    POOL_TYPE       poolType,
    ULONG           tag
)
{
    return ExAllocatePoolZero(poolType,iSize,tag);
}

PVOID 
operator new[](
    size_t          iSize,
    _When_((poolType & NonPagedPoolMustSucceed) != 0,
        __drv_reportError("Must succeed pool allocations are forbidden. "
            "Allocation failures cause a system crash"))
    POOL_TYPE       poolType,
    ULONG           tag
)
{
    return ExAllocatePoolZero(poolType, iSize, tag);
}

/*++

Routine Description:

    Array delete() operator.

Arguments:

    pVoid -
        The memory to free.

Return Value:

    None

--*/
void 
__cdecl 
operator delete[](
    PVOID pVoid
)
{
    if (pVoid)
    {
        ExFreePool(pVoid);
    }
}

/*++

Routine Description:

    Sized delete() operator.

Arguments:

    pVoid -
        The memory to free.

    size -
        The size of the memory to free.

Return Value:

    None

--*/
void __cdecl operator delete
(
    void *pVoid,
    size_t /*size*/
)
{
    if (pVoid)
    {
        ExFreePool(pVoid);
    }
}

/*++

Routine Description:

    Sized delete[]() operator.

Arguments:

    pVoid -
        The memory to free.

    size -
        The size of the memory to free.

Return Value:

    None

--*/
void __cdecl operator delete[]
(
    void *pVoid,
    size_t /*size*/
)
{
    if (pVoid)
    {
        ExFreePool(pVoid);
    }
}

void __cdecl operator delete
(
    PVOID pVoid
    )
{
    if (pVoid) {
        ExFreePool(pVoid);
    }
}

/**************************************************************************

    DESCRIPTOR AND DISPATCH LAYOUT

**************************************************************************/

//
// CaptureDeviceDispatch:
//
// This is the dispatch table for the capture device.  Plug and play
// notifications as well as power management notifications are dispatched
// through this table.
//
DEFINE_CAMERA_KSDEVICE_DISPATCH( AvsCameraDispatch, CAvsCameraDevice );

//
// CaptureDeviceDescriptor:
//
// This is the device descriptor for the capture device.  It points to the
// dispatch table and contains a list of filter descriptors that describe
// filter-types that this device supports.  Note that the filter-descriptors
// can be created dynamically and the factories created via
// KsCreateFilterFactory as well.
//
const
KSDEVICE_DESCRIPTOR
AvsCameraDeviceDescriptor =
{
    &AvsCameraDispatch,
    0,
    NULL
};

/**************************************************************************

    INITIALIZATION CODE

**************************************************************************/


extern "C" DRIVER_INITIALIZE DriverEntry;

extern "C"
NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)

/*++

Routine Description:

    Driver entry point.  Pass off control to the AVStream initialization
    function (KsInitializeDriver) and return the status code from it.

Arguments:

    DriverObject -
        The WDM driver object for our driver

    RegistryPath -
        The registry path for our registry info

Return Value:

    As from KsInitializeDriver

--*/

{
    //
    // Simply pass the device descriptor and parameters off to AVStream
    // to initialize us.  This will cause filter factories to be set up
    // at add & start.  Everything is done based on the descriptors passed
    // here.
    //
    return
        KsInitializeDriver (
            DriverObject,
            RegistryPath,
            &AvsCameraDeviceDescriptor
        );

}



<|start_filename|>audio/sysvad/EndpointsCommon/usbhsmictopo.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsmictopo.h

Abstract:

    Declaration of topology miniport for the mic (external: headphone).

--*/

#ifndef _SYSVAD_USBHSMICTOPO_H_
#define _SYSVAD_USBHSMICTOPO_H_

// Function declarations.
NTSTATUS
PropertyHandler_UsbHsMicTopoFilter( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
    );

NTSTATUS
PropertyHandler_UsbHsMicTopoNode( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
    );

NTSTATUS 
PropertyHandler_UsbHsMicTopoNodeEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

NTSTATUS
PropertyHandler_UsbHsMicTopoFilterEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

#endif // _SYSVAD_USBHSMICTOPO_H_



<|start_filename|>network/wlan/WDI/HAL/phydm/phydm.h<|end_filename|>
/******************************************************************************
 *
 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
 *                                        
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 *
 ******************************************************************************/


#ifndef	__HALDMOUTSRC_H__
#define __HALDMOUTSRC_H__

//============================================================
// include files
//============================================================
#include "phydm_pre_define.h"
#include "phydm_dig.h"
#include "phydm_edcaturbocheck.h"
#include "phydm_pathdiv.h"
#include "phydm_antdiv.h"
#include "phydm_antdect.h"
#include "phydm_dynamicbbpowersaving.h"
#include "phydm_rainfo.h"
#include "phydm_dynamictxpower.h"
#include "phydm_cfotracking.h"
#include "phydm_acs.h"
#include "phydm_adaptivity.h"


//#include "rtl8814a/phydm_iqk_8814a.h"
//#include "rtl8822b/phydm_iqk_8822b.h"



#if (DM_ODM_SUPPORT_TYPE & (ODM_AP))
#include "halphyrf_ap.h"
#include "phydm_powertracking_ap.h"
#endif

#if (DM_ODM_SUPPORT_TYPE & (ODM_CE))
#include "phydm_beamforming.h"
#include "phydm_noisemonitor.h"
#include "halphyrf_ce.h"
#include "phydm_powertracking_ce.h"
#endif

#if(DM_ODM_SUPPORT_TYPE & (ODM_WIN))
//#include "phydm_beamforming.h"
#include "phydm_rxhp.h"
#include "halphyrf_win.h"
#include "phydm_powertracking_win.h"
#endif

//============================================================
// Definition 
//============================================================
//
// 2011/09/22 MH Define all team supprt ability.
//

//
// 2011/09/22 MH Define for all teams. Please Define the constan in your precomp header.
//
//#define		DM_ODM_SUPPORT_AP			0
//#define		DM_ODM_SUPPORT_ADSL			0
//#define		DM_ODM_SUPPORT_CE			0
//#define		DM_ODM_SUPPORT_MP			1

//
// 2011/09/28 MH Define ODM SW team support flag.
//

//For SW AntDiv, PathDiv, 8192C AntDiv joint use
#define	TP_MODE		0
#define	RSSI_MODE		1

#define	TRAFFIC_LOW	0
#define	TRAFFIC_HIGH	1
#define	TRAFFIC_ULTRA_LOW	2

#define	NONE			0




//8723A High Power IGI Setting
#define		DM_DIG_HIGH_PWR_IGI_LOWER_BOUND	0x22
#define  		DM_DIG_Gmode_HIGH_PWR_IGI_LOWER_BOUND 0x28
#define		DM_DIG_HIGH_PWR_THRESHOLD	0x3a
#define		DM_DIG_LOW_PWR_THRESHOLD	0x14


//============================================================
// structure and define
//============================================================

//
// 2011/09/20 MH Add for AP/ADSLpseudo DM structuer requirement.
// We need to remove to other position???
//
#if(DM_ODM_SUPPORT_TYPE & (ODM_CE|ODM_WIN))
typedef		struct rtl8192cd_priv {
	u1Byte		temp;

}rtl8192cd_priv, *prtl8192cd_priv;
#endif


#if(DM_ODM_SUPPORT_TYPE & (ODM_AP|ODM_ADSL))
typedef		struct _ADAPTER{
	u1Byte		temp;
	#ifdef AP_BUILD_WORKAROUND
	HAL_DATA_TYPE*		temp2;
	prtl8192cd_priv		priv;
	#endif
}ADAPTER, *PADAPTER;
#endif

#if (DM_ODM_SUPPORT_TYPE == ODM_AP)

typedef		struct _WLAN_STA{
	u1Byte		temp;
} WLAN_STA, *PRT_WLAN_STA;

#endif

typedef struct _Dynamic_Primary_CCA{
	u1Byte		PriCCA_flag;
	u1Byte		intf_flag;
	u1Byte		intf_type;  
	u1Byte		DupRTS_flag;
	u1Byte		Monitor_flag;
	u1Byte		CH_offset;
	u1Byte  	MF_state;
}Pri_CCA_T, *pPri_CCA_T;


#if (DM_ODM_SUPPORT_TYPE & ODM_AP)


#ifdef ADSL_AP_BUILD_WORKAROUND
#define MAX_TOLERANCE			5
#define IQK_DELAY_TIME			1		//ms
#endif
#if 0//defined in 8192cd.h
//
// Indicate different AP vendor for IOT issue.
//
typedef enum _HT_IOT_PEER
{
	HT_IOT_PEER_UNKNOWN 			= 0,
	HT_IOT_PEER_REALTEK 			= 1,
	HT_IOT_PEER_REALTEK_92SE 		= 2,
	HT_IOT_PEER_BROADCOM 		= 3,
	HT_IOT_PEER_RALINK 			= 4,
	HT_IOT_PEER_ATHEROS 			= 5,
	HT_IOT_PEER_CISCO 				= 6,
	HT_IOT_PEER_MERU 				= 7,	
	HT_IOT_PEER_MARVELL 			= 8,
	HT_IOT_PEER_REALTEK_SOFTAP 	= 9,// peer is RealTek SOFT_AP, by Bohn, 2009.12.17
	HT_IOT_PEER_SELF_SOFTAP 		= 10, // Self is SoftAP
	HT_IOT_PEER_AIRGO 				= 11,
	HT_IOT_PEER_INTEL 				= 12, 
	HT_IOT_PEER_RTK_APCLIENT 		= 13, 
	HT_IOT_PEER_REALTEK_81XX 		= 14,	
	HT_IOT_PEER_REALTEK_WOW 		= 15,	
	HT_IOT_PEER_MAX 				= 16
}HT_IOT_PEER_E, *PHTIOT_PEER_E;
#endif
#endif//#if(DM_ODM_SUPPORT_TYPE & (ODM_AP|ODM_ADSL))

#define		DM_Type_ByFW			0
#define		DM_Type_ByDriver		1

//
// Declare for common info
//

#define IQK_THRESHOLD			8
#define DPK_THRESHOLD			4


#if (DM_ODM_SUPPORT_TYPE &  (ODM_AP))
__PACK typedef struct _ODM_Phy_Status_Info_
{
	u1Byte		RxPWDBAll;
	u1Byte		SignalQuality;					/* in 0-100 index. */
	u1Byte		RxMIMOSignalStrength[4];		/* in 0~100 index */
	s1Byte		RxMIMOSignalQuality[4];		/* EVM */
	s1Byte		RxSNR[4];					/* per-path's SNR */
#if (RTL8822B_SUPPORT == 1)
	u1Byte		RxCount;						/* RX path counter---*/
#endif
	u1Byte		BandWidth;

} __WLAN_ATTRIB_PACK__ ODM_PHY_INFO_T, *PODM_PHY_INFO_T;

typedef struct _ODM_Phy_Status_Info_Append_
{
	u1Byte		MAC_CRC32;	

}ODM_PHY_INFO_Append_T,*PODM_PHY_INFO_Append_T;

#else

typedef struct _ODM_Phy_Status_Info_
{
	//
	// Be care, if you want to add any element please insert between 
	// RxPWDBAll & SignalStrength.
	//
#if (DM_ODM_SUPPORT_TYPE &  (ODM_WIN))
	u4Byte		RxPWDBAll;	
#else
	u1Byte		RxPWDBAll;	
#endif
	u1Byte		SignalQuality;				/* in 0-100 index. */
	s1Byte		RxMIMOSignalQuality[4];		/* per-path's EVM */
	u1Byte		RxMIMOEVMdbm[4];			/* per-path's EVM dbm */
	u1Byte		RxMIMOSignalStrength[4];	/* in 0~100 index */
	s2Byte		Cfo_short[4];				/* per-path's Cfo_short */
	s2Byte		Cfo_tail[4];					/* per-path's Cfo_tail */
	s1Byte		RxPower;					/* in dBm Translate from PWdB */
	s1Byte		RecvSignalPower;			/* Real power in dBm for this packet, no beautification and aggregation. Keep this raw info to be used for the other procedures. */
	u1Byte		BTRxRSSIPercentage;
	u1Byte		SignalStrength;				/* in 0-100 index. */
	s1Byte		RxPwr[4];					/* per-path's pwdb */
	s1Byte		RxSNR[4];					/* per-path's SNR	*/
#if (RTL8822B_SUPPORT == 1)
	u1Byte		RxCount:2;					/* RX path counter---*/
	u1Byte		BandWidth:2;
	u1Byte		rxsc:4;						/* sub-channel---*/
#else
	u1Byte		BandWidth;
#endif
	u1Byte		btCoexPwrAdjust;
#if (RTL8822B_SUPPORT == 1)
	u1Byte		channel;						/* channel number---*/
	BOOLEAN		bMuPacket;					/* is MU packet or not---*/
	BOOLEAN		bBeamformed;				/* BF packet---*/
#endif
}ODM_PHY_INFO_T,*PODM_PHY_INFO_T;
#endif

typedef struct _ODM_Per_Pkt_Info_
{
	//u1Byte		Rate;	
	u1Byte		DataRate;
	u1Byte		StationID;
	BOOLEAN		bPacketMatchBSSID;
	BOOLEAN		bPacketToSelf;
	BOOLEAN		bPacketBeacon;
	BOOLEAN		bToSelf;
}ODM_PACKET_INFO_T,*PODM_PACKET_INFO_T;


typedef struct _ODM_Phy_Dbg_Info_
{
	//ODM Write,debug info
	s1Byte		RxSNRdB[4];
	u4Byte		NumQryPhyStatus;
	u4Byte		NumQryPhyStatusCCK;
	u4Byte		NumQryPhyStatusOFDM;
#if (RTL8822B_SUPPORT == 1)
	u4Byte		NumQryMuPkt;
	u4Byte		NumQryBfPkt;
#endif
	u1Byte		NumQryBeaconPkt;
	//Others
	s4Byte		RxEVM[4];	
	
}ODM_PHY_DBG_INFO_T;


typedef struct _ODM_Mac_Status_Info_
{
	u1Byte	test;
	
}ODM_MAC_INFO;

//
// 2011/20/20 MH For MP driver RT_WLAN_STA =  STA_INFO_T
// Please declare below ODM relative info in your STA info structure.
//
#if 1
typedef		struct _ODM_STA_INFO{
	// Driver Write
	BOOLEAN		bUsed;				// record the sta status link or not?
	//u1Byte		WirelessMode;		// 
	u1Byte		IOTPeer;			// Enum value.	HT_IOT_PEER_E

	// ODM Write
	//1 PHY_STATUS_INFO
	u1Byte		RSSI_Path[4];		// 
	u1Byte		RSSI_Ave;
	u1Byte		RXEVM[4];
	u1Byte		RXSNR[4];

	// ODM Write
	//1 TX_INFO (may changed by IC)
	//TX_INFO_T		pTxInfo;				// Define in IC folder. Move lower layer.
#if 0
	u1Byte		ANTSEL_A;			//in Jagar: 4bit; others: 2bit
	u1Byte		ANTSEL_B;			//in Jagar: 4bit; others: 2bit
	u1Byte		ANTSEL_C;			//only in Jagar: 4bit
	u1Byte		ANTSEL_D;			//only in Jagar: 4bit
	u1Byte		TX_ANTL;			//not in Jagar: 2bit
	u1Byte		TX_ANT_HT;			//not in Jagar: 2bit
	u1Byte		TX_ANT_CCK;			//not in Jagar: 2bit
	u1Byte		TXAGC_A;			//not in Jagar: 4bit
	u1Byte		TXAGC_B;			//not in Jagar: 4bit
	u1Byte		TXPWR_OFFSET;		//only in Jagar: 3bit
	u1Byte		TX_ANT;				//only in Jagar: 4bit for TX_ANTL/TX_ANTHT/TX_ANT_CCK
#endif

	//
	// 	Please use compile flag to disabe the strcutrue for other IC except 88E.
	//	Move To lower layer.
	//
	// ODM Write Wilson will handle this part(said by Luke.Lee)
	//TX_RPT_T		pTxRpt;				// Define in IC folder. Move lower layer.
#if 0	
	//1 For 88E RA (don't redefine the naming)
	u1Byte		rate_id;
	u1Byte		rate_SGI;
	u1Byte		rssi_sta_ra;
	u1Byte		SGI_enable;
	u1Byte		Decision_rate;
	u1Byte		Pre_rate;
	u1Byte		Active;

	// Driver write Wilson handle.
	//1 TX_RPT (don't redefine the naming)
	u2Byte		RTY[4];				// ???
	u2Byte		TOTAL;				// ???
	u2Byte		DROP;				// ???
	//
	// Please use compile flag to disabe the strcutrue for other IC except 88E.
	//
#endif

}ODM_STA_INFO_T, *PODM_STA_INFO_T;
#endif

//
// 2011/10/20 MH Define Common info enum for all team.
//
typedef enum _ODM_Common_Info_Definition
{
//-------------REMOVED CASE-----------//
	//ODM_CMNINFO_CCK_HP,
	//ODM_CMNINFO_RFPATH_ENABLE,		// Define as ODM write???	
	//ODM_CMNINFO_BT_COEXIST,				// ODM_BT_COEXIST_E
	//ODM_CMNINFO_OP_MODE,				// ODM_OPERATION_MODE_E
//-------------REMOVED CASE-----------//

	//
	// Fixed value:
	//

	//-----------HOOK BEFORE REG INIT-----------//
	ODM_CMNINFO_PLATFORM = 0,
	ODM_CMNINFO_ABILITY,					// ODM_ABILITY_E
	ODM_CMNINFO_INTERFACE,				// ODM_INTERFACE_E
	ODM_CMNINFO_MP_TEST_CHIP,
	ODM_CMNINFO_IC_TYPE,					// ODM_IC_TYPE_E
	ODM_CMNINFO_CUT_VER,					// ODM_CUT_VERSION_E
	ODM_CMNINFO_FAB_VER,					// ODM_FAB_E
	ODM_CMNINFO_RF_TYPE,					// ODM_RF_PATH_E or ODM_RF_TYPE_E?
	ODM_CMNINFO_RFE_TYPE, 
	ODM_CMNINFO_BOARD_TYPE,				// ODM_BOARD_TYPE_E
	ODM_CMNINFO_PACKAGE_TYPE,
	ODM_CMNINFO_EXT_LNA,					// TRUE
	ODM_CMNINFO_5G_EXT_LNA,	
	ODM_CMNINFO_EXT_PA,
	ODM_CMNINFO_5G_EXT_PA,
	ODM_CMNINFO_GPA,
	ODM_CMNINFO_APA,
	ODM_CMNINFO_GLNA,
	ODM_CMNINFO_ALNA,
	ODM_CMNINFO_EXT_TRSW,
	ODM_CMNINFO_EXT_LNA_GAIN,
	ODM_CMNINFO_PATCH_ID,				//CUSTOMER ID
	ODM_CMNINFO_BINHCT_TEST,
	ODM_CMNINFO_BWIFI_TEST,
	ODM_CMNINFO_SMART_CONCURRENT,
	ODM_CMNINFO_CONFIG_BB_RF,
	ODM_CMNINFO_DOMAIN_CODE_2G,
	ODM_CMNINFO_DOMAIN_CODE_5G,
	ODM_CMNINFO_IQKFWOFFLOAD,
	ODM_CMNINFO_HUBUSBMODE,
	ODM_CMNINFO_FWDWRSVDPAGEINPROGRESS,
	ODM_CMNINFO_TX_TP,
	ODM_CMNINFO_RX_TP,
	ODM_CMNINFO_SOUNDING_SEQ,
	//-----------HOOK BEFORE REG INIT-----------//	


	//
	// Dynamic value:
	//
//--------- POINTER REFERENCE-----------//
	ODM_CMNINFO_MAC_PHY_MODE,			// ODM_MAC_PHY_MODE_E
	ODM_CMNINFO_TX_UNI,
	ODM_CMNINFO_RX_UNI,
	ODM_CMNINFO_WM_MODE,				// ODM_WIRELESS_MODE_E
	ODM_CMNINFO_BAND,					// ODM_BAND_TYPE_E
	ODM_CMNINFO_SEC_CHNL_OFFSET,		// ODM_SEC_CHNL_OFFSET_E
	ODM_CMNINFO_SEC_MODE,				// ODM_SECURITY_E
	ODM_CMNINFO_BW,						// ODM_BW_E
	ODM_CMNINFO_CHNL,
	ODM_CMNINFO_FORCED_RATE,
	
	ODM_CMNINFO_DMSP_GET_VALUE,
	ODM_CMNINFO_BUDDY_ADAPTOR,
	ODM_CMNINFO_DMSP_IS_MASTER,
	ODM_CMNINFO_SCAN,
	ODM_CMNINFO_POWER_SAVING,
	ODM_CMNINFO_ONE_PATH_CCA,			// ODM_CCA_PATH_E
	ODM_CMNINFO_DRV_STOP,
	ODM_CMNINFO_PNP_IN,
	ODM_CMNINFO_INIT_ON,
	ODM_CMNINFO_ANT_TEST,
	ODM_CMNINFO_NET_CLOSED,
	//ODM_CMNINFO_RTSTA_AID,				// For win driver only?
	ODM_CMNINFO_FORCED_IGI_LB,
	ODM_CMNINFO_P2P_LINK,
	ODM_CMNINFO_FCS_MODE,
	ODM_CMNINFO_IS1ANTENNA,
	ODM_CMNINFO_RFDEFAULTPATH,
//--------- POINTER REFERENCE-----------//

//------------CALL BY VALUE-------------//
	ODM_CMNINFO_WIFI_DIRECT,
	ODM_CMNINFO_WIFI_DISPLAY,
	ODM_CMNINFO_LINK_IN_PROGRESS,			
	ODM_CMNINFO_LINK,
	ODM_CMNINFO_STATION_STATE,
	ODM_CMNINFO_RSSI_MIN,
	ODM_CMNINFO_DBG_COMP,				// u8Byte
	ODM_CMNINFO_DBG_LEVEL,				// u4Byte
	ODM_CMNINFO_RA_THRESHOLD_HIGH,		// u1Byte
	ODM_CMNINFO_RA_THRESHOLD_LOW,		// u1Byte
	ODM_CMNINFO_RF_ANTENNA_TYPE,		// u1Byte
	ODM_CMNINFO_BT_ENABLED,
	ODM_CMNINFO_BT_HS_CONNECT_PROCESS,
	ODM_CMNINFO_BT_HS_RSSI,
	ODM_CMNINFO_BT_OPERATION,
	ODM_CMNINFO_BT_LIMITED_DIG,					//Need to Limited Dig or not
	ODM_CMNINFO_BT_DIG,
	ODM_CMNINFO_BT_BUSY,					//Check Bt is using or not//neil	
	ODM_CMNINFO_BT_DISABLE_EDCA,
#if(DM_ODM_SUPPORT_TYPE & ODM_AP)		// for repeater mode add by YuChen 2014.06.23
#ifdef UNIVERSAL_REPEATER
	ODM_CMNINFO_VXD_LINK,
#endif
#endif
	ODM_CMNINFO_AP_TOTAL_NUM,
	ODM_CMNINFO_POWER_TRAINING,
//------------CALL BY VALUE-------------//

	//
	// Dynamic ptr array hook itms.
	//
	ODM_CMNINFO_STA_STATUS,
	ODM_CMNINFO_PHY_STATUS,
	ODM_CMNINFO_MAC_STATUS,
	
	ODM_CMNINFO_MAX,


}ODM_CMNINFO_E;

//
// 2011/10/20 MH Define ODM support ability.  ODM_CMNINFO_ABILITY
//
typedef enum _ODM_Support_Ability_Definition
{
	//
	// BB ODM section BIT 0-19
	//
	ODM_BB_DIG					= BIT0,
	ODM_BB_RA_MASK				= BIT1,
	ODM_BB_DYNAMIC_TXPWR		= BIT2,
	ODM_BB_FA_CNT					= BIT3,
	ODM_BB_RSSI_MONITOR			= BIT4,
	ODM_BB_CCK_PD				= BIT5,
	ODM_BB_ANT_DIV				= BIT6,
	ODM_BB_PWR_SAVE				= BIT7,
	ODM_BB_PWR_TRAIN				= BIT8,
	ODM_BB_RATE_ADAPTIVE			= BIT9,
	ODM_BB_PATH_DIV				= BIT10,
	ODM_BB_PSD					= BIT11,
	ODM_BB_RXHP					= BIT12,
	ODM_BB_ADAPTIVITY				= BIT13,
	ODM_BB_CFO_TRACKING			= BIT14,
	ODM_BB_NHM_CNT				= BIT15,
	ODM_BB_PRIMARY_CCA			= BIT16,
	ODM_BB_TXBF				= BIT17,
	
	//
	// MAC DM section BIT 20-23
	//
	ODM_MAC_EDCA_TURBO			= BIT20,
	ODM_MAC_EARLY_MODE			= BIT21,
	
	//
	// RF ODM section BIT 24-31
	//
	ODM_RF_TX_PWR_TRACK			= BIT24,
	ODM_RF_RX_GAIN_TRACK			= BIT25,
	ODM_RF_CALIBRATION			= BIT26,
	
}ODM_ABILITY_E;

//Move some non-DM enum,define, struc. form phydm.h to phydm_types.h by Dino

// ODM_CMNINFO_ONE_PATH_CCA
typedef enum tag_CCA_Path
{
	ODM_CCA_2R			= 0,
	ODM_CCA_1R_A		= 1,
	ODM_CCA_1R_B		= 2,
}ODM_CCA_PATH_E;

//move RAInfo to Phydm_RaInfo.h

//Remove struct  PATHDIV_PARA to odm_PathDiv.h 

//Remove struct to odm_PowerTracking.h by YuChen
//
// ODM Dynamic common info value definition
//
//Move AntDiv form phydm.h to Phydm_AntDiv.h by Dino

//move PathDiv to Phydm_PathDiv.h

typedef enum _BASEBAND_CONFIG_PHY_REG_PG_VALUE_TYPE{
	PHY_REG_PG_RELATIVE_VALUE = 0,
	PHY_REG_PG_EXACT_VALUE = 1
} PHY_REG_PG_TYPE;

//
// 2011/09/22 MH Copy from SD4 defined structure. We use to support PHY DM integration.
//
#if(DM_ODM_SUPPORT_TYPE & ODM_WIN)
#if (RT_PLATFORM != PLATFORM_LINUX)
typedef 
#endif
	
struct DM_Out_Source_Dynamic_Mechanism_Structure
#else// for AP,ADSL,CE Team
typedef  struct DM_Out_Source_Dynamic_Mechanism_Structure
#endif
{
	//RT_TIMER 	FastAntTrainingTimer;
	//
	//	Add for different team use temporarily
	//
	PADAPTER		Adapter;		// For CE/NIC team
	prtl8192cd_priv	priv;			// For AP/ADSL team
	// WHen you use Adapter or priv pointer, you must make sure the pointer is ready.
	BOOLEAN			odm_ready;

#if(DM_ODM_SUPPORT_TYPE & (ODM_CE|ODM_WIN))
	rtl8192cd_priv		fake_priv;
#endif
#if(DM_ODM_SUPPORT_TYPE & (ODM_AP|ODM_ADSL))
	// ADSL_AP_BUILD_WORKAROUND
	ADAPTER			fake_adapter;
#endif
	
	PHY_REG_PG_TYPE		PhyRegPgValueType;
	u1Byte				PhyRegPgVersion;

	u8Byte			DebugComponents;
	u4Byte			DebugLevel;
	
	u4Byte			NumQryPhyStatusAll; 	//CCK + OFDM
	u4Byte			LastNumQryPhyStatusAll; 
	u4Byte			RxPWDBAve;
	BOOLEAN			MPDIG_2G; 		//off MPDIG
	u1Byte			Times_2G;
	
//------ ODM HANDLE, DRIVER NEEDS NOT TO HOOK------//
	BOOLEAN			bCckHighPower; 
	u1Byte			RFPathRxEnable;		// ODM_CMNINFO_RFPATH_ENABLE
	u1Byte			ControlChannel;
//------ ODM HANDLE, DRIVER NEEDS NOT TO HOOK------//

//--------REMOVED COMMON INFO----------//
	//u1Byte				PseudoMacPhyMode;
	//BOOLEAN			*BTCoexist;
	//BOOLEAN			PseudoBtCoexist;
	//u1Byte				OPMode;
	//BOOLEAN			bAPMode;
	//BOOLEAN			bClientMode;
	//BOOLEAN			bAdHocMode;
	//BOOLEAN			bSlaveOfDMSP;
//--------REMOVED COMMON INFO----------//


//1  COMMON INFORMATION

	//
	// Init Value
	//
//-----------HOOK BEFORE REG INIT-----------//	
	// ODM Platform info AP/ADSL/CE/MP = 1/2/3/4
	u1Byte			SupportPlatform;		
	// ODM Support Ability DIG/RATR/TX_PWR_TRACK/ ¡K¡K = 1/2/3/¡K
	u4Byte			SupportAbility;
	// ODM PCIE/USB/SDIO = 1/2/3
	u1Byte			SupportInterface;			
	// ODM composite or independent. Bit oriented/ 92C+92D+ .... or any other type = 1/2/3/...
	u4Byte			SupportICType;	
	// Cut Version TestChip/A-cut/B-cut... = 0/1/2/3/...
	u1Byte			CutVersion;
	// Fab Version TSMC/UMC = 0/1
	u1Byte			FabVersion;
	// RF Type 4T4R/3T3R/2T2R/1T2R/1T1R/...
	u1Byte			RFType;
	u1Byte			RFEType;	
	// Board Type Normal/HighPower/MiniCard/SLIM/Combo/... = 0/1/2/3/4/...
	u1Byte			BoardType;
	u1Byte			PackageType;
	u2Byte			TypeGLNA;
	u2Byte			TypeGPA;
	u2Byte			TypeALNA;
	u2Byte			TypeAPA;
	// with external LNA  NO/Yes = 0/1
	u1Byte			ExtLNA; // 2G
	u1Byte			ExtLNA5G; //5G
	// with external PA  NO/Yes = 0/1
	u1Byte			ExtPA; // 2G
	u1Byte			ExtPA5G; //5G
	// with external TRSW  NO/Yes = 0/1
	u1Byte			ExtTRSW;
	u1Byte			ExtLNAGain; // 2G
	u1Byte			PatchID; //Customer ID
	BOOLEAN			bInHctTest;
	BOOLEAN			bWIFITest;

	BOOLEAN			bDualMacSmartConcurrent;
	u4Byte			BK_SupportAbility;
	u1Byte			AntDivType;
	BOOLEAN			ConfigBBRF;
	u1Byte			odm_Regulation2_4G;
	u1Byte			odm_Regulation5G;
	u1Byte			IQKFWOffload;
	BOOLEAN			cck_new_agc;
//-----------HOOK BEFORE REG INIT-----------//	

	//
	// Dynamic Value
	//	
//--------- POINTER REFERENCE-----------//

	u1Byte			u1Byte_temp;
	BOOLEAN			BOOLEAN_temp;
	PADAPTER		PADAPTER_temp;
	
	// MAC PHY Mode SMSP/DMSP/DMDP = 0/1/2
	u1Byte			*pMacPhyMode;
	//TX Unicast byte count
	u8Byte			*pNumTxBytesUnicast;
	//RX Unicast byte count
	u8Byte			*pNumRxBytesUnicast;
	// Wireless mode B/G/A/N = BIT0/BIT1/BIT2/BIT3
	u1Byte			*pWirelessMode; //ODM_WIRELESS_MODE_E
	// Frequence band 2.4G/5G = 0/1
	u1Byte			*pBandType;
	// Secondary channel offset don't_care/below/above = 0/1/2
	u1Byte			*pSecChOffset;
	// Security mode Open/WEP/AES/TKIP = 0/1/2/3
	u1Byte			*pSecurity;
	// BW info 20M/40M/80M = 0/1/2
	u1Byte			*pBandWidth;
 	// Central channel location Ch1/Ch2/....
	u1Byte			*pChannel;	//central channel number
	BOOLEAN			DPK_Done;
	// Common info for 92D DMSP
	
	BOOLEAN			*pbGetValueFromOtherMac;
	PADAPTER		*pBuddyAdapter;
	BOOLEAN			*pbMasterOfDMSP; //MAC0: master, MAC1: subordinate
	// Common info for Status
	BOOLEAN			*pbScanInProcess;
	BOOLEAN			*pbPowerSaving;
	// CCA Path 2-path/path-A/path-B = 0/1/2; using ODM_CCA_PATH_E.
	u1Byte			*pOnePathCCA;
	//pMgntInfo->AntennaTest
	u1Byte			*pAntennaTest;
	BOOLEAN			*pbNet_closed;
	//u1Byte			*pAidMap;
	u1Byte			*pu1ForcedIgiLb;
	BOOLEAN			*pIsFcsModeEnable;
/*--------- For 8723B IQK-----------*/
	BOOLEAN			*pIs1Antenna;
	u1Byte			*pRFDefaultPath;
	// 0:S1, 1:S0
	
//--------- POINTER REFERENCE-----------//
	pu2Byte			pForcedDataRate;
	pu1Byte			HubUsbMode;
	BOOLEAN			*pbFwDwRsvdPageInProgress;
	u4Byte			*pCurrentTxTP;
	u4Byte			*pCurrentRxTP;
	u1Byte			*pSoundingSeq;
//------------CALL BY VALUE-------------//
	BOOLEAN			bLinkInProcess;
	BOOLEAN			bWIFI_Direct;
	BOOLEAN			bWIFI_Display;
	BOOLEAN			bLinked;
	BOOLEAN			bsta_state;
#if(DM_ODM_SUPPORT_TYPE & ODM_AP)		// for repeater mode add by YuChen 2014.06.23
#ifdef UNIVERSAL_REPEATER
	BOOLEAN			VXD_bLinked;
#endif
#endif									// for repeater mode add by YuChen 2014.06.23	
	u1Byte			RSSI_Min;	
	u1Byte			InterfaceIndex; /*Add for 92D  dual MAC: 0--Mac0 1--Mac1*/
	BOOLEAN			bIsMPChip;
	BOOLEAN			bOneEntryOnly;
	BOOLEAN			mp_mode;
	u4Byte			OneEntry_MACID;
	u1Byte			pre_number_linked_client;	
	u1Byte			number_linked_client;
	u1Byte			pre_number_active_client;	
	u1Byte			number_active_client;
	// Common info for BTDM
	BOOLEAN			bBtEnabled;			// BT is enabled
	BOOLEAN			bBtConnectProcess;	// BT HS is under connection progress.
	u1Byte			btHsRssi;				// BT HS mode wifi rssi value.
	BOOLEAN			bBtHsOperation;		// BT HS mode is under progress
	u1Byte			btHsDigVal;			// use BT rssi to decide the DIG value
	BOOLEAN			bBtDisableEdcaTurbo;	// Under some condition, don't enable the EDCA Turbo
	BOOLEAN			bBtBusy;   			// BT is busy.
	BOOLEAN			bBtLimitedDig;   		// BT is busy.
	BOOLEAN			bDisablePhyApi;
//------------CALL BY VALUE-------------//
	u1Byte			RSSI_A;
	u1Byte			RSSI_B;
	u1Byte			RSSI_C;
	u1Byte			RSSI_D;
	u8Byte			RSSI_TRSW;	
	u8Byte			RSSI_TRSW_H;
	u8Byte			RSSI_TRSW_L;	
	u8Byte			RSSI_TRSW_iso;
	u1Byte			TXAntStatus;
	u1Byte			RXAntStatus;
	u1Byte			cck_lna_idx;
	u1Byte			cck_vga_idx;
	u1Byte			ofdm_agc_idx[4];

	u1Byte			RxRate;
	BOOLEAN			bNoisyState;
	u1Byte			TxRate;
	u1Byte			LinkedInterval;
	u1Byte			preChannel;
	u4Byte			TxagcOffsetValueA;
	BOOLEAN			IsTxagcOffsetPositiveA;
	u4Byte			TxagcOffsetValueB;
	BOOLEAN			IsTxagcOffsetPositiveB;
	u8Byte			lastTxOkCnt;
	u8Byte			lastRxOkCnt;
	u4Byte			BbSwingOffsetA;
	BOOLEAN			IsBbSwingOffsetPositiveA;
	u4Byte			BbSwingOffsetB;
	BOOLEAN			IsBbSwingOffsetPositiveB;
	u1Byte			antdiv_rssi;
	u1Byte			fat_comb_a;
	u1Byte			fat_comb_b;
	u1Byte			antdiv_intvl;
	u1Byte			AntType;
	u1Byte			pre_AntType;
	u1Byte			antdiv_period;
	u1Byte			antdiv_select;
	u1Byte			path_select;	
	u1Byte			antdiv_evm_en;
	u1Byte			bdc_holdstate;
	u1Byte			NdpaPeriod;
	BOOLEAN			H2C_RARpt_connect;
	BOOLEAN			cck_agc_report_type;
	
	u1Byte			dm_dig_max_TH;
	u1Byte 			dm_dig_min_TH;
	u1Byte 			print_agc;

	//For Adaptivtiy
	u2Byte			NHM_cnt_0;
	u2Byte			NHM_cnt_1;
	s1Byte			TH_L2H_ini;
	s1Byte			TH_EDCCA_HL_diff;
	s1Byte			TH_L2H_ini_mode2;
	s1Byte			TH_EDCCA_HL_diff_mode2;
	BOOLEAN			Carrier_Sense_enable;
	u1Byte			Adaptivity_IGI_upper;
	BOOLEAN			adaptivity_flag;
	u1Byte			DCbackoff;
	BOOLEAN			Adaptivity_enable;
	u1Byte			APTotalNum;
	BOOLEAN			EDCCA_enable;
	ADAPTIVITY_STATISTICS	Adaptivity;
	//For Adaptivtiy
	u1Byte			LastUSBHub;
	u1Byte			TxBfDataRate;
	u1Byte			CurrentAddress[6];
	
	u1Byte			c2h_cmd_start;
	u1Byte			fw_debug_trace[60]; 
	u1Byte			pre_c2h_seq;
	BOOLEAN			fw_buff_is_enpty;
	u4Byte			data_frame_num;

	/*for noise detection*/
	BOOLEAN			NoisyDecision; /*b_noisy*/
	BOOLEAN			pre_b_noisy;	
	u4Byte			NoisyDecision_Smooth;

#if (DM_ODM_SUPPORT_TYPE &  (ODM_CE))
	ODM_NOISE_MONITOR noise_level;//[ODM_MAX_CHANNEL_NUM];
#endif
	//
	//2 Define STA info.
	// _ODM_STA_INFO
	// 2012/01/12 MH For MP, we need to reduce one array pointer for default port.??
	PSTA_INFO_T		pODM_StaInfo[ODM_ASSOCIATE_ENTRY_NUM];
	u2Byte			platform2phydm_macid_table[ODM_ASSOCIATE_ENTRY_NUM];		/* platform_macid_table[platform_macid] = phydm_macid */

	u2Byte 			CurrminRptTime;
	ODM_RA_INFO_T   RAInfo[ODM_ASSOCIATE_ENTRY_NUM]; //Use MacID as array index. STA MacID=0, VWiFi Client MacID={1, ODM_ASSOCIATE_ENTRY_NUM-1} //YJ,add,120119

	//
	// 2012/02/14 MH Add to share 88E ra with other SW team.
	// We need to colelct all support abilit to a proper area.
	//
	BOOLEAN				RaSupport88E;

	// Define ...........

	// Latest packet phy info (ODM write)
	ODM_PHY_DBG_INFO_T	 PhyDbgInfo;
	//PHY_INFO_88E		PhyInfo;

	// Latest packet phy info (ODM write)
	ODM_MAC_INFO		*pMacInfo;
	//MAC_INFO_88E		MacInfo;

	// Different Team independt structure??

	//
	//TX_RTP_CMN		TX_retrpo;
	//TX_RTP_88E		TX_retrpo;
	//TX_RTP_8195		TX_retrpo;

	//
	//ODM Structure
	//
#if (defined(CONFIG_HW_ANTENNA_DIVERSITY))
	#if (DM_ODM_SUPPORT_TYPE & (ODM_AP))
	BDC_T					DM_BdcTable;
	#endif
	
	#ifdef CONFIG_HL_SMART_ANTENNA_TYPE1
	SAT_T						dm_sat_table;
	#endif
	
#endif
	FAT_T						DM_FatTable;
	DIG_T						DM_DigTable;

	PS_T						DM_PSTable;
	Pri_CCA_T					DM_PriCCA;
#if (DM_ODM_SUPPORT_TYPE & ODM_WIN)
	RXHP_T						DM_RXHP_Table;
#endif
	RA_T						DM_RA_Table;  
	FALSE_ALARM_STATISTICS		FalseAlmCnt;
	FALSE_ALARM_STATISTICS		FlaseAlmCntBuddyAdapter;
	SWAT_T						DM_SWAT_Table;
	BOOLEAN						RSSI_test;
	CFO_TRACKING    				DM_CfoTrack;
	ACS							DM_ACS;



	//IQK_INFO	IQK_info;


#if (RTL8822B_SUPPORT == 1)
	IQK_INFO	IQK_info_8822B;
#endif /* (RTL8814A_SUPPORT==1) */


#if (DM_ODM_SUPPORT_TYPE & ODM_WIN)
	//Path Div Struct
	PATHDIV_PARA	pathIQK;
#endif
#if(defined(CONFIG_PATH_DIVERSITY))
	PATHDIV_T	DM_PathDiv;
#endif	

	EDCA_T		DM_EDCA_Table;
	u4Byte		WMMEDCA_BE;

	// Copy from SD4 structure
	//
	// ==================================================
	//

	//common
	//u1Byte		DM_Type;	
	//u1Byte    PSD_Report_RXHP[80];   // Add By Gary
	//u1Byte    PSD_func_flag;               // Add By Gary
	//for DIG
	//u1Byte		bDMInitialGainEnable;
	//u1Byte		binitialized; // for dm_initial_gain_Multi_STA use.
	//for Antenna diversity
	//u8	AntDivCfg;// 0:OFF , 1:ON, 2:by efuse
	//PSTA_INFO_T RSSI_target;

	BOOLEAN			*pbDriverStopped;
	BOOLEAN			*pbDriverIsGoingToPnpSetPowerSleep;
	BOOLEAN			*pinit_adpt_in_progress;

	//PSD
	BOOLEAN			bUserAssignLevel;
	RT_TIMER 		PSDTimer;
	u1Byte			RSSI_BT;			//come from BT
	BOOLEAN			bPSDinProcess;
	BOOLEAN			bPSDactive;
	BOOLEAN			bDMInitialGainEnable;

	//MPT DIG
	RT_TIMER 		MPT_DIGTimer;
	
	//for rate adaptive, in fact,  88c/92c fw will handle this
	u1Byte			bUseRAMask;

	ODM_RATE_ADAPTIVE	RateAdaptive;
//#if (DM_ODM_SUPPORT_TYPE & (ODM_WIN | ODM_CE))
#if(defined(CONFIG_ANT_DETECTION))
	ANT_DETECTED_INFO	AntDetectedInfo; // Antenna detected information for RSSI tool
#endif
	ODM_RF_CAL_T	RFCalibrateInfo;

	
	//
	// Dynamic ATC switch
	//

#if (DM_ODM_SUPPORT_TYPE &  (ODM_WIN|ODM_CE))	
	//
	// Power Training
	//
	u1Byte			ForcePowerTrainingState;
	BOOLEAN			bChangeState;
	u4Byte			PT_score;
	u8Byte			OFDM_RX_Cnt;
	u8Byte			CCK_RX_Cnt;
#endif
	BOOLEAN			bDisablePowerTraining;

	//
	// ODM system resource.
	//

	// ODM relative time.
	RT_TIMER 				PathDivSwitchTimer;
	//2011.09.27 add for Path Diversity
	RT_TIMER				CCKPathDiversityTimer;
	RT_TIMER 	FastAntTrainingTimer;
#ifdef ODM_EVM_ENHANCE_ANTDIV
	RT_TIMER 			EVM_FastAntTrainingTimer;
#endif
	RT_TIMER		sbdcnt_timer;

	// ODM relative workitem.
#if (DM_ODM_SUPPORT_TYPE == ODM_WIN)
#if USE_WORKITEM
	RT_WORK_ITEM			PathDivSwitchWorkitem;
	RT_WORK_ITEM			CCKPathDiversityWorkitem;
	RT_WORK_ITEM			FastAntTrainingWorkitem;
	RT_WORK_ITEM			MPT_DIGWorkitem;
	RT_WORK_ITEM			RaRptWorkitem;
	RT_WORK_ITEM			sbdcnt_workitem;
#endif
#endif

#if (DM_ODM_SUPPORT_TYPE & (ODM_WIN | ODM_CE))
#if (BEAMFORMING_SUPPORT == 1)
	RT_BEAMFORMING_INFO BeamformingInfo;
#endif 
#endif

#if(DM_ODM_SUPPORT_TYPE & ODM_WIN)
	
#if (RT_PLATFORM != PLATFORM_LINUX)
} DM_ODM_T, *PDM_ODM_T;		// DM_Dynamic_Mechanism_Structure
#else
};
#endif	

#else// for AP,ADSL,CE Team
} DM_ODM_T, *PDM_ODM_T;		// DM_Dynamic_Mechanism_Structure
#endif


typedef enum _PHYDM_STRUCTURE_TYPE{
	PHYDM_FALSEALMCNT,
	PHYDM_CFOTRACK,
	PHYDM_ADAPTIVITY,
	PHYDM_ROMINFO,
	
}PHYDM_STRUCTURE_TYPE;



 typedef enum _ODM_RF_CONTENT{
	odm_radioa_txt = 0x1000,
	odm_radiob_txt = 0x1001,
	odm_radioc_txt = 0x1002,
	odm_radiod_txt = 0x1003
} ODM_RF_CONTENT;

typedef enum _ODM_BB_Config_Type{
	CONFIG_BB_PHY_REG,   
	CONFIG_BB_AGC_TAB,   
	CONFIG_BB_AGC_TAB_2G,
	CONFIG_BB_AGC_TAB_5G, 
	CONFIG_BB_PHY_REG_PG,
	CONFIG_BB_PHY_REG_MP,
	CONFIG_BB_AGC_TAB_DIFF,
} ODM_BB_Config_Type, *PODM_BB_Config_Type;

typedef enum _ODM_RF_Config_Type{ 
	CONFIG_RF_RADIO,
    CONFIG_RF_TXPWR_LMT,
} ODM_RF_Config_Type, *PODM_RF_Config_Type;

typedef enum _ODM_FW_Config_Type{
    CONFIG_FW_NIC,
    CONFIG_FW_NIC_2,
    CONFIG_FW_AP,
    CONFIG_FW_AP_2,
    CONFIG_FW_MP,
    CONFIG_FW_WoWLAN,
    CONFIG_FW_WoWLAN_2,
    CONFIG_FW_AP_WoWLAN,
    CONFIG_FW_BT,
} ODM_FW_Config_Type;

// Status code
#if (DM_ODM_SUPPORT_TYPE != ODM_WIN)
typedef enum _RT_STATUS{
	RT_STATUS_SUCCESS,
	RT_STATUS_FAILURE,
	RT_STATUS_PENDING,
	RT_STATUS_RESOURCE,
	RT_STATUS_INVALID_CONTEXT,
	RT_STATUS_INVALID_PARAMETER,
	RT_STATUS_NOT_SUPPORT,
	RT_STATUS_OS_API_FAILED,
}RT_STATUS,*PRT_STATUS;
#endif // end of RT_STATUS definition

#ifdef REMOVE_PACK
#pragma pack()
#endif

//#include "odm_function.h"

//3===========================================================
//3 DIG
//3===========================================================

//Remove DIG by Yuchen

//3===========================================================
//3 AGC RX High Power Mode
//3===========================================================
#define          LNA_Low_Gain_1                      0x64
#define          LNA_Low_Gain_2                      0x5A
#define          LNA_Low_Gain_3                      0x58

#define          FA_RXHP_TH1                           5000
#define          FA_RXHP_TH2                           1500
#define          FA_RXHP_TH3                             800
#define          FA_RXHP_TH4                             600
#define          FA_RXHP_TH5                             500

//3===========================================================
//3 EDCA
//3===========================================================

//3===========================================================
//3 Dynamic Tx Power
//3===========================================================
//Dynamic Tx Power Control Threshold

//Remove By YuChen

//3===========================================================
//3 Tx Power Tracking
//3===========================================================



//3===========================================================
//3 Rate Adaptive
//3===========================================================
//Remove to odm_RaInfo.h by RS_James

//3===========================================================
//3 BB Power Save
//3===========================================================

typedef enum tag_1R_CCA_Type_Definition
{
	CCA_1R =0,
	CCA_2R = 1,
	CCA_MAX = 2,
}DM_1R_CCA_E;

typedef enum tag_RF_Type_Definition
{
	RF_Save =0,
	RF_Normal = 1,
	RF_MAX = 2,
}DM_RF_E;


//
// Extern Global Variables.
//
//PowerTracking move to odm_powerTrakcing.h by YuChen
//
// check Sta pointer valid or not
//
#if (DM_ODM_SUPPORT_TYPE & (ODM_AP|ODM_ADSL))
#define IS_STA_VALID(pSta)		(pSta && pSta->expire_to)
#elif (DM_ODM_SUPPORT_TYPE & ODM_WIN)
#define IS_STA_VALID(pSta)		(pSta && pSta->bUsed)
#else
#define IS_STA_VALID(pSta)		(pSta)
#endif

//Remove DIG by yuchen

//Remove BB power saving by Yuchen

//remove PT by yuchen

//ODM_RAStateCheck() Remove by RS_James

#if(DM_ODM_SUPPORT_TYPE & (ODM_WIN|ODM_AP|ODM_ADSL))
//============================================================
// function prototype
//============================================================
//#define DM_ChangeDynamicInitGainThresh		ODM_ChangeDynamicInitGainThresh
//void	ODM_ChangeDynamicInitGainThresh(IN	PADAPTER	pAdapter,
//											IN	INT32		DM_Type,
//											IN	INT32		DM_Value);

//Remove DIG by yuchen


BOOLEAN
ODM_CheckPowerStatus(
	IN	PADAPTER		Adapter
	);


//Remove ODM_RateAdaptiveStateApInit() by RS_James

//Remove Edca by YuChen

#endif



u4Byte odm_ConvertTo_dB(u4Byte Value);

u4Byte odm_ConvertTo_linear(u4Byte Value);

#if((DM_ODM_SUPPORT_TYPE==ODM_WIN)||(DM_ODM_SUPPORT_TYPE==ODM_CE))

u4Byte
GetPSDData(
	PDM_ODM_T	pDM_Odm,
	unsigned int 	point,
	u1Byte initial_gain_psd);

#endif

#if (DM_ODM_SUPPORT_TYPE & ODM_WIN)	
VOID
ODM_DMWatchdog_LPS(
	IN		PDM_ODM_T		pDM_Odm
);
#endif


s4Byte
ODM_PWdB_Conversion(
    IN  s4Byte X,
    IN  u4Byte TotalBit,
    IN  u4Byte DecimalBit
    );

s4Byte
ODM_SignConversion(
    IN  s4Byte value,
    IN  u4Byte TotalBit
    );

VOID 
ODM_DMInit(
 IN	PDM_ODM_T	pDM_Odm
);

VOID
ODM_DMReset(
	IN	PDM_ODM_T	pDM_Odm
	);

VOID
phydm_support_ablity_debug(
	IN		PVOID		pDM_VOID,
	IN		u4Byte		*const dm_value,
	IN		u4Byte			*_used,
	OUT		char				*output,
	IN		u4Byte			*_out_len
	);

VOID
ODM_DMWatchdog(
	IN		PDM_ODM_T			pDM_Odm			// For common use in the future
	);

VOID
ODM_CmnInfoInit(
	IN		PDM_ODM_T		pDM_Odm,
	IN		ODM_CMNINFO_E	CmnInfo,
	IN		u4Byte			Value	
	);

VOID
ODM_CmnInfoHook(
	IN		PDM_ODM_T		pDM_Odm,
	IN		ODM_CMNINFO_E	CmnInfo,
	IN		PVOID			pValue	
	);

VOID
ODM_CmnInfoPtrArrayHook(
	IN		PDM_ODM_T		pDM_Odm,
	IN		ODM_CMNINFO_E	CmnInfo,
	IN		u2Byte			Index,
	IN		PVOID			pValue	
	);

VOID
ODM_CmnInfoUpdate(
	IN		PDM_ODM_T		pDM_Odm,
	IN		u4Byte			CmnInfo,
	IN		u8Byte			Value	
	);

#if(DM_ODM_SUPPORT_TYPE==ODM_AP)
VOID 
ODM_InitAllThreads(
    IN PDM_ODM_T	pDM_Odm 
    );

VOID
ODM_StopAllThreads(
	IN PDM_ODM_T	pDM_Odm 
	);
#endif

VOID 
ODM_InitAllTimers(
    IN PDM_ODM_T	pDM_Odm 
    );

VOID 
ODM_CancelAllTimers(
    IN PDM_ODM_T    pDM_Odm 
    );

VOID
ODM_ReleaseAllTimers(
    IN PDM_ODM_T	pDM_Odm 
    );




#if (DM_ODM_SUPPORT_TYPE == ODM_WIN)
VOID ODM_InitAllWorkItems(IN PDM_ODM_T	pDM_Odm );
VOID ODM_FreeAllWorkItems(IN PDM_ODM_T	pDM_Odm );



u8Byte
PlatformDivision64(
	IN u8Byte	x,
	IN u8Byte	y
);

//====================================================
//3 PathDiV End
//====================================================


#define DM_ChangeDynamicInitGainThresh		ODM_ChangeDynamicInitGainThresh
//void	ODM_ChangeDynamicInitGainThresh(IN	PADAPTER	pAdapter,
//											IN	INT32		DM_Type,
//											IN	INT32		DM_Value);
//
// PathDiveristy Remove by RS_James

typedef enum tag_DIG_Connect_Definition
{
	DIG_STA_DISCONNECT = 0,	
	DIG_STA_CONNECT = 1,
	DIG_STA_BEFORE_CONNECT = 2,
	DIG_MultiSTA_DISCONNECT = 3,
	DIG_MultiSTA_CONNECT = 4,
	DIG_CONNECT_MAX
}DM_DIG_CONNECT_E;


//
// 2012/01/12 MH Check afapter status. Temp fix BSOD.
//
#define	HAL_ADAPTER_STS_CHK(pDM_Odm)\
	if (pDM_Odm->Adapter == NULL)\
	{\
		return;\
	}\


//
// For new definition in MP temporarily fro power tracking,
//


#endif	// #if (DM_ODM_SUPPORT_TYPE == ODM_WIN)

VOID
ODM_AsocEntry_Init(
	IN		PDM_ODM_T		pDM_Odm
	);

//Remove ODM_DynamicARFBSelect() by RS_James

PVOID
PhyDM_Get_Structure(
	IN		PDM_ODM_T		pDM_Odm,
	IN		u1Byte			Structure_Type
);

#if (DM_ODM_SUPPORT_TYPE == ODM_WIN) ||(DM_ODM_SUPPORT_TYPE == ODM_CE)
/*===========================================================*/
/* The following is for compile only*/
/*===========================================================*/

#define	IS_HARDWARE_TYPE_8723A(_Adapter)			FALSE
#define IS_HARDWARE_TYPE_8723AE(_Adapter)			FALSE
#define	IS_HARDWARE_TYPE_8192C(_Adapter)			FALSE
#define	IS_HARDWARE_TYPE_8192D(_Adapter)			FALSE
#define	RF_T_METER_92D					0x42


#define SET_TX_DESC_ANTSEL_A_92C(__pTxDesc, __Value) SET_BITS_TO_LE_1BYTE(__pTxDesc+8+3, 0, 1, __Value)
#define SET_TX_DESC_TX_ANTL_92C(__pTxDesc, __Value) SET_BITS_TO_LE_1BYTE(__pTxDesc+8+3, 4, 2, __Value)
#define SET_TX_DESC_TX_ANT_HT_92C(__pTxDesc, __Value) SET_BITS_TO_LE_1BYTE(__pTxDesc+8+3, 6, 2, __Value)
#define SET_TX_DESC_TX_ANT_CCK_92C(__pTxDesc, __Value) SET_BITS_TO_LE_1BYTE(__pTxDesc+8+3, 2, 2, __Value)

#define GET_RX_STATUS_DESC_RX_MCS(__pRxStatusDesc)				LE_BITS_TO_1BYTE( __pRxStatusDesc+12, 0, 6)

#define		RX_HAL_IS_CCK_RATE_92C(pDesc)\
			(GET_RX_STATUS_DESC_RX_MCS(pDesc) == DESC_RATE1M ||\
			GET_RX_STATUS_DESC_RX_MCS(pDesc) == DESC_RATE2M ||\
			GET_RX_STATUS_DESC_RX_MCS(pDesc) == DESC_RATE5_5M ||\
			GET_RX_STATUS_DESC_RX_MCS(pDesc) == DESC_RATE11M)

#define		H2C_92C_PSD_RESULT				16

#define		rConfig_ram64x16				0xb2c

#define TARGET_CHNL_NUM_2G_5G	59
#if (DM_ODM_SUPPORT_TYPE == ODM_WIN)

VOID
FillH2CCmd92C(	
	IN	PADAPTER		Adapter,
	IN	u1Byte 	ElementID,
	IN	u4Byte 	CmdLen,
	IN	pu1Byte	pCmdBuffer
);
VOID
PHY_SetTxPowerLevel8192C(
	IN	PADAPTER		Adapter,
	IN	u1Byte			channel
	);
u1Byte GetRightChnlPlaceforIQK(u1Byte chnl);

#endif

//===========================================================
#endif //#if (DM_ODM_SUPPORT_TYPE == ODM_WIN)

#if (DM_ODM_SUPPORT_TYPE == ODM_CE)
void odm_dtc(PDM_ODM_T pDM_Odm);
#endif /* #if (DM_ODM_SUPPORT_TYPE == ODM_CE) */


VOID phydm_NoisyDetection(IN	PDM_ODM_T	pDM_Odm	);


#endif



<|start_filename|>general/SimpleMediaSource/MediaSource/SimpleMediaSourceActivate.cpp<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
#include "pch.h"

namespace winrt::WindowsSample::implementation
{
    // IMFActivate
    IFACEMETHODIMP SimpleMediaSourceActivate::ActivateObject(REFIID riid, void** ppv)
    {
        RETURN_HR_IF_NULL(E_POINTER, ppv);
        *ppv = nullptr;

        wil::com_ptr_nothrow<IMFAttributes> spAttributes;
        RETURN_IF_FAILED(MFCreateAttributes(&spAttributes, 1));

        DEBUG_MSG(L"Activate SimpleMediaSource");
        m_spSimpleMediaSrc = winrt::make_self<winrt::WindowsSample::implementation::SimpleMediaSource>();
        RETURN_IF_FAILED(m_spSimpleMediaSrc->Initialize());
        RETURN_IF_FAILED(m_spSimpleMediaSrc->QueryInterface(riid, ppv));

        return S_OK;
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::ShutdownObject()
    {
        return S_OK;
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::DetachObject()
    {
        m_spSimpleMediaSrc = nullptr;
        return S_OK;
    }

    // IMFAttributes (inherits by IMFActivate)
    IFACEMETHODIMP SimpleMediaSourceActivate::GetItem(_In_ REFGUID guidKey, _Inout_opt_  PROPVARIANT* pValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetItem(guidKey, pValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetItemType(_In_ REFGUID guidKey, _Out_ MF_ATTRIBUTE_TYPE* pType)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetItemType(guidKey, pType);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::CompareItem(_In_ REFGUID guidKey, _In_ REFPROPVARIANT Value, _Out_ BOOL* pbResult)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->CompareItem(guidKey, Value, pbResult);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::Compare(_In_ IMFAttributes* pTheirs, _In_ MF_ATTRIBUTES_MATCH_TYPE MatchType, _Out_ BOOL* pbResult)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->Compare(pTheirs, MatchType, pbResult);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetUINT32(_In_ REFGUID guidKey, _Out_ UINT32* punValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetUINT32(guidKey, punValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetUINT64(_In_ REFGUID guidKey, _Out_ UINT64* punValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetUINT64(guidKey, punValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetDouble(_In_ REFGUID guidKey, _Out_ double* pfValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetDouble(guidKey, pfValue);
    }
    IFACEMETHODIMP SimpleMediaSourceActivate::GetGUID(_In_ REFGUID guidKey, _Out_ GUID* pguidValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetGUID(guidKey, pguidValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetStringLength(_In_ REFGUID guidKey, _Out_ UINT32* pcchLength)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetStringLength(guidKey, pcchLength);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetString(_In_ REFGUID guidKey, _Out_writes_(cchBufSize) LPWSTR pwszValue, _In_ UINT32 cchBufSize, _Inout_opt_ UINT32* pcchLength)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetAllocatedString(_In_ REFGUID guidKey, _Out_writes_(*pcchLength + 1) LPWSTR* ppwszValue, _Inout_  UINT32* pcchLength)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetBlobSize(_In_ REFGUID guidKey, _Out_ UINT32* pcbBlobSize)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetBlobSize(guidKey, pcbBlobSize);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetBlob(_In_ REFGUID  guidKey, _Out_writes_(cbBufSize) UINT8* pBuf, UINT32 cbBufSize, _Inout_  UINT32* pcbBlobSize)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetAllocatedBlob(__RPC__in REFGUID guidKey, __RPC__deref_out_ecount_full_opt(*pcbSize) UINT8** ppBuf, __RPC__out UINT32* pcbSize)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetUnknown(__RPC__in REFGUID guidKey, __RPC__in REFIID riid, __RPC__deref_out_opt LPVOID* ppv)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetUnknown(guidKey, riid, ppv);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetItem(_In_ REFGUID guidKey, _In_ REFPROPVARIANT Value)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetItem(guidKey, Value);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::DeleteItem(_In_ REFGUID guidKey)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->DeleteItem(guidKey);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::DeleteAllItems()
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->DeleteAllItems();
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetUINT32(_In_ REFGUID guidKey, _In_ UINT32  unValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetUINT32(guidKey, unValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetUINT64(_In_ REFGUID guidKey, _In_ UINT64  unValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetUINT64(guidKey, unValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetDouble(_In_ REFGUID guidKey, _In_ double  fValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetDouble(guidKey, fValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetGUID(_In_ REFGUID guidKey, _In_ REFGUID guidValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetGUID(guidKey, guidValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetString(_In_ REFGUID guidKey, _In_ LPCWSTR wszValue)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetString(guidKey, wszValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetBlob(_In_ REFGUID guidKey, _In_reads_(cbBufSize) const UINT8* pBuf, UINT32 cbBufSize)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetBlob(guidKey, pBuf, cbBufSize);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::SetUnknown(_In_ REFGUID guidKey, _In_ IUnknown* pUnknown) 
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->SetUnknown(guidKey, pUnknown);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::LockStore() 
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->LockStore();
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::UnlockStore() 
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->UnlockStore();
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetCount(_Out_ UINT32* pcItems) 
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetCount(pcItems);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::GetItemByIndex(UINT32 unIndex, _Out_ GUID* pguidKey, _Inout_ PROPVARIANT* pValue) 
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->GetItemByIndex(unIndex, pguidKey, pValue);
    }

    IFACEMETHODIMP SimpleMediaSourceActivate::CopyAllItems(_In_ IMFAttributes* pDest)
    {
        if (!m_spActivateAttributes)
            RETURN_IF_FAILED(MFCreateAttributes(&m_spActivateAttributes, 1));
        return m_spActivateAttributes->CopyAllItems(pDest);
    }
}


<|start_filename|>audio/simpleaudiosample/Source/Filters/micarraywavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    micarraywavtable.h

Abstract:-

    Declaration of wave miniport tables for the mic array.

--*/

#ifndef _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_
#define _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_

//
// Mic array range.
//
#define MICARRAY_RAW_CHANNELS                   2       // Channels for raw mode
#define MICARRAY_DEVICE_MAX_CHANNELS            2       // Max channels overall
#define MICARRAY_32_BITS_PER_SAMPLE_PCM         32      // 32 Bits Per Sample
#define MICARRAY_RAW_SAMPLE_RATE                48000   // Raw sample rate

//
// Max # of pin instances.
//
#define MICARRAY_MAX_INPUT_STREAMS              1

//=============================================================================
static
KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArrayPinSupportedDeviceFormats[] =
{
    // 48 KHz 32-bit 2 channels
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                384000,
                8,
                32,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            32,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

//
// Supported modes (only on streaming pins).
//
static
MODE_AND_DEFAULT_FORMAT MicArrayPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        &MicArrayPinSupportedDeviceFormats[0].DataFormat
    }
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
//
static
PIN_DEVICE_FORMATS_AND_MODES MicArrayPinDeviceFormatsAndModes[] =
{
    {
        BridgePin,
        NULL,
        0,
        NULL,
        0
    },
    {
        SystemCapturePin,
        MicArrayPinSupportedDeviceFormats,
        SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats),
        MicArrayPinSupportedDeviceModes,
        SIZEOF_ARRAY(MicArrayPinSupportedDeviceModes)
    }
};

//=============================================================================
// Data ranges
//
// See CMiniportWaveRT::DataRangeIntersection.
//
static
KSDATARANGE_AUDIO MicArrayPinDataRangesRawStream[] =
{
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY_RAW_CHANNELS,
        MICARRAY_32_BITS_PER_SAMPLE_PCM,
        MICARRAY_32_BITS_PER_SAMPLE_PCM,
        MICARRAY_RAW_SAMPLE_RATE,
        MICARRAY_RAW_SAMPLE_RATE
    },
};

static
PKSDATARANGE MicArrayPinDataRangePointersStream[] =
{
    // All supported device formats should be listed in the DataRange.
    PKSDATARANGE(&MicArrayPinDataRangesRawStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
};

//=============================================================================
static
KSDATARANGE MicArrayPinDataRangesBridge[] =
{
    {
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE MicArrayPinDataRangePointersBridge[] =
{
    &MicArrayPinDataRangesBridge[0]
};

//=============================================================================
static
PCPIN_DESCRIPTOR MicArrayWaveMiniportPins[] =
{
    // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicArrayPinDataRangePointersBridge),
            MicArrayPinDataRangePointersBridge,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST
    {
        MICARRAY_MAX_INPUT_STREAMS,
        MICARRAY_MAX_INPUT_STREAMS,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicArrayPinDataRangePointersStream),
            MicArrayPinDataRangePointersStream,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            &KSAUDFNAME_RECORDING_CONTROL,
            0
        }
    }
};

//=============================================================================
static
PCNODE_DESCRIPTOR MicArrayWaveMiniportNodes[] =
{
    // KSNODE_WAVE_ADC
    {
        0,                      // Flags
        NULL,                   // AutomationTable
        &KSNODETYPE_ADC,        // Type
        NULL                    // Name
    }
};

//=============================================================================
static
PCCONNECTION_DESCRIPTOR MicArrayWaveMiniportConnections[] =
{
    { PCFILTER_NODE,        KSPIN_WAVE_BRIDGE,      KSNODE_WAVE_ADC,     1 },
    { KSNODE_WAVE_ADC,      0,                      PCFILTER_NODE,       KSPIN_WAVEIN_HOST },
};

//=============================================================================

static
SIMPLEAUDIOSAMPLE_PROPERTY_ITEM PropertiesMicArrayWaveFilter[] =
{
    {
        {
            &KSPROPSETID_General,
            KSPROPERTY_GENERAL_COMPONENTID,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_Pin,
            KSPROPERTY_PIN_PROPOSEDATAFORMAT,
            KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_Pin,
            KSPROPERTY_PIN_PROPOSEDATAFORMAT2,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArrayWaveFilter, PropertiesMicArrayWaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR MicArrayWaveMiniportFilterDescriptor =
{
    0,                                              // Version
    &AutomationMicArrayWaveFilter,                  // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                       // PinSize
    SIZEOF_ARRAY(MicArrayWaveMiniportPins),         // PinCount
    MicArrayWaveMiniportPins,                       // Pins
    sizeof(PCNODE_DESCRIPTOR),                      // NodeSize
    SIZEOF_ARRAY(MicArrayWaveMiniportNodes),        // NodeCount
    MicArrayWaveMiniportNodes,                      // Nodes
    SIZEOF_ARRAY(MicArrayWaveMiniportConnections),  // ConnectionCount
    MicArrayWaveMiniportConnections,                // Connections
    0,                                              // CategoryCount
    NULL                                            // Categories  - use defaults (audio, render, capture)
};

#endif // _SIMPLEAUDIOSAMPLE_MICARRAYWAVTABLE_H_


<|start_filename|>gnss/gnssUmdf/Driver.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    driver.h

Abstract:

    This file contains the driver definitions.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

EVT_WDF_DRIVER_UNLOAD OnDriverUnload;
EVT_WDF_OBJECT_CONTEXT_CLEANUP GnssUmdfEvtDriverContextCleanup;


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/DeviceControl.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    DeviceControl.cpp

Abstract:

    Implements the functions to control the OSR USB FX2 device.

Environment:

    User mode

--*/

#include "stdafx.h"

//
// Keep track of where the OSRFX2 device's bar graph currently is.
//
INT             CurrentBar;
BAR_GRAPH_STATE BarGraphState;

/*++

Routine Description:

    Initialize global variables for OSRFX2 device.

Arguments:

    VOID

Return Value:

    VOID

--*/
VOID
OsrFx2InitializeDevice(
    VOID
    )
{
    CurrentBar = 0;
}


/*++

Routine Description:

    Turns off all of the bar graph lights on the OSR USB FX2 device.

Arguments:

    Context - The callback context

Return Value:

    VOID

--*/
DWORD
OsrFx2ClearAllBars(
    _In_ PDEVICE_CONTEXT Context
    )
{
    DWORD Err = ERROR_SUCCESS;
    ULONG BytesReturned;

    BarGraphState.BarsAsUChar = 0;

    if (!DeviceIoControl(Context->DeviceHandle,
                         IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY,
                         &BarGraphState,          // Pointer to InBuffer
                         sizeof(BAR_GRAPH_STATE), // Length of InBuffer
                         NULL,                    // Pointer to OutBuffer
                         0,                       // Length of OutBuffer
                         &BytesReturned,          // BytesReturned
                         0))                      // Pointer to Overlapped structure
    {
        Err = GetLastError();
        goto cleanup;
    }

cleanup:

    return Err;
}


/*++

Routine Description:

    Lights the next bar on the OSR USB FX2 device.

Arguments:

    Context - The callback context

Return Value:

    VOID

--*/
DWORD
OsrFx2LightNextBar(
    _In_ PDEVICE_CONTEXT Context
    )
{
    DWORD Err = ERROR_SUCCESS;
    ULONG BytesReturned;

    //
    // Normalize to 0-7
    //
    CurrentBar += 1;

    if (CurrentBar > 7)
    {
        CurrentBar = 0;
    }

    BarGraphState.BarsAsUChar = 1 << (UCHAR)CurrentBar;

    if (!DeviceIoControl(Context->DeviceHandle,
                         IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY,
                         &BarGraphState,          // Pointer to InBuffer
                         sizeof(BAR_GRAPH_STATE), // Length of InBuffer
                         NULL,                    // Pointer to OutBuffer
                         0,                       // Length of OutBuffer
                         &BytesReturned,          // BytesReturned
                         0))                      // Pointer to Overlapped structure
    {
        Err = GetLastError();
        goto cleanup;
    }

cleanup:

    return Err;
}

/*++

Routine Description:

    Lights the next bar on the OSRFX2 device.

Arguments:

    Context - The device context

Return Value:

    A Win32 error code.

--*/
DWORD
OsrFx2ControlDevice(
    _In_ PDEVICE_CONTEXT Context
    )
{
    DWORD Err = ERROR_SUCCESS;

    Err = OsrFx2ClearAllBars(Context);

    if (Err != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    Err = OsrFx2LightNextBar(Context);

    if (Err != ERROR_SUCCESS)
    {
        goto cleanup;
    }

cleanup:

    return Err;
}


<|start_filename|>filesys/fastfat/close.c<|end_filename|>
/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    Close.c

Abstract:

    This module implements the File Close routine for Fat called by the
    dispatch driver.


--*/

#include "FatProcs.h"

//
//  The Bug check file id for this module
//

#define BugCheckFileId                   (FAT_BUG_CHECK_CLOSE)

//
//  The local debug trace level
//

#define Dbg                              (DEBUG_TRACE_CLOSE)

ULONG FatMaxDelayedCloseCount;


#define FatAcquireCloseMutex() {                        \
    NT_ASSERT(KeAreApcsDisabled());                     \
    ExAcquireFastMutexUnsafe( &FatCloseQueueMutex );    \
}

#define FatReleaseCloseMutex() {                        \
    NT_ASSERT(KeAreApcsDisabled());                     \
    ExReleaseFastMutexUnsafe( &FatCloseQueueMutex );    \
}

//
//  Local procedure prototypes
//

_Requires_lock_held_(_Global_critical_region_)
VOID
FatQueueClose (
    IN PCLOSE_CONTEXT CloseContext,
    IN BOOLEAN DelayClose
    );

_Requires_lock_held_(_Global_critical_region_)
PCLOSE_CONTEXT
FatRemoveClose (
    PVCB Vcb OPTIONAL,
    PVCB LastVcbHint OPTIONAL
    );

IO_WORKITEM_ROUTINE FatCloseWorker;

VOID
FatCloseWorker (
    _In_        PDEVICE_OBJECT DeviceObject,
    _In_opt_    PVOID Context
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatFsdClose)
#pragma alloc_text(PAGE, FatFspClose)
#pragma alloc_text(PAGE, FatRemoveClose)
#pragma alloc_text(PAGE, FatCommonClose)
#pragma alloc_text(PAGE, FatCloseWorker)
#endif


_Function_class_(IRP_MJ_CLOSE)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdClose (
    _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of Close.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the
        file exists

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The FSD status for the IRP

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PIO_STACK_LOCATION IrpSp;
    PFILE_OBJECT FileObject;

    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;
    TYPE_OF_OPEN TypeOfOpen;

    BOOLEAN TopLevel;
    BOOLEAN VcbDeleted = FALSE;

    PAGED_CODE();

    //
    //  If we were called with our file system device object instead of a
    //  volume device object, just complete this request with STATUS_SUCCESS
    //

    if (FatDeviceIsFatFsdo( VolumeDeviceObject))  {

        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = FILE_OPENED;

        IoCompleteRequest( Irp, IO_DISK_INCREMENT );

        return STATUS_SUCCESS;
    }

    DebugTrace(+1, Dbg, "FatFsdClose\n", 0);

    //
    //  Call the common Close routine
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    //
    //  Get a pointer to the current stack location and the file object
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    FileObject = IrpSp->FileObject;

    //
    //  Decode the file object and set the read-only bit in the Ccb.
    //

    TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );

    if (Ccb && IsFileObjectReadOnly(FileObject)) {

        SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY );
    }

    try {

        PCLOSE_CONTEXT CloseContext = NULL;

        //
        //  If we are top level, WAIT can be TRUE, otherwise make it FALSE
        //  to avoid deadlocks, unless this is a top
        //  level request not originating from the system process.
        //

        BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess);

#if (NTDDI_VERSION >= NTDDI_WIN8)

        //
        //  To catch the odd case where a close comes in without a preceding cleanup,
        //  call the oplock package to get rid of any oplock state.  This can only
        //  be safely done in the FSD path.
        //
        
        if ((Fcb != NULL) &&
            !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) &&
            FatIsFileOplockable( Fcb )) {
        
            //
            //  This is equivalent to handling cleanup, and it always cleans up any
            //  oplock immediately.  Also, we don't need any locking of the FCB here;
            //  the oplock's own lock will be sufficient for this purpose.
            //
        
            FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
                                Irp,
                                0,
                                NULL,
                                NULL,
                                NULL );
        }
#endif

        //
        //  Metadata streams have had close contexts preallocated. Pull one out now, while we're 
        //  guaranteed the VCB exists.
        //

        if ( (TypeOfOpen == VirtualVolumeFile) || (TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile)
            ) {

            CloseContext = FatAllocateCloseContext( Vcb );
            NT_ASSERT( CloseContext != NULL );
            CloseContext->Free = TRUE;

        }

        //
        //  Call the common Close routine if we are not delaying this close.
        //

        if ((((TypeOfOpen == UserFileOpen) || 
                (TypeOfOpen == UserDirectoryOpen)) &&
                 FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) &&
                 !FatData.ShutdownStarted) ||
                (FatCommonClose( Vcb, Fcb, Ccb, TypeOfOpen, Wait, TopLevel, &VcbDeleted ) == STATUS_PENDING)) {

            //
            //  Ship it off to the delayed close queue if we tried to close, and got STATUS_PENDING, or
            //  if the user open told us to delay the close.
            //

            //
            //  Metadata streams have had close contexts preallocated. If we have a user open,
            //  pull the close context out of the Ccb.
            //

            if( CloseContext == NULL ) {
            
                //
                //  Free up any query template strings before using the close context fields,
                //  which overlap (union)
                //

                FatDeallocateCcbStrings( Ccb );

                CloseContext = &Ccb->CloseContext;
                CloseContext->Free = FALSE;
                
                SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT );
            }

            //
            //  If the status is pending, then let's get the information we
            //  need into the close context we already have bagged, complete
            //  the request, and post it.  It is important we allocate nothing
            //  in the close path.
            //

            CloseContext->Vcb = Vcb;
            CloseContext->Fcb = Fcb;
            CloseContext->TypeOfOpen = TypeOfOpen;

            //
            //  Send it off, either to an ExWorkerThread or to the async
            //  close list.
            //

            FatQueueClose( CloseContext,
                           (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE)));

        } else {
            
            //
            //  The close proceeded synchronously, so for the metadata objects we
            //  can now drop the close context we preallocated.
            //
            
            if ((TypeOfOpen == VirtualVolumeFile) ||
                (TypeOfOpen == DirectoryFile) ||
                (TypeOfOpen == EaFile)
                ) {

                if (CloseContext != NULL) {

                    ExFreePool( CloseContext );
                    
                }
            }
        }

        FatCompleteRequest( FatNull, Irp, Status );

    } 
    except(FatExceptionFilter( NULL, GetExceptionInformation() )) {

        //
        //  We had some trouble trying to perform the requested
        //  operation, so we'll abort the I/O request with the 
        //  error status that we get back from the exception code.
        //

        Status = FatProcessException( NULL, Irp, GetExceptionCode() );
    }

    if (TopLevel) { IoSetTopLevelIrp( NULL ); }

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}

VOID
FatCloseWorker (
    _In_        PDEVICE_OBJECT DeviceObject,
    _In_opt_    PVOID Context
    )
/*++

Routine Description:

    This routine is a shim between the IO worker package and FatFspClose.

Arguments:

    DeviceObject - Registration device object, unused
    Context - Context value, unused

Return Value:

    None.

--*/
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER( DeviceObject );
    
    FsRtlEnterFileSystem();
    
    FatFspClose (Context);
    
    FsRtlExitFileSystem();
}


_Requires_lock_held_(_Global_critical_region_)
VOID
FatFspClose (
    IN PVCB Vcb OPTIONAL
    )

/*++

Routine Description:

    This routine implements the FSP part of Close.

Arguments:

    Vcb - If present, tells us to only close file objects opened on the
        specified volume.

Return Value:

    None.

--*/

{
    PCLOSE_CONTEXT CloseContext;
    PVCB CurrentVcb = NULL;
    PVCB LastVcb = NULL;
    BOOLEAN FreeContext = FALSE;
    BOOLEAN TopLevel = FALSE;

    ULONG LoopsWithVcbHeld = 0;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "FatFspClose\n", 0);

    //
    //  Set the top level IRP for the true FSP operation.
    //
    
    if (!ARGUMENT_PRESENT( Vcb )) {
        
        IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
        TopLevel = TRUE;
    }

    while ((CloseContext = FatRemoveClose(Vcb, LastVcb)) != NULL) {

        //
        //  If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of
        //  creates by doing several closes with one acquisition of the Vcb.
        //
        //  Note that we cannot be holding the Vcb on entry to FatCommonClose
        //  if this is last close as we will try to acquire FatData, and
        //  worse the volume (and therefore the Vcb) may go away.
        //

        if (!ARGUMENT_PRESENT(Vcb)) {
             
            if (!FatData.ShutdownStarted) {

                if (CloseContext->Vcb != CurrentVcb) {

                    LoopsWithVcbHeld = 0;

                    //
                    //  Release a previously held Vcb, if any.
                    //

                    if (CurrentVcb != NULL) {

                        ExReleaseResourceLite( &CurrentVcb->Resource);
                    }

                    //
                    //  Get the new Vcb.
                    //

                    CurrentVcb = CloseContext->Vcb;
                    (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );

                } else {

                    //
                    //  Share the resource occasionally if we seem to be finding a lot
                    //  of closes for a single volume.
                    //

                    if (++LoopsWithVcbHeld >= 20) {

                        if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) +
                            ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) {

                            ExReleaseResourceLite( &CurrentVcb->Resource);
                            (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
                        }

                        LoopsWithVcbHeld = 0;
                    }
                }

                //
                //  Now check the Open count.  We may be about to delete this volume!
                //
                //  The test below must be <= 1 because there could still be outstanding
                //  stream references on this VCB that are not counted in the OpenFileCount.
                //  For example if there are no open files OpenFileCount could be zero and we would
                //  not release the resource here.  The call to FatCommonClose() below may cause
                //  the VCB to be torn down and we will try to release memory we don't
                //  own later.
                //

                if (CurrentVcb->OpenFileCount <= 1) {
                    ExReleaseResourceLite( &CurrentVcb->Resource);
                    CurrentVcb = NULL;
                }
            //
            //  If shutdown has started while processing our list, drop the
            //  current Vcb resource.
            //

            } else if (CurrentVcb != NULL) {

                ExReleaseResourceLite( &CurrentVcb->Resource);
                CurrentVcb = NULL;
            }
        }

        LastVcb = CurrentVcb;

        //
        //  Call the common Close routine.  Protected in a try {} except {}
        //

        try {

            //
            //  The close context either is in the CCB, automatically freed,
            //  or was from pool for a metadata fileobject, CCB is NULL, and
            //  we'll need to free it.
            //

            FreeContext = CloseContext->Free;

            (VOID)FatCommonClose( CloseContext->Vcb,
                                  CloseContext->Fcb,
                                  (FreeContext ? NULL :
                                                 CONTAINING_RECORD( CloseContext, CCB, CloseContext)),
                                  CloseContext->TypeOfOpen,
                                  TRUE,
                                  TopLevel,
                                  NULL );

        } except(FatExceptionFilter( NULL, GetExceptionInformation() )) {

            //
            //  Ignore anything we expect.
            //

            NOTHING;
        }

        //
        //  Drop the context if it came from pool.
        //
        
        if (FreeContext) {

            ExFreePool( CloseContext );
        }
    }

    //
    //  Release a previously held Vcb, if any.
    //

    if (CurrentVcb != NULL) {

        ExReleaseResourceLite( &CurrentVcb->Resource);
    }

    //
    //  Clean up the top level IRP hint if we owned it.
    //
    
    if (!ARGUMENT_PRESENT( Vcb )) {
        
        IoSetTopLevelIrp( NULL );
    }
    
    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0);
}


_Requires_lock_held_(_Global_critical_region_)    
VOID
FatQueueClose (
    IN PCLOSE_CONTEXT CloseContext,
    IN BOOLEAN DelayClose
    )

/*++

Routine Description:

    Enqueue a deferred close to one of the two delayed close queues.

Arguments:

    CloseContext - a close context to enqueue for the delayed close thread.
    
    DelayClose - whether this should go on the delayed close queue (unreferenced
        objects).

Return Value:

    None.

--*/

{
    BOOLEAN StartWorker = FALSE;

    FatAcquireCloseMutex();

    if (DelayClose) {

        InsertTailList( &FatData.DelayedCloseList,
                        &CloseContext->GlobalLinks );
        InsertTailList( &CloseContext->Vcb->DelayedCloseList,
                        &CloseContext->VcbLinks );

        FatData.DelayedCloseCount += 1;

        if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) &&
            !FatData.AsyncCloseActive) {

            FatData.AsyncCloseActive = TRUE;
            StartWorker = TRUE;
        }

    } else {

        InsertTailList( &FatData.AsyncCloseList,
                        &CloseContext->GlobalLinks );
        InsertTailList( &CloseContext->Vcb->AsyncCloseList,
                        &CloseContext->VcbLinks );

        FatData.AsyncCloseCount += 1;

        if (!FatData.AsyncCloseActive) {

            FatData.AsyncCloseActive = TRUE;
            StartWorker = TRUE;
        }
    }

    FatReleaseCloseMutex();

    if (StartWorker) {

        IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL );
    }
}


_Requires_lock_held_(_Global_critical_region_)    
PCLOSE_CONTEXT
FatRemoveClose (
    PVCB Vcb OPTIONAL,
    PVCB LastVcbHint OPTIONAL
    )

/*++

Routine Description:

    Dequeue a deferred close from one of the two delayed close queues.

Arguments:

    Vcb - if specified, only returns close for this volume.
    
    LastVcbHint - if specified and other starvation avoidance is required by
        the system condition, will attempt to return closes for this volume.

Return Value:

    A close to perform.

--*/

{
    PLIST_ENTRY Entry;
    PCLOSE_CONTEXT CloseContext;
    BOOLEAN WorkerThread;

    PAGED_CODE();

    FatAcquireCloseMutex();

    //
    //  Remember if this is the worker thread, so we can pull down the active
    //  flag should we run everything out.
    //
    
    WorkerThread = (Vcb == NULL);

    //
    //  If the queues are above the limits by a significant amount, we have
    //  to try hard to pull them down.  To do this, we will aggressively try
    //  to find closes for the last volume the caller looked at.  This will
    //  make sure we fully utilize the acquisition of the volume, which can
    //  be a hugely expensive resource to get (create/close/cleanup use it
    //  exclusively).
    //
    //  Only do this in the delayed close thread.  We will know this is the
    //  case by seeing a NULL mandatory Vcb.
    //

    if (Vcb == NULL && LastVcbHint != NULL) {

        //
        //  Flip over to aggressive at twice the legal limit, and flip it
        //  off at the legal limit.
        //
        
        if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) {

            FatData.HighAsync = TRUE;
        
        } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) {

            FatData.HighAsync = FALSE;
        }
            
        if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) {

            FatData.HighDelayed = TRUE;
        
        } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) {

            FatData.HighDelayed = FALSE;
        }

        if (FatData.HighAsync || FatData.HighDelayed) {

            Vcb = LastVcbHint;
        }
    }
        
    //
    //  Do the case when we don't care about which Vcb the close is on.
    //  This is the case when we are in an ExWorkerThread and aren't
    //  under pressure.
    //

    if (Vcb == NULL) {

        AnyClose:

        //
        //  First check the list of async closes.
        //

        if (!IsListEmpty( &FatData.AsyncCloseList )) {

            Entry = RemoveHeadList( &FatData.AsyncCloseList );
            FatData.AsyncCloseCount -= 1;

            CloseContext = CONTAINING_RECORD( Entry,
                                              CLOSE_CONTEXT,
                                              GlobalLinks );

            RemoveEntryList( &CloseContext->VcbLinks );

        //
        //  Do any delayed closes over half the limit, unless shutdown has
        //  started (then kill them all).
        //

        } else if (!IsListEmpty( &FatData.DelayedCloseList ) &&
                   (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 ||
                    FatData.ShutdownStarted)) {

            Entry = RemoveHeadList( &FatData.DelayedCloseList );
            FatData.DelayedCloseCount -= 1;

            CloseContext = CONTAINING_RECORD( Entry,
                                              CLOSE_CONTEXT,
                                              GlobalLinks );

            RemoveEntryList( &CloseContext->VcbLinks );

        //
        //  There are no more closes to perform; show that we are done.
        //

        } else {

            CloseContext = NULL;

            if (WorkerThread) {
                
                FatData.AsyncCloseActive = FALSE;
            }
        }

    //
    //  We're running down a specific volume.
    //
    
    } else {


        //
        //  First check the list of async closes.
        //

        if (!IsListEmpty( &Vcb->AsyncCloseList )) {

            Entry = RemoveHeadList( &Vcb->AsyncCloseList );
            FatData.AsyncCloseCount -= 1;

            CloseContext = CONTAINING_RECORD( Entry,
                                              CLOSE_CONTEXT,
                                              VcbLinks );

            RemoveEntryList( &CloseContext->GlobalLinks );

        //
        //  Do any delayed closes.
        //

        } else if (!IsListEmpty( &Vcb->DelayedCloseList )) {

            Entry = RemoveHeadList( &Vcb->DelayedCloseList );
            FatData.DelayedCloseCount -= 1;

            CloseContext = CONTAINING_RECORD( Entry,
                                              CLOSE_CONTEXT,
                                              VcbLinks );
        
            RemoveEntryList( &CloseContext->GlobalLinks );
        
        //
        //  If we were trying to run down the queues but didn't find anything for this
        //  volume, flip over to accept anything and try again.
        //

        } else if (LastVcbHint) {

            goto AnyClose;
        
        //
        //  There are no more closes to perform; show that we are done.
        //

        } else {

            CloseContext = NULL;
        }
    }

    FatReleaseCloseMutex();

    return CloseContext;
}


_Requires_lock_held_(_Global_critical_region_)    
NTSTATUS
FatCommonClose (
    IN PVCB Vcb,
    IN PFCB Fcb,
    IN PCCB Ccb,
    IN TYPE_OF_OPEN TypeOfOpen,
    IN BOOLEAN Wait,
    IN BOOLEAN TopLevel,
    OUT PBOOLEAN VcbDeleted OPTIONAL
    )

/*++

Routine Description:

    This is the common routine for closing a file/directory called by both
    the fsd and fsp threads.

    Close is invoked whenever the last reference to a file object is deleted.
    Cleanup is invoked when the last handle to a file object is closed, and
    is called before close.

    The function of close is to completely tear down and remove the fcb/dcb/ccb
    structures associated with the file object.

Arguments:

    Fcb - Supplies the file to process.

    Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE
        then we must try to acquire the Vcb anyway.

    TopLevel - If this is TRUE this is a top level request.

    VcbDeleted - Returns whether the VCB was deleted by this call.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PDCB ParentDcb;
    BOOLEAN RecursiveClose;
    BOOLEAN LocalVcbDeleted;
    IRP_CONTEXT IrpContext;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);

    //
    //  Initialize the callers variable, if needed.
    //

    LocalVcbDeleted = FALSE;

    if (ARGUMENT_PRESENT( VcbDeleted )) {

        *VcbDeleted = LocalVcbDeleted;
    }

    //
    //  Special case the unopened file object
    //

    if (TypeOfOpen == UnopenedFileObject) {

        DebugTrace(0, Dbg, "Close unopened file object\n", 0);

        Status = STATUS_SUCCESS;

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
        return Status;
    }

    //
    //  Set up our stack IrpContext.
    //

    RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );

    IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT;
    IrpContext.NodeByteSize = sizeof( IrpContext );
    IrpContext.MajorFunction = IRP_MJ_CLOSE;
    IrpContext.Vcb = Vcb;
    
    if (Wait) {

        SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
    }

    //
    //  Acquire exclusive access to the Vcb and enqueue the irp if we didn't
    //  get access.
    //

#pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" )
    if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {

        return STATUS_PENDING;
    }

    //
    //  The following test makes sure that we don't blow away an Fcb if we
    //  are trying to do a Supersede/Overwrite open above us.  This test
    //  does not apply for the EA file.
    //

    if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) &&
        Vcb->EaFcb != Fcb) {

        ExReleaseResourceLite( &Vcb->Resource );

        return STATUS_PENDING;
    }

    //
    //  Setting the following flag prevents recursive closes of directory file
    //  objects, which are handled in a special case loop.
    //

    if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {

        RecursiveClose = TRUE;

    } else {


        SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
        RecursiveClose = FALSE;

        //
        //  Since we are at the top of the close chain, we need to add
        //  a reference to the VCB.  This will keep it from going away
        //  on us until we are ready to check for a dismount below.
        //

        Vcb->OpenFileCount += 1;
    }

    try {

        //
        //  Case on the type of open that we are trying to close.
        //

        switch (TypeOfOpen) {

        case VirtualVolumeFile:

            DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
            InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case UserVolumeOpen:

            DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);

            Vcb->DirectAccessOpenCount -= 1;
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }

            FatDeleteCcb( &IrpContext, &Ccb );

            try_return( Status = STATUS_SUCCESS );
            break;

        case EaFile:

            DebugTrace(0, Dbg, "Close EaFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
            InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case DirectoryFile:

            DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);

            InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount );

            //
            //  Remove this internal open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );

            //
            //  If this is the root directory, it is a residual open
            //  as well.
            //

            if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) {

                InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );
            }

            //
            //  If this is a recursive close, just return here.
            //

            if ( RecursiveClose ) {

                try_return( Status = STATUS_SUCCESS );

            } else {

                break;
            }


        case UserDirectoryOpen:
        case UserFileOpen:

            DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);

            //
            //  Uninitialize the cache map if we no longer need to use it
            //

            if ((NodeType(Fcb) == FAT_NTC_DCB) &&
                IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) &&
                (Fcb->OpenCount == 1) &&
                (Fcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                //
                //  Dereference the directory file.  This may cause a close
                //  Irp to be processed, so we need to do this before we destroy
                //  the Fcb.
                //

                Fcb->Specific.Dcb.DirectoryFile = NULL;
                ObDereferenceObject( DirectoryFileObject );
            }

            Fcb->OpenCount -= 1;            
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
           
            FatDeleteCcb( &IrpContext, &Ccb );

            break;

        default:

#pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." )
            FatBugCheck( TypeOfOpen, 0, 0 );
        }

        //
        //  At this point we've cleaned up any on-disk structure that needs
        //  to be done, and we can now update the in-memory structures.
        //  Now if this is an unreferenced FCB or if it is
        //  an unreferenced DCB (not the root) then we can remove
        //  the fcb and set our ParentDcb to non null.
        //

        if (((NodeType(Fcb) == FAT_NTC_FCB) &&
             (Fcb->OpenCount == 0))

                ||

             ((NodeType(Fcb) == FAT_NTC_DCB) &&
              (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) &&
              (Fcb->OpenCount == 0) &&
              (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {

            ParentDcb = Fcb->ParentDcb;

            SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

            FatDeleteFcb( &IrpContext, &Fcb );

            //
            //  Uninitialize our parent's cache map if we no longer need
            //  to use it.
            //

            while ((NodeType(ParentDcb) == FAT_NTC_DCB) &&
                   IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) &&
                   (ParentDcb->OpenCount == 0) &&
                   (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject;

                DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                ParentDcb->Specific.Dcb.DirectoryFile = NULL;

                ObDereferenceObject( DirectoryFileObject );

                //
                //  Now, if the ObDereferenceObject() caused the final close
                //  to come in, then blow away the Fcb and continue up,
                //  otherwise wait for Mm to to dereference its file objects
                //  and stop here..
                //

                if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {

                    PDCB CurrentDcb;

                    CurrentDcb = ParentDcb;
                    ParentDcb = CurrentDcb->ParentDcb;

                    SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

                    FatDeleteFcb( &IrpContext, &CurrentDcb );

                } else {

                    break;
                }
            }
        }

        Status = STATUS_SUCCESS;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCommonClose );

        //
        //  We are done processing the close.  If we are the top of the close
        //  chain, see if the VCB can go away.  We have biased the open count by
        //  one, so we need to take that into account.
        //

        if (!RecursiveClose) {

            //
            //  See if there is only one open left.  If so, it is ours.  We only want
            //  to check for a dismount if a dismount is not already in progress.
            //  We also only do this if the Vcb condition is not VcbGood and the 
            //  caller can handle the VCB going away. This is determined by whether
            //  they passed in the VcbDeleted argument. This request also needs
            //  to be top level.
            //

            if (Vcb->OpenFileCount == 1 &&
                Vcb->VcbCondition != VcbGood &&
                !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) &&
                ARGUMENT_PRESENT( VcbDeleted ) &&
                TopLevel) {

                //
                //  We need the global lock, which must be acquired before the
                //  VCB.  Since we already have the VCB, we have to drop and
                //  reacquire here.  Note that we always want to wait from this
                //  point on.  Note that the VCB cannot go away, since we have
                //  biased the open file count.
                //

                FatReleaseVcb( &IrpContext,
                               Vcb );

                SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );

#pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" )
                FatAcquireExclusiveGlobal( &IrpContext );

                FatAcquireExclusiveVcb( &IrpContext,
                                        Vcb );

                //
                //  We have our locks in the correct order.  Remove our
                //  extra open and check for a dismount.  Note that if
                //  something changed while we dropped the lock, it will
                //  not matter, since the dismount code does the correct
                //  checks to make sure the volume can really go away.
                //

                Vcb->OpenFileCount -= 1;

                LocalVcbDeleted = FatCheckForDismount( &IrpContext,
                                                       Vcb,
                                                       FALSE );

                FatReleaseGlobal( &IrpContext );

                //
                //  Let the caller know what happened, if they want this information.
                //

                if (ARGUMENT_PRESENT( VcbDeleted )) {

                    *VcbDeleted = LocalVcbDeleted;
                }

            } else {

                //
                //  The volume cannot go away now.  Just remove our extra reference.
                //

                Vcb->OpenFileCount -= 1;
            }

            //
            //  If the VCB is still around, clear our recursion flag.
            //

            if (!LocalVcbDeleted) {

                ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS );
            }
        }

        //
        //  Only release the VCB if it did not go away.
        //

        if (!LocalVcbDeleted) {

            FatReleaseVcb( &IrpContext, Vcb );
        }

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
    }

    return Status;
}



<|start_filename|>tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    fail_driver1.c

Abstract:

    This is a sample driver that contains intentionally placed
    code defects in order to illustrate how Static Driver Verifier
    works. This driver is not functional and not intended as a 
    sample for real driver development projects.

Environment:

    Kernel mode

--*/

#include "fail_driver1.h"

#define _DRIVER_NAME_ "fail_driver1"

#ifndef __cplusplus
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, DriverAddDevice)
#pragma alloc_text (PAGE, DispatchCreate)
#pragma alloc_text (PAGE, DispatchRead)
#pragma alloc_text (PAGE, DispatchPower)
#pragma alloc_text (PAGE, DispatchSystemControl)
#pragma alloc_text (PAGE, DispatchPnp)
#pragma alloc_text (PAGE, DriverUnload)
#endif

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{

    UNREFERENCED_PARAMETER(RegistryPath);
    DriverObject->MajorFunction[IRP_MJ_CREATE]         = DispatchCreate;
    DriverObject->MajorFunction[IRP_MJ_READ]           = DispatchRead;
    DriverObject->MajorFunction[IRP_MJ_POWER]          = DispatchPower;
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchSystemControl;
    DriverObject->MajorFunction[IRP_MJ_PNP]            = DispatchPnp;
    DriverObject->DriverExtension->AddDevice           = DriverAddDevice;
    DriverObject->DriverUnload                         = DriverUnload;

    return STATUS_SUCCESS;
}

NTSTATUS
DriverAddDevice(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PDEVICE_OBJECT PhysicalDeviceObject
    )
{
    PDEVICE_OBJECT device;
	PDEVICE_OBJECT TopOfStack;
    PDRIVER_DEVICE_EXTENSION extension ;
    NTSTATUS status;
    
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(PhysicalDeviceObject);
    
    PAGED_CODE();
    
    // Injected CA defect for C28171
    PAGED_CODE();

    status = IoCreateDevice(DriverObject,                 
                            sizeof(DRIVER_DEVICE_EXTENSION), 
                            NULL,                   
                            FILE_DEVICE_DISK,  
                            0,                     
                            FALSE,                 
                            &device                
                            );
    if(status==STATUS_SUCCESS)
    {
  
       extension = (PDRIVER_DEVICE_EXTENSION)(device->DeviceExtension);

       TopOfStack = IoAttachDeviceToDeviceStack (
                                       device,
                                       PhysicalDeviceObject);
       if (NULL == TopOfStack) 
	   {
           IoDeleteDevice(device);
           return STATUS_DEVICE_REMOVED;
       }


       IoInitializeDpcRequest(device,DpcForIsrRoutine);

       device->Flags &= ~DO_DEVICE_INITIALIZING;


    }

   return status;
}

NTSTATUS
DispatchCreate (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
{   
    KAFFINITY ProcessorMask;
    PDRIVER_DEVICE_EXTENSION extension ;
    
    PVOID *badPointer = NULL;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);

    PAGED_CODE();
    

    ExFreePool(badPointer);


    extension = (PDRIVER_DEVICE_EXTENSION)DeviceObject -> DeviceExtension;

    ProcessorMask   =  (KAFFINITY)1;
    
    IoConnectInterrupt( &extension->InterruptObject,
                         InterruptServiceRoutine,
                         extension,
                         NULL,
                         extension->ControllerVector,
                         PASSIVE_LEVEL,
                         PASSIVE_LEVEL,
                         LevelSensitive,
                         TRUE,
                         ProcessorMask,
                         TRUE );
	
    return STATUS_SUCCESS;
}

NTSTATUS
DispatchRead (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
{  
    /*
       This defect is injected for the "SpinLock" rule.
    */
    KSPIN_LOCK  queueLock;
    KIRQL oldIrql;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);
    PAGED_CODE();

    KeInitializeSpinLock(&queueLock);
     

    KeAcquireSpinLock(&queueLock, &oldIrql);
	
    return STATUS_SUCCESS;
}

NTSTATUS
DispatchPower (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
{
    NTSTATUS status;
    PDRIVER_DEVICE_EXTENSION extension = (PDRIVER_DEVICE_EXTENSION)(DeviceObject->DeviceExtension); 
    PAGED_CODE();
    
    
    IoSetCompletionRoutine(Irp, CompletionRoutine, extension, TRUE, TRUE, TRUE);
    
    status = IoCallDriver(DeviceObject,Irp);
    return status;
}

NTSTATUS
DispatchSystemControl (
    _In_  PDEVICE_OBJECT  DeviceObject,
    _Inout_  PIRP            Irp
    )
{   
    /*
       This defect is injected for the "CancelSpinLock" rule.
    */
    KIRQL oldIrql;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);
    PAGED_CODE();
   
    IoAcquireCancelSpinLock(&oldIrql);
    return STATUS_SUCCESS;
}

NTSTATUS
DispatchPnp (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
{   
	
    /*
       This defect is injected for "LowerDriverReturn" rule.
    */
    NTSTATUS status = IoCallDriver(DeviceObject,Irp);
    PAGED_CODE();

    status = STATUS_SUCCESS;
    return status;
}

NTSTATUS
CompletionRoutine(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_reads_opt_(_Inexpressible_("varies")) PVOID EventIn
    )
{
    
    PKEVENT Event = (PKEVENT)EventIn;
    KIRQL oldIrql;
    PDRIVER_DEVICE_EXTENSION extension = (PDRIVER_DEVICE_EXTENSION)(DeviceObject->DeviceExtension); 
    UNREFERENCED_PARAMETER(Irp);
    _Analysis_assume_(EventIn != NULL);
    KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);

    /*
       This defect is injected for IrqlKeSetEvent rule
    */ 
    KeSetEvent(Event, extension->Increment, TRUE);
    return STATUS_SUCCESS;
}

BOOLEAN
InterruptServiceRoutine (
    _In_      PKINTERRUPT Interrupt,
    _In_opt_  PVOID DeviceExtensionIn
    )
{     
    PDRIVER_DEVICE_EXTENSION DeviceExtension = (PDRIVER_DEVICE_EXTENSION)DeviceExtensionIn;
    PVOID Context = NULL;        
    _Analysis_assume_(DeviceExtension != NULL);
    UNREFERENCED_PARAMETER(Interrupt); 

    IoRequestDpc(DeviceExtension->DeviceObject, DeviceExtension->Irp, Context);
    return TRUE;
}

VOID
DpcForIsrRoutine(    
    _In_ PKDPC  Dpc,    
    _In_ struct _DEVICE_OBJECT  *DeviceObject,    
    _Inout_ struct _IRP  *Irp,    
    _In_opt_ PVOID  Context)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);
    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(Dpc);
    /*
       This defect is injected for IrqlIoApcLte rule

    */
    IoGetInitialStack();
}

VOID
DriverUnload(
    _In_ PDRIVER_OBJECT DriverObject
    )
{
    UNREFERENCED_PARAMETER(DriverObject);
    PAGED_CODE();
     
    return;
}


<|start_filename|>audio/sysvad/common.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    Common.h

Abstract:
    
    CAdapterCommon class declaration.

--*/

#ifndef _SYSVAD_COMMON_H_
#define _SYSVAD_COMMON_H_

#define HNSTIME_PER_MILLISECOND 10000

//=============================================================================
// Macros
//=============================================================================

#define UNREFERENCED_VAR(status) \
    status = status 

//-------------------------------------------------------------------------
// Description:
//
// jump to the given label.
//
// Parameters:
//
//      label - [in] label to jump if condition is met
//
#define JUMP(label)                                             \
        goto label;  

//-------------------------------------------------------------------------
// Description:
//
// If the condition evaluates to TRUE, jump to the given label.
//
// Parameters:
//
//      condition - [in] Code that fits in if statement
//      label - [in] label to jump if condition is met
//
#define IF_TRUE_JUMP(condition, label)                          \
    if (condition)                                               \
    {                                                           \
        goto label;                                             \
    }

//-------------------------------------------------------------------------
// Description:
//
// If the condition evaluates to TRUE, perform the given statement
// then jump to the given label.
//
// Parameters:
//
//      condition - [in] Code that fits in if statement
//      action - [in] action to perform in body of if statement
//      label - [in] label to jump if condition is met
//
#define IF_TRUE_ACTION_JUMP(condition, action, label)           \
    if (condition)                                               \
    {                                                           \
        action;                                                 \
        goto label;                                             \
    }
    
//-------------------------------------------------------------------------
// Description:
//
// If the ntStatus is not NT_SUCCESS, perform the given statement then jump to
// the given label.
//
// Parameters:
//
//      ntStatus - [in] Value to check
//      action - [in] action to perform in body of if statement
//      label - [in] label to jump if condition is met
//
#define IF_FAILED_ACTION_JUMP(ntStatus, action, label)          \
        if (!NT_SUCCESS(ntStatus))                              \
        {                                                       \
            action;                                             \
            goto label;                                         \
        }
    
//-------------------------------------------------------------------------
// Description:
//
// If the ntStatus passed is not NT_SUCCESS, jump to the given label.
//
// Parameters:
//
//      ntStatus - [in] Value to check
//      label - [in] label to jump if condition is met
//
#define IF_FAILED_JUMP(ntStatus, label)                         \
    if (!NT_SUCCESS(ntStatus))                                   \
    {                                                           \
        goto label;                                             \
    }

#define SAFE_RELEASE(p) {if (p) { (p)->Release(); (p) = nullptr; } }
#define SAFE_DELETE_PTR_WITH_TAG(ptr, tag) if(ptr) { ExFreePoolWithTag((ptr), tag); (ptr) = NULL; }

// JACKDESC_RGB(r, g, b) 
#define JACKDESC_RGB(r, g, b) \
    ((COLORREF)((r << 16) | (g << 8) | (b)))

// Min/Max defines.
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))

#define MINWAVERT_POOLTAG           'RWNM'
#define MINTOPORT_POOLTAG           'RTNM'
#define MINADAPTER_POOLTAG          'uAyS'
#define USBSIDEBANDTEST_POOLTAG01   '1AyS'
#define USBSIDEBANDTEST_POOLTAG02   '2AyS'
#define USBSIDEBANDTEST_POOLTAG03   '3AyS'
#define USBSIDEBANDTEST_POOLTAG04   '4AyS'
#define USBSIDEBANDTEST_POOLTAG05   '5AyS'
#define USBSIDEBANDTEST_POOLTAG06   '6AyS'
#define USBSIDEBANDTEST_POOLTAG07   '7AyS'
#define USBSIDEBANDTEST_POOLTAG08   '8AyS'
#define USBSIDEBANDTEST_POOLTAG09   '9AyS'
#define USBSIDEBANDTEST_POOLTAG010  'aAyS'
#define USBSIDEBANDTEST_POOLTAG011  'bAyS'
#define USBSIDEBANDTEST_POOLTAG012  'cAyS'
#define USBSIDEBANDTEST_POOLTAG013  'dAyS'
#define USBSIDEBANDTEST_POOLTAG014  'eAyS'
#define USBSIDEBANDTEST_POOLTAG015  'fAyS'
#define USBSIDEBANDTEST_POOLTAG016  'gAyS'
#define A2DPSIDEBANDTEST_POOLTAG01  'hAyS'
#define A2DPSIDEBANDTEST_POOLTAG02  'iAyS'
#define A2DPSIDEBANDTEST_POOLTAG03  'jAyS'
#define A2DPSIDEBANDTEST_POOLTAG04  'kAyS'
#define A2DPSIDEBANDTEST_POOLTAG05  'lAyS'
#define A2DPSIDEBANDTEST_POOLTAG06  'mAyS'
#define A2DPSIDEBANDTEST_POOLTAG07  'nAyS'

typedef enum
{
    eSpeakerDevice = 0,
    eSpeakerHpDevice,
    eHdmiRenderDevice,
    eMicInDevice,
    eMicArrayDevice1,
    eMicArrayDevice2,
    eMicArrayDevice3,
    eBthHfpSpeakerDevice,
    eBthHfpMicDevice,
    eCellularDevice,
    eHandsetSpeakerDevice,
    eHandsetMicDevice,
    eSpeakerHsDevice,
    eMicHsDevice,
    eFmRxDevice,
    eSpdifRenderDevice,
    eUsbHsSpeakerDevice,
    eUsbHsMicDevice,
    eA2dpHpSpeakerDevice,
    eMaxDeviceType
    
} eDeviceType;

//
// Signal processing modes and default formats structs.
//
typedef struct _MODE_AND_DEFAULT_FORMAT {
    GUID            Mode;
    KSDATAFORMAT*   DefaultFormat;
} MODE_AND_DEFAULT_FORMAT, *PMODE_AND_DEFAULT_FORMAT;

//
// Enumeration of the various types of pins implemented in this driver.
//
typedef enum
{
    NoPin,
    BridgePin,
    SystemRenderPin,
    OffloadRenderPin,
    RenderLoopbackPin,
    SystemCapturePin,
    KeywordCapturePin,
    TelephonyBidiPin,
} PINTYPE;

//
// PIN_DEVICE_FORMATS_AND_MODES
//
//  Used to specify a pin's type (e.g. system, offload, etc.), formats, and
//  modes. Conceptually serves similar purpose as the PCPIN_DESCRIPTOR to
//  define a pin, but is more specific to driver implementation.
//
//  Arrays of these structures follow the same order as the filter's
//  pin descriptor array so that KS pin IDs can serve as an index.
//
typedef struct _PIN_DEVICE_FORMATS_AND_MODES
{
    PINTYPE                             PinType;

    KSDATAFORMAT_WAVEFORMATEXTENSIBLE * WaveFormats;
    ULONG                               WaveFormatsCount;
    
    MODE_AND_DEFAULT_FORMAT *           ModeAndDefaultFormat;
    ULONG                               ModeAndDefaultFormatCount;

} PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES;

typedef struct _SYSVAD_AUDIOPOSTURE_INFO
{
    BOOL OrientationSupported;
} SYSVAD_AUDIOPOSTURE_INFO, *PSYSVAD_AUDIOPOSTURE_INFO;

//
// Parameter module handler function prototypes.
//
static const GUID NULL_GUID = 
{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}};

static const GUID MODULETYPE_HWEFFECT =
{0x2608993c,0x361e,0x488c,{0xa0,0xbc,0x8f,0x3d,0xe7,0xba,0x12,0xb4}};

//
// Forward declaration.
//
struct AUDIOMODULE_DESCRIPTOR;
typedef AUDIOMODULE_DESCRIPTOR *PAUDIOMODULE_DESCRIPTOR;

typedef struct _AUDIOMODULE_PARAMETER_INFO
{
    USHORT      AccessFlags;        // get/set/basic-support attributes.
    USHORT      Flags;        
    ULONG       Size;
    DWORD       VtType;
    PVOID       ValidSet;
    ULONG       ValidSetCount;
} AUDIOMODULE_PARAMETER_INFO, *PAUDIOMODULE_PARAMETER_INFO;

#define AUDIOMODULE_PARAMETER_FLAG_CHANGE_NOTIFICATION  0x00000001

//
// Module callbacks.
//
typedef
NTSTATUS
(*FN_AUDIOMODULE_INIT_CLASS)
(
    _In_  const AUDIOMODULE_DESCRIPTOR * Module,
    _Inout_opt_ PVOID           Context,
    _In_        size_t          Size,
    _In_        KSAUDIOMODULE_NOTIFICATION * NotificationHeader,
    _In_opt_    PPORTCLSNOTIFICATIONS PortNotifications
);

typedef
NTSTATUS
(*FN_AUDIOMODULE_INIT_INSTANCE)
(
    _In_  const AUDIOMODULE_DESCRIPTOR * Module,
    _In_opt_    PVOID           TemplateContext,
    _Inout_opt_ PVOID           Context,
    _In_        size_t          Size,
    _In_        ULONG           InstanceId
);

typedef
VOID
(*FN_AUDIOMODULE_CLEANUP)
(
    _In_        PVOID           Context
);

typedef
NTSTATUS
(*FN_AUDIOMODULE_HANDLER)
(
    _Inout_opt_                          PVOID   Context,
    _In_reads_bytes_(InBufferCb)         PVOID   InBuffer,
    _In_                                 ULONG   InBufferCb,
    _Out_writes_bytes_opt_(*OutBufferCb) PVOID   OutBuffer,
    _Inout_                              ULONG * OutBufferCb 
);

//
// Module description.
//
struct AUDIOMODULE_DESCRIPTOR
{
    const GUID *                    ClassId; 
    const GUID *                    ProcessingMode;
    WCHAR                           Name[AUDIOMODULE_MAX_NAME_CCH_SIZE];
    ULONG                           InstanceId;
    ULONG                           VersionMajor;
    ULONG                           VersionMinor;
    ULONG                           Flags;
    size_t                          ContextSize;
    FN_AUDIOMODULE_INIT_CLASS       InitClass;
    FN_AUDIOMODULE_INIT_INSTANCE    InitInstance;
    FN_AUDIOMODULE_CLEANUP          Cleanup;
    FN_AUDIOMODULE_HANDLER          Handler;
};

#define AUDIOMODULE_DESCRIPTOR_FLAG_NONE        0x00000000

//
// Audio module instance defintion.
// This sample driver generates an instance id by combinding the
// configuration set # for a class module id with the instance of that 
// configuration. Real driver should use a more robust scheme, such as
// an indirect mapping from/to an instance id to/from a configuration set
// + location in the pipeline + any other info the driver needs.
//
#define AUDIOMODULE_CLASS_CFG_ID_MASK           0xFF
#define AUDIOMODULE_CLASS_CFG_INSTANCE_ID_MASK  0xFFFFFF

#define AUDIOMODULE_INSTANCE_ID(ClassCfgId, ClassCfgInstanceId) \
    ((ULONG(ClassCfgId & AUDIOMODULE_CLASS_CFG_ID_MASK) << 24) | \
     (ULONG(ClassCfgInstanceId & AUDIOMODULE_CLASS_CFG_INSTANCE_ID_MASK)))

#define AUDIOMODULE_GET_CLASSCFGID(InstanceId) \
    (ULONG(InstanceId) >> 24 & AUDIOMODULE_CLASS_CFG_ID_MASK)

//
// Used to track run-time audio module changes.
//
struct AUDIOMODULE
{
    const AUDIOMODULE_DESCRIPTOR *  Descriptor;
    PVOID                           Context;
    ULONG                           InstanceId;
    ULONG                           NextCfgInstanceId;  // used by filter modules
    BOOL                            Enabled;
};

// forward declaration.
typedef struct _ENDPOINT_MINIPAIR *PENDPOINT_MINIPAIR;

// both wave & topology miniport create function prototypes have this form:
typedef HRESULT (*PFNCREATEMINIPORT)(
    _Out_           PUNKNOWN                              * Unknown,
    _In_            REFCLSID,
    _In_opt_        PUNKNOWN                                UnknownOuter,
    _In_            POOL_FLAGS                              PoolFlags, 
    _In_            PUNKNOWN                                UnknownAdapter,
    _In_opt_        PVOID                                   DeviceContext,
    _In_            PENDPOINT_MINIPAIR                      MiniportPair
);

//=============================================================================
//
//=============================================================================
typedef struct _SYSVAD_DEVPROPERTY {
    const DEVPROPKEY   *PropertyKey;
    DEVPROPTYPE Type;
    ULONG BufferSize;
    __field_bcount_opt(BufferSize) PVOID Buffer;
} SYSVAD_DEVPROPERTY, PSYSVAD_DEVPROPERTY;

#define ENDPOINT_NO_FLAGS                       0x00000000
#define ENDPOINT_OFFLOAD_SUPPORTED              0x00000001
#define ENDPOINT_LOOPBACK_SUPPORTED             0x00000002
#define ENDPOINT_SOUNDDETECTOR_SUPPORTED        0x00000004
#define ENDPOINT_CELLULAR_PROVIDER1             0x00000008
#define ENDPOINT_CELLULAR_PROVIDER2             0x00000010

//
// Endpoint miniport pair (wave/topology) descriptor.
//
typedef struct _ENDPOINT_MINIPAIR 
{
    eDeviceType                     DeviceType;

    // Topology miniport.
    PWSTR                           TopoName;               // make sure this or the template name matches with SYSVAD.<TopoName>.szPname in the inf's [Strings] section
    PWSTR                           TemplateTopoName;       // optional template name
    PFNCREATEMINIPORT               TopoCreateCallback;
    PCFILTER_DESCRIPTOR*            TopoDescriptor;
    ULONG                           TopoInterfacePropertyCount;
    const SYSVAD_DEVPROPERTY*       TopoInterfaceProperties;

    // Wave RT miniport.
    PWSTR                           WaveName;               // make sure this or the template name matches with SYSVAD.<WaveName>.szPname in the inf's [Strings] section
    PWSTR                           TemplateWaveName;       // optional template name
    PFNCREATEMINIPORT               WaveCreateCallback;
    PCFILTER_DESCRIPTOR*            WaveDescriptor;
    ULONG                           WaveInterfacePropertyCount;
    const SYSVAD_DEVPROPERTY*       WaveInterfaceProperties;

    USHORT                          DeviceMaxChannels;
    PIN_DEVICE_FORMATS_AND_MODES*   PinDeviceFormatsAndModes;
    ULONG                           PinDeviceFormatsAndModesCount;

    // Miniport physical connections.
    PHYSICALCONNECTIONTABLE*        PhysicalConnections;
    ULONG                           PhysicalConnectionCount;

    // General endpoint flags (one of more ENDPOINT_<flag-type>, see above)
    ULONG                           DeviceFlags;

    // Static module list description.
    AUDIOMODULE_DESCRIPTOR *        ModuleList;
    ULONG                           ModuleListCount;
    const GUID *                    ModuleNotificationDeviceId;
} ENDPOINT_MINIPAIR;

//=============================================================================
// Defines
//=============================================================================

DEFINE_GUID(IID_IAdapterCommon,
0x7eda2950, 0xbf9f, 0x11d0, 0x87, 0x1f, 0x0, 0xa0, 0xc9, 0x11, 0xb5, 0x44);

// {4E1E5697-4D04-4438-A913-B0EEE03732BA}
DEFINE_GUID(IID_IMiniportChange, 
0x4e1e5697, 0x4d04, 0x4438, 0xa9, 0x13, 0xb0, 0xee, 0xe0, 0x37, 0x32, 0xba);

//=============================================================================
// Interfaces
//=============================================================================

///////////////////////////////////////////////////////////////////////////////
// IID_IMiniportChange
//
DECLARE_INTERFACE_(IMiniportChange, IUnknown)
{
    STDMETHOD_(NTSTATUS, NotifyEndpointPair) 
    ( 
        THIS_
        _In_ WCHAR              *RenderEndpointTopoName,
        _In_ ULONG              RenderEndpointNameLen,
        _In_ ULONG              RenderPinId,
        _In_ WCHAR              *CaptureEndpointTopoName,
        _In_ ULONG              CaptureEndpointNameLen,
        _In_ ULONG              CapturePinId
    ) PURE;
};

///////////////////////////////////////////////////////////////////////////////
// IAdapterCommon
//
DECLARE_INTERFACE_(IAdapterCommon, IUnknown)
{
    STDMETHOD_(NTSTATUS,        Init) 
    ( 
        THIS_
        _In_  PDEVICE_OBJECT      DeviceObject 
    ) PURE;

    STDMETHOD_(PDEVICE_OBJECT,  GetDeviceObject)
    (
        THIS
    ) PURE;
    
    STDMETHOD_(PDEVICE_OBJECT,  GetPhysicalDeviceObject)
    (
        THIS
    ) PURE;

    STDMETHOD_(WDFDEVICE,       GetWdfDevice)
    (
        THIS
    ) PURE;

    STDMETHOD_(VOID,            SetWaveServiceGroup) 
    ( 
        THIS_
        _In_ PSERVICEGROUP        ServiceGroup 
    ) PURE;

    STDMETHOD_(BOOL,            bDevSpecificRead)
    (
        THIS_
    ) PURE;

    STDMETHOD_(VOID,            bDevSpecificWrite)
    (
        THIS_
        _In_  BOOL                bDevSpecific
    );

    STDMETHOD_(INT,             iDevSpecificRead)
    (
        THIS_
    ) PURE;

    STDMETHOD_(VOID,            iDevSpecificWrite)
    (
        THIS_
        _In_  INT                 iDevSpecific
    );

    STDMETHOD_(UINT,            uiDevSpecificRead)
    (
        THIS_
    ) PURE;

    STDMETHOD_(VOID,            uiDevSpecificWrite)
    (
        THIS_
        _In_  UINT                uiDevSpecific
    );

    STDMETHOD_(BOOL,            MixerMuteRead)
    (
        THIS_
        _In_  ULONG               Index,
        _In_  ULONG               Channel
    ) PURE;

    STDMETHOD_(VOID,            MixerMuteWrite)
    (
        THIS_
        _In_  ULONG               Index,
        _In_  ULONG               Channel,
        _In_  BOOL                Value
    );

    STDMETHOD_(ULONG,           MixerMuxRead)
    (
        THIS
    );

    STDMETHOD_(VOID,            MixerMuxWrite)
    (
        THIS_
        _In_  ULONG               Index
    );

    STDMETHOD_(LONG,            MixerVolumeRead) 
    ( 
        THIS_
        _In_  ULONG               Index,
        _In_  ULONG               Channel
    ) PURE;

    STDMETHOD_(VOID,            MixerVolumeWrite) 
    ( 
        THIS_
        _In_  ULONG               Index,
        _In_  ULONG               Channel,
        _In_  LONG                Value 
    ) PURE;
    
    STDMETHOD_(LONG,            MixerPeakMeterRead) 
    ( 
        THIS_
        _In_  ULONG               Index,
        _In_  ULONG               Channel
    ) PURE;

    STDMETHOD_(VOID,            MixerReset) 
    ( 
        THIS 
    ) PURE;

    STDMETHOD_(NTSTATUS,        WriteEtwEvent) 
    ( 
        THIS_ 
        _In_ EPcMiniportEngineEvent    miniportEventType,
        _In_ ULONGLONG  ullData1,
        _In_ ULONGLONG  ullData2,
        _In_ ULONGLONG  ullData3,
        _In_ ULONGLONG  ullData4
    ) PURE;

    STDMETHOD_(VOID,            SetEtwHelper) 
    ( 
        THIS_
        PPORTCLSETWHELPER _pPortClsEtwHelper
    ) PURE;
    
    STDMETHOD_(NTSTATUS,        InstallSubdevice)
    ( 
        _In_opt_        PIRP                                        Irp,
        _In_            PWSTR                                       Name,
        _In_opt_        PWSTR                                       TemplateName,
        _In_            REFGUID                                     PortClassId,
        _In_            REFGUID                                     MiniportClassId,
        _In_opt_        PFNCREATEMINIPORT                           MiniportCreate,
        _In_            ULONG                                       cPropertyCount,
        _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY   * pProperties,
        _In_opt_        PVOID                                       DeviceContext,
        _In_            PENDPOINT_MINIPAIR                          MiniportPair,
        _In_opt_        PRESOURCELIST                               ResourceList,
        _In_            REFGUID                                     PortInterfaceId,
        _Out_opt_       PUNKNOWN                                  * OutPortInterface,
        _Out_opt_       PUNKNOWN                                  * OutPortUnknown,
        _Out_opt_       PUNKNOWN                                  * OutMiniportUnknown
    );    

    STDMETHOD_(NTSTATUS,        UnregisterSubdevice)
    (
        THIS_
        _In_opt_   PUNKNOWN     UnknownPort
    );

    STDMETHOD_(NTSTATUS,        ConnectTopologies)
    (
        THIS_
        _In_ PUNKNOWN                   UnknownTopology,
        _In_ PUNKNOWN                   UnknownWave,
        _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
        _In_ ULONG                      PhysicalConnectionCount
    );

    STDMETHOD_(NTSTATUS,        DisconnectTopologies)
    (
        THIS_
        _In_ PUNKNOWN                   UnknownTopology,
        _In_ PUNKNOWN                   UnknownWave,
        _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
        _In_ ULONG                      PhysicalConnectionCount
    );

    STDMETHOD_(NTSTATUS,        InstallEndpointFilters)
    (
        THIS_
        _In_opt_    PIRP                Irp, 
        _In_        PENDPOINT_MINIPAIR  MiniportPair,
        _In_opt_    PVOID               DeviceContext,
        _Out_opt_   PUNKNOWN *          UnknownTopology,
        _Out_opt_   PUNKNOWN *          UnknownWave,
        _Out_opt_   PUNKNOWN *          UnknownMiniportTopology,
        _Out_opt_   PUNKNOWN *          UnknownMiniportWave
    );

    STDMETHOD_(NTSTATUS,        RemoveEndpointFilters)
    (
        THIS_
        _In_        PENDPOINT_MINIPAIR  MiniportPair,
        _In_opt_    PUNKNOWN            UnknownTopology,
        _In_opt_    PUNKNOWN            UnknownWave
    );

    STDMETHOD_(NTSTATUS,        GetFilters)
    (
        THIS_
        _In_        PENDPOINT_MINIPAIR  MiniportPair,
        _Out_opt_   PUNKNOWN            *UnknownTopologyPort,
        _Out_opt_   PUNKNOWN            *UnknownTopologyMiniport,
        _Out_opt_   PUNKNOWN            *UnknownWavePort,
        _Out_opt_   PUNKNOWN            *UnknownWaveMiniport
    );

    STDMETHOD_(NTSTATUS,        SetIdlePowerManagement)
    (
        THIS_
        _In_        PENDPOINT_MINIPAIR  MiniportPair,
        _In_        BOOL                bEnable
    );

#ifdef SYSVAD_BTH_BYPASS
    STDMETHOD_(NTSTATUS,        InitBthScoBypass)();
       
#endif // SYSVAD_BTH_BYPASS

#ifdef SYSVAD_USB_SIDEBAND
    STDMETHOD_(NTSTATUS,        InitUsbSideband)();

    STDMETHOD_(NTSTATUS,        AddDeviceAsPowerDependency)
    (
        _In_ PDEVICE_OBJECT pdo
    );

    STDMETHOD_(NTSTATUS,        RemoveDeviceAsPowerDependency)
    (
        _In_ PDEVICE_OBJECT pdo
    );

#endif // SYSVAD_USB_SIDEBAND

#ifdef SYSVAD_A2DP_SIDEBAND
    STDMETHOD_(NTSTATUS,        InitA2dpSideband)();

#endif // SYSVAD_A2DP_SIDEBAND

    STDMETHOD_(VOID, Cleanup)();

#ifdef SYSVAD_USB_SIDEBAND
    STDMETHOD_(NTSTATUS, UpdatePowerRelations)(_In_ PIRP Irp);
#endif // SYSVAD_USB_SIDEBAND

    STDMETHOD_(NTSTATUS, NotifyEndpointPair) 
    ( 
        THIS_
        _In_ WCHAR  *RenderEndpointTopoName,
        _In_ ULONG  RenderEndpointNameLen,
        _In_ ULONG  RenderPinId,
        _In_ WCHAR  *CaptureEndpointTopoName,
        _In_ ULONG  CaptureEndpointNameLen,
        _In_ ULONG  CapturePinId
    ) PURE;
};

typedef IAdapterCommon *PADAPTERCOMMON;
typedef IMiniportChange *PMINIPORTCHANGE;

//=============================================================================
// Function Prototypes
//=============================================================================
NTSTATUS
NewAdapterCommon
( 
    _Out_       PUNKNOWN *              Unknown,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN                UnknownOuter,
    _In_        POOL_FLAGS              PoolFlags
);


#if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND)

// Event callback definition.
typedef VOID (*PFNEVENTNOTIFICATION)(
    _In_opt_    PVOID   Context
);

DEFINE_GUID(IID_IBthHfpDeviceCommon,
    0x576b824a, 0x5248, 0x47b1, 0x82, 0xc5, 0xe4, 0x7b, 0xa7, 0xe2, 0xaf, 0x2b);

DEFINE_GUID(IID_IUsbHsDeviceCommon,
    0xb57b5547, 0x63f, 0x4a58, 0xb3, 0x7, 0x90, 0xb2, 0xfc, 0x6c, 0x32, 0xdb);

DEFINE_GUID(IID_IA2dpHpDeviceCommon,
    0xe1a6f128, 0x905, 0x4c18, 0xb4, 0x84, 0xc2, 0xca, 0x45, 0xb1, 0x57, 0x6d);



//=============================================================================
// Interfaces
//=============================================================================

///////////////////////////////////////////////////////////////////////////////
// ISidebandDeviceCommon
//
DECLARE_INTERFACE_(ISidebandDeviceCommon, IUnknown)
{
    STDMETHOD_(BOOL,                IsVolumeSupported)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;
    
    STDMETHOD_(PVOID,               GetVolumeSettings)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _Out_       PULONG                  Size 
    ) PURE;

    STDMETHOD_(NTSTATUS,            GetVolume)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_        LONG                    Channel,
        _Out_       LONG                    *pVolume
    ) PURE;

    STDMETHOD_(NTSTATUS,            SetVolume)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_        LONG                    Channel,
        _In_        LONG                    Volume
    ) PURE;

    STDMETHOD_(BOOL,                IsMuteSupported)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;
    
    STDMETHOD_(PVOID,               GetMuteSettings)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _Out_       PULONG                  Size 
    ) PURE;

    STDMETHOD_(LONG,                GetMute)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_        LONG                    Channel
    ) PURE;

    STDMETHOD_(NTSTATUS,            SetMute)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_        LONG                    Channel,
        _In_        LONG                    Mute
    ) PURE;

    STDMETHOD_(BOOL,                GetConnectionStatus)
    (
        THIS_
    ) PURE;
    
    STDMETHOD_(NTSTATUS,            Connect)
    (
        THIS_
    ) PURE;

    STDMETHOD_(NTSTATUS,            Disconnect)
    (
        THIS_
    ) PURE;
    
    STDMETHOD_(BOOL,                GetStreamStatus)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(NTSTATUS,            StreamOpen)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(NTSTATUS,            StreamStart)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(NTSTATUS,            StreamSuspend)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(NTSTATUS,            StreamClose)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(GUID,                GetContainerId)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    STDMETHOD_(VOID,                SetVolumeHandler)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
        _In_opt_    PVOID                   EventHandlerContext
    ) PURE;

    STDMETHOD_(VOID,                SetMuteHandler)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
        _In_opt_    PVOID                   EventHandlerContext
    ) PURE;
    
    STDMETHOD_(VOID,                SetConnectionStatusHandler)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
        _In_opt_    PVOID                   EventHandlerContext
    ) PURE;

    STDMETHOD_(VOID,                SetFormatChangeHandler)
    (
        THIS_
        _In_        eDeviceType             deviceType,
        _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
        _In_opt_    PVOID                   EventHandlerContext
    ) PURE;

    STDMETHOD_(PPIN_DEVICE_FORMATS_AND_MODES, GetFormatsAndModes)
    (
        THIS_
        _In_        eDeviceType             deviceType
    ) PURE;

    _IRQL_requires_max_(DISPATCH_LEVEL)
    STDMETHOD_(BOOL,                IsNRECSupported)
    (
        THIS_
    ) PURE;
    
    STDMETHOD_(BOOL,                GetNRECDisableStatus)
    (
        THIS_
    ) PURE;
};
typedef ISidebandDeviceCommon *PSIDEBANDDEVICECOMMON;

#endif // SYSVAD_BTH_BYPASS

#endif  //_SYSVAD_COMMON_H_




<|start_filename|>audio/simpleaudiosample/Source/Inc/NewDelete.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

NewDelete.h

Abstract:

Declaration of placement new and delete operators.
--*/
#pragma once

#ifdef _NEW_DELETE_OPERATORS_

/*****************************************************************************
* Functions
*/

/*****************************************************************************
* ::new()
*****************************************************************************
* New function for creating objects with a specified allocation tag and
* pool type
*/
PVOID operator new
(
    size_t      iSize,
    POOL_FLAGS  poolFlags,
    ULONG       tag
);


/*****************************************************************************
* ::new()
*****************************************************************************
* New function for creating objects with a specified pool type.
*/
PVOID operator new
(
    size_t      iSize,
    POOL_FLAGS  poolFlags
);


/*****************************************************************************
* ::delete()
*****************************************************************************
* Delete with tag function.
*/
void __cdecl operator delete
(
    PVOID pVoid,
    ULONG tag
);


/*****************************************************************************
* ::delete()
*****************************************************************************
* Sized Delete function.
*/
void __cdecl operator delete
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid,
    _In_ size_t cbSize
);


/*****************************************************************************
* ::delete()
*****************************************************************************
* Basic Delete function.
*/
void __cdecl operator delete
(
    PVOID pVoid
);


/*****************************************************************************
* ::delete()
*****************************************************************************
* Sized Array Delete function.
*/
void __cdecl operator delete[]
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid,
    _In_ size_t cbSize
);


/*****************************************************************************
* ::delete()
*****************************************************************************
* Array Delete function.
*/
void __cdecl operator delete[]
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid
);

#endif//_NEW_DELETE_OPERATORS_


<|start_filename|>storage/miniports/storahci/src/data.h<|end_filename|>
/*++

Copyright (c) 2017  Microsoft Corporation

Module Name:

    data.h

Abstract:

    Definitions needed by storahci log/diagnostic function.

Author:

--*/

#pragma once

//
// Diagnostic data definitions for STORAHCI
//

#define MAX_EXECUTION_HISTORY_ENTRY_COUNT   200 // 200 entries take up 20KB

typedef struct _SLOT_MANAGER {

    ULONG HighPriorityAttribute;

    ULONG NCQueueSlice;
    ULONG NormalQueueSlice;
    ULONG SingleIoSlice;

    ULONG CommandsIssued;
    ULONG CommandsToComplete;

    //
    // These issued slices are used to determine the type of command
    // being programmed to adapter.
    // They are used instead of reading PxCI and PxSACT.
    //
    ULONG NCQueueSliceIssued;
    ULONG NormalQueueSliceIssued;
    ULONG SingleIoSliceIssued;

    ULONG Reserved;

} SLOT_MANAGER, *PSLOT_MANAGER;

typedef struct _EXECUTION_HISTORY {

    ULONG Function;
    ULONG IS;
    SLOT_MANAGER SlotManager;   //SLOT_MANAGER from _AHCI_CHANNEL_EXTENSION
    ULONG Px[0x10];      //Px registers value, end to AHCI_SNOTIFICATION -- SNTF
    LARGE_INTEGER TimeStamp;

} EXECUTION_HISTORY, *PEXECUTION_HISTORY;

typedef struct _ATA_IO_RECORD {

    ULONG   SuccessCount;

    ULONG   CrcErrorCount;
    ULONG   MediaErrorCount;
    ULONG   EndofMediaCount;
    ULONG   IllegalCommandCount;
    ULONG   AbortedCommandCount;
    ULONG   DeviceFaultCount;

    ULONG   OtherErrorCount;

    ULONG   NcqReadLogErrorCount;   // used to record the READ LOG EXT command error count when used for NCQ Error Recovery.

    ULONG   PortDriverResetCount;
    ULONG   TotalResetCount;

} ATA_IO_RECORD, *PATA_IO_RECORD;

typedef struct _STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY {

    ULONG ExecutionHistoryCount;
    ULONG ExecutionHistoryIndex;
    EXECUTION_HISTORY History[MAX_EXECUTION_HISTORY_ENTRY_COUNT];

} STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY, *PSTORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY;

typedef struct _STORAGE_DIAGNOSTIC_AHCI_DATA {

    ULONG Version;
    ULONG Count;
    STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY ExecutionHistory;
    AHCI_PORT Registers;
    ATA_IO_RECORD IoRecord;

} STORAGE_DIAGNOSTIC_AHCI_DATA, *PSTORAGE_DIAGNOSTIC_AHCI_DATA;



<|start_filename|>audio/sysvad/EndpointsCommon/a2dphpspeakertopo.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphpspeakertopo.cpp

Abstract:

    Implementation of topology miniport for the A2DP Headphone speaker (external).

--*/
#ifdef SYSVAD_A2DP_SIDEBAND

#pragma warning (disable : 4127)

#include <sysvad.h>
#include "simple.h"
#include "mintopo.h"
#include "a2dphptopo.h"
#include "a2dphpspeakertopo.h"
#include "a2dphpspeakertoptable.h"

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpSpeakerTopoFilter
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Redirects property request to miniport object

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_A2dpHpSpeakerTopoFilter]"));

    // PropertryRequest structure is filled by portcls. 
    // MajorTarget is a pointer to miniport object for miniports.
    //
    NTSTATUS            ntStatus = STATUS_INVALID_DEVICE_REQUEST;

    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack))
    {
        switch(PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_JACK_DESCRIPTION:
                ntStatus = PropertyHandler_A2dpHpJackDescription(
                    PropertyRequest,
                    ARRAYSIZE(A2dpHpSpeakerJackDescriptions),
                    A2dpHpSpeakerJackDescriptions);
                break;
                
            case KSPROPERTY_JACK_DESCRIPTION2:
                ntStatus = PropertyHandler_A2dpHpJackDescription2(
                    PropertyRequest,
                    ARRAYSIZE(A2dpHpSpeakerJackDescriptions),
                    A2dpHpSpeakerJackDescriptions);
                break;
                
            case KSPROPERTY_JACK_CONTAINERID:
                ntStatus = PropertyHandler_A2dpHpJackContainerId(
                    PropertyRequest,
                    ARRAYSIZE(A2dpHpSpeakerJackDescriptions),
                    A2dpHpSpeakerJackDescriptions);
                break;
        }
    }
    else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio))
    {
        switch (PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_ONESHOT_RECONNECT:
                ntStatus = PropertyHandler_A2dpHpOneShotReconnect(PropertyRequest);
                break;

            case KSPROPERTY_ONESHOT_DISCONNECT:
                ntStatus = PropertyHandler_A2dpHpOneShotDisconnect(PropertyRequest);
                break;
        }
    }

    return ntStatus;
} // PropertyHandler_A2dpHpSpeakerTopoFilter

//=============================================================================
#pragma code_seg()
NTSTATUS
PropertyHandler_A2dpHpSpeakerTopoFilterEvent
(
    _In_    PPCEVENT_REQUEST    EventRequest
)
{
    ASSERT(EventRequest);

    DPF_ENTER(("[PropertyHandler_A2dpHpSpeakerTopoFilterEvent]"));

    return PropertyHandler_A2dpHpTopoNodeEvent(EventRequest);
}

#pragma code_seg()
#endif  // SYSVAD_A2DP_SIDEBAND




<|start_filename|>audio/sysvad/EndpointsCommon/usbhsminwavert.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsminwavert.cpp

Abstract:

    Implementation of wavert miniport.

--*/
#ifdef SYSVAD_USB_SIDEBAND

#pragma warning (disable : 4127)

#include <sysvad.h>
#include <limits.h>
#include "simple.h"
#include "minwavert.h"
#include "minwavertstream.h"
#include "IHVPrivatePropertySet.h"


//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::PropertyHandler_UsbHsAudioEffectsDiscoveryEffectsList  
(
    _In_ PPCPROPERTY_REQUEST PropertyRequest
)
/*++

Routine Description:

  Handles ( KSPROPSETID_AudioEffectsDiscovery, KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST )

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    DPF_ENTER(("[PropertyHandler_UsbHsAudioEffectsDiscoveryEffectsList]"));

    NTSTATUS    ntStatus    = STATUS_INVALID_PARAMETER;
    ULONG       nPinId      = (ULONG)-1;

    // 
    // Validate Pin ID.
    //
    if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        nPinId = *(PULONG(PropertyRequest->Instance));

        // This prop is valid only on streaming pins.
        if (IsSystemRenderPin(nPinId) || IsSystemCapturePin(nPinId))
        {
            ntStatus = STATUS_SUCCESS;
        }
        else if (IsBridgePin(nPinId) ||
                 IsLoopbackPin(nPinId) ||
                  IsOffloadPin(nPinId))
        {
             ntStatus = STATUS_NOT_SUPPORTED;
        }
    }

    IF_FAILED_JUMP(ntStatus, Done);

    //
    // Valid actions: get and basicsupport.
    //
    ntStatus = STATUS_INVALID_PARAMETER;

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ntStatus = 
            PropertyHandler_BasicSupport
            (
                PropertyRequest,
                KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
                VT_ILLEGAL
            );
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        ASSERT(m_pSidebandDevice != NULL);


        // If it is render pin (data flow in), 
        // return size 0 effect list
        if (IsSystemRenderPin(nPinId))
        {
            PropertyRequest->ValueSize = 0;
            ntStatus = STATUS_SUCCESS;
        }
        else
        {
            // Compute total size, two effects: NS and EC (see below).
            ULONG   cbMinSize = sizeof(GUID) * 2;
            
            if (PropertyRequest->ValueSize == 0)
            {
                PropertyRequest->ValueSize = cbMinSize;
                ntStatus = STATUS_BUFFER_OVERFLOW;
            }
            else if (PropertyRequest->ValueSize < cbMinSize)
            {
                ntStatus = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
                PGUID effectList = PGUID(PropertyRequest->Value);

                *effectList = AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION;
                *(effectList + 1) = AUDIO_EFFECT_TYPE_NOISE_SUPPRESSION;
                
                PropertyRequest->ValueSize = cbMinSize;
                ntStatus = STATUS_SUCCESS;
            } 
        }
    }

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_UsbHsWaveFilter
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Redirects general property request to miniport object

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    NTSTATUS            ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    CMiniportWaveRT*    waveRt = reinterpret_cast<CMiniportWaveRT*>(PropertyRequest->MajorTarget);

    if (waveRt == NULL)
    {
        return STATUS_INVALID_PARAMETER;
    }

    waveRt->AddRef();
    
    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin))
    {
        switch (PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_PIN_PROPOSEDATAFORMAT:
                ntStatus = waveRt->PropertyHandlerProposedFormat(PropertyRequest);
                break;

            case KSPROPERTY_PIN_PROPOSEDATAFORMAT2:
                ntStatus = waveRt->PropertyHandlerProposedFormat2(PropertyRequest);
                break;

            default:
                DPF(D_TERSE, ("[PropertyHandler_UsbHsWaveFilter: Invalid Device Request]"));
        }
    }
    else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioEffectsDiscovery))
    {
        switch(PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST:
                ntStatus = waveRt->PropertyHandler_UsbHsAudioEffectsDiscoveryEffectsList(PropertyRequest);
                break;

            default:
                DPF(D_TERSE, ("[PropertyHandler_UsbHsWaveFilter: Invalid Device Request]"));
        }
    }
    
    waveRt->Release();

    return ntStatus;
} // PropertyHandler_UsbHsWaveFilter

#pragma code_seg("PAGE")
#endif  // SYSVAD_USB_SIDEBAND

  




<|start_filename|>audio/sysvad/EndpointsCommon/a2dphptopo.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphptopo.cpp

Abstract:

    Implementation of topology miniport for the A2DP Headphone (external).

--*/
#ifdef SYSVAD_A2DP_SIDEBAND

#pragma warning (disable : 4127)

#include <sysvad.h>
#include "simple.h"
#include "mintopo.h"
#include "a2dphptopo.h"

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpVolumeLevel_BasicSupport
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
)
/*++

Routine Description:

  Handles basic support for A2DP Headphone ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL )

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                ntStatus          = STATUS_SUCCESS;
    ULONG                   cbDescriptionSize = sizeof(KSPROPERTY_DESCRIPTION);

    ASSERT(PropertyRequest);

    if (PropertyRequest->ValueSize >= cbDescriptionSize)
    {
        PCMiniportTopology          miniport            = (PCMiniportTopology)PropertyRequest->MajorTarget;
        ULONG                       volumeSettingsSize  = 0;
        PKSPROPERTY_DESCRIPTION     volumeSettings;
        PKSPROPERTY_DESCRIPTION     propDesc;
        PSIDEBANDDEVICECOMMON       a2dpHpDevice;
        
        propDesc        = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
        a2dpHpDevice    = miniport->GetSidebandDevice(); // weak ref.
        ASSERT(a2dpHpDevice != NULL);
        
        volumeSettings  = (PKSPROPERTY_DESCRIPTION)a2dpHpDevice->GetVolumeSettings(miniport->m_DeviceType, &volumeSettingsSize);
        ASSERT(volumeSettings != NULL);
        ASSERT(volumeSettingsSize != 0);

        ULONG cbFullProperty    = volumeSettings->DescriptionSize;

        // if return buffer can hold a range description, return it
        if(PropertyRequest->ValueSize >= cbFullProperty)
        {
            RtlCopyMemory(propDesc, volumeSettings, volumeSettings->DescriptionSize);
            // set the return value size
            PropertyRequest->ValueSize = cbFullProperty;
        } 
        else
        {
            // otherwise only return description header
            RtlCopyMemory(propDesc, volumeSettings, cbDescriptionSize);
            // set the return value size
            PropertyRequest->ValueSize = cbDescriptionSize;
        }
    } 
    else if(PropertyRequest->ValueSize >= sizeof(ULONG))
    {
        // if return buffer can hold a ULONG, return the access flags
        PULONG AccessFlags = PULONG(PropertyRequest->Value);

        PropertyRequest->ValueSize = sizeof(ULONG);
        *AccessFlags = KSPROPERTY_TYPE_ALL;
    }
    else
    {
        PropertyRequest->ValueSize = 0;
        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpMute_BasicSupport
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
)
/*++

Routine Description:

  Handles basic support for A2DP Headphone ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MUTE )

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                ntStatus          = STATUS_SUCCESS;
    ULONG                   cbDescriptionSize = sizeof(KSPROPERTY_DESCRIPTION);
    ASSERT(PropertyRequest);

    if (PropertyRequest->ValueSize >= cbDescriptionSize)
    {
        PCMiniportTopology          miniport            = (PCMiniportTopology)PropertyRequest->MajorTarget;
        ULONG                       muteSettingsSize    = 0;
        PKSPROPERTY_DESCRIPTION     muteSettings;
        PKSPROPERTY_DESCRIPTION     propDesc;
        PSIDEBANDDEVICECOMMON       a2dpHpDevice;
        
        propDesc        = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
        a2dpHpDevice    = miniport->GetSidebandDevice(); // weak ref.
        ASSERT(a2dpHpDevice != NULL);
        
        muteSettings  = (PKSPROPERTY_DESCRIPTION)a2dpHpDevice->GetMuteSettings(miniport->m_DeviceType, &muteSettingsSize);
        ASSERT(muteSettings != NULL);
        ASSERT(muteSettingsSize != 0);

        ULONG cbFullProperty    = muteSettings->DescriptionSize;

        // if return buffer can hold mute description, return it too
        if(PropertyRequest->ValueSize >= cbFullProperty)
        {
            RtlCopyMemory(propDesc, muteSettings, muteSettings->DescriptionSize);
            // set the return value size
            PropertyRequest->ValueSize = cbFullProperty;
        } 
        else
        {
            // otherwise only return description header
            RtlCopyMemory(propDesc, muteSettings, cbDescriptionSize);
            // set the return value size
            PropertyRequest->ValueSize = cbDescriptionSize;
        }
    } 
    else if(PropertyRequest->ValueSize >= sizeof(ULONG))
    {
        // if return buffer can hold a ULONG, return the access flags
        PULONG AccessFlags = PULONG(PropertyRequest->Value);

        PropertyRequest->ValueSize = sizeof(ULONG);
        *AccessFlags = KSPROPERTY_TYPE_ALL;
    }
    else
    {
        PropertyRequest->ValueSize = 0;
        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpJackDescription
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest, 
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions
)
/*++

Routine Description:

  Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION )

Arguments:

  PropertyRequest - 

  cJackDescriptions -

  JackDescriptions - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                ntStatus            = STATUS_INVALID_DEVICE_REQUEST;
    ULONG                   nPinId              = (ULONG)-1;
    PCMiniportTopology      miniport            = (PCMiniportTopology)PropertyRequest->MajorTarget;

    if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        nPinId = *(PULONG(PropertyRequest->Instance));

        if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL))
        {
            if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
            {
                ntStatus = 
                    PropertyHandler_BasicSupport
                    (
                        PropertyRequest,
                        KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
                        VT_ILLEGAL
                    );
            }
            else
            {
                ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION);

                if (PropertyRequest->ValueSize == 0)
                {
                    PropertyRequest->ValueSize = cbNeeded;
                    ntStatus = STATUS_BUFFER_OVERFLOW;
                }
                else if (PropertyRequest->ValueSize < cbNeeded)
                {
                    ntStatus = STATUS_BUFFER_TOO_SMALL;
                }
                else
                {
                    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
                    {
                        PSIDEBANDDEVICECOMMON a2dpHpDevice    = NULL;
                        PKSMULTIPLE_ITEM    pMI             = (PKSMULTIPLE_ITEM)PropertyRequest->Value;
                        PKSJACK_DESCRIPTION pDesc           = (PKSJACK_DESCRIPTION)(pMI+1);

                        a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref.
                        ASSERT(a2dpHpDevice != NULL);
                        
                        pMI->Size = cbNeeded;
                        pMI->Count = 1;

                        RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION));

                        pDesc->IsConnected = a2dpHpDevice->GetConnectionStatus() ? TRUE : FALSE;
                        
                        ntStatus = STATUS_SUCCESS;
                    }
                }
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpJackDescription2
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions
)
/*++

Routine Description:

  Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 )

Arguments:

  PropertyRequest - 

  cJackDescriptions -

  JackDescriptions - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    ULONG                   nPinId = (ULONG)-1;
    
    if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        nPinId = *(PULONG(PropertyRequest->Instance));

        if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL))
        {
            if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
            {
                ntStatus = 
                    PropertyHandler_BasicSupport
                    (
                        PropertyRequest,
                        KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
                        VT_ILLEGAL
                    );
            }
            else
            {
                ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2);

                if (PropertyRequest->ValueSize == 0)
                {
                    PropertyRequest->ValueSize = cbNeeded;
                    ntStatus = STATUS_BUFFER_OVERFLOW;
                }
                else if (PropertyRequest->ValueSize < cbNeeded)
                {
                    ntStatus = STATUS_BUFFER_TOO_SMALL;
                }
                else
                {
                    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
                    {
                        PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value;
                        PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1);

                        pMI->Size = cbNeeded;
                        pMI->Count = 1;
                        
                        RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2));

                        //
                        // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether 
                        // the jack is currently active, streaming, idle, or hardware not ready.
                        //
                        pDesc->DeviceStateInfo = 0;

                        //
                        // From MSDN:
                        // "If an audio device lacks jack presence detection, the IsConnected member of
                        // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the 
                        // ambiguity that results from this dual meaning of the TRUE value for IsConnected, 
                        // a client application can call IKsJackDescription2::GetJackDescription2 to read 
                        // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has
                        // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint 
                        // does in fact support jack presence detection. In that case, the return value of 
                        // the IsConnected member can be interpreted to accurately reflect the insertion status
                        // of the jack."
                        //
                        // Bit definitions:
                        // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY
                        // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY 
                        //
                        pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY;
                        
                        ntStatus = STATUS_SUCCESS;
                    }
                }
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_A2dpHpJackContainerId
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions
)
/*++

Routine Description:

  Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_CONTAINERID )

Arguments:

  PropertyRequest - 

  cJackDescriptions -

  JackDescriptions - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                ntStatus    = STATUS_INVALID_DEVICE_REQUEST;
    PCMiniportTopology      miniport    = (PCMiniportTopology)PropertyRequest->MajorTarget;
    ULONG                   nPinId      = (ULONG)-1;
    
    if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        nPinId = *(PULONG(PropertyRequest->Instance));

        // This property is only valid on bridge pins.
        if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL))
        {
            if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
            {
                ntStatus = 
                    PropertyHandler_BasicSupport
                    (
                        PropertyRequest,
                        KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
                        VT_ILLEGAL
                    );
            }
            else
            {
                ULONG cbNeeded = sizeof(GUID);

                if (PropertyRequest->ValueSize == 0)
                {
                    PropertyRequest->ValueSize = cbNeeded;
                    ntStatus = STATUS_BUFFER_OVERFLOW;
                }
                else if (PropertyRequest->ValueSize < cbNeeded)
                {
                    ntStatus = STATUS_BUFFER_TOO_SMALL;
                }
                else
                {
                    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
                    {
                        PSIDEBANDDEVICECOMMON a2dpHpDevice = NULL;
                        
                        GUID* guid = (GUID *)PropertyRequest->Value;
                        
                        a2dpHpDevice = miniport->GetSidebandDevice(); // weak ref.
                        ASSERT(a2dpHpDevice != NULL);

                        *guid = a2dpHpDevice->GetContainerId(miniport->m_DeviceType);

                        ntStatus = STATUS_SUCCESS;
                    }
                }
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS 
PropertyHandler_A2dpHpTopoNodeEvent
(
    _In_    PPCEVENT_REQUEST    EventRequest
)
{
    ASSERT(EventRequest);

    DPF_ENTER(("[%!FUNC!]"));

    // The major target is the object pointer to the topology miniport.
    PCMiniportTopology  pMiniport = (PCMiniportTopology)EventRequest->MajorTarget;
    ASSERT (pMiniport);

    switch (EventRequest->Verb)
    {
        // Do we support event handling?!?
        case PCEVENT_VERB_SUPPORT:
            DPF(D_VERBOSE, ("BasicSupport Query for event."));
            break;

        // We should add the event now!
        case PCEVENT_VERB_ADD:
            DPF(D_VERBOSE, ("Adding event."));

            // If we have the interface and EventEntry is defined ...
            if (EventRequest->EventEntry)
            {
                pMiniport->AddEventToEventList(EventRequest->EventEntry);
            }
            else
            {
                return STATUS_UNSUCCESSFUL;
            }
            break;

        case PCEVENT_VERB_REMOVE:
            // We cannot remove the event but we can stop generating the
            // events. However, it also doesn't hurt to always generate them ...
            DPF(D_VERBOSE, ("Removing event."));
            break;

        default:
            return STATUS_INVALID_PARAMETER;
    }

    return STATUS_SUCCESS;
}

NTSTATUS PropertyHandler_A2dpHpOneShotReconnect(PPCPROPERTY_REQUEST PropertyRequest)
{
    DPF_ENTER(("[%!FUNC!]"));
    NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ntStatus = PropertyHandler_BasicSupport(
            PropertyRequest,
            KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
            VT_ILLEGAL
        );
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        // Nothing needs to be done in this handler. It only needs to be defined to enable connect and disconnect through the sound control panel.
        ntStatus = STATUS_SUCCESS;
    }
    return ntStatus;
}

NTSTATUS PropertyHandler_A2dpHpOneShotDisconnect(PPCPROPERTY_REQUEST PropertyRequest)
{
    DPF_ENTER(("[%!FUNC!]"));
    NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ntStatus = PropertyHandler_BasicSupport(
            PropertyRequest,
            KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
            VT_ILLEGAL
        );
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        // Nothing needs to be done in this handler. It only needs to be defined to enable connect and disconnect through the sound control panel.
        ntStatus = STATUS_SUCCESS;
    }
    return ntStatus;
}

#pragma code_seg()
#endif //SYSVAD_A2DP_SIDEBAND




<|start_filename|>general/registry/regfltr/sys/driver.c<|end_filename|>
/*++
Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    regfltr.c

Abstract: 

    Sample driver used to run the kernel mode registry callback samples.

Environment:

    Kernel mode only

--*/

#include "regfltr.h"


DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD     DeviceUnload;

_Dispatch_type_(IRP_MJ_CREATE)         DRIVER_DISPATCH DeviceCreate;
_Dispatch_type_(IRP_MJ_CLOSE)          DRIVER_DISPATCH DeviceClose;
_Dispatch_type_(IRP_MJ_CLEANUP)        DRIVER_DISPATCH DeviceCleanup;
_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH DeviceControl;

//
// Pointer to the device object used to register registry callbacks
//
PDEVICE_OBJECT g_DeviceObj;

//
// Registry callback version
//
ULONG g_MajorVersion;
ULONG g_MinorVersion;

//
// Set to TRUE if TM and RM were successfully created and the transaction
// callback was successfully enabled. 
//
BOOLEAN g_RMCreated;


//
// OS version globals initialized in driver entry 
//

BOOLEAN g_IsWin8OrGreater = FALSE;

VOID
DetectOSVersion()
/*++

Routine Description:

    This routine determines the OS version and initializes some globals used
    in the sample. 

Arguments:
    
    None
    
Return value:

    None. On failure, global variables stay at default value

--*/
{

    RTL_OSVERSIONINFOEXW VersionInfo = {0};
    NTSTATUS Status;
    ULONGLONG ConditionMask = 0;

    //
    // Set VersionInfo to Win7's version number and then use
    // RtlVerifVersionInfo to see if this is win8 or greater.
    //
    
    VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
    VersionInfo.dwMajorVersion = 6;
    VersionInfo.dwMinorVersion = 1;

    VER_SET_CONDITION(ConditionMask, VER_MAJORVERSION, VER_LESS_EQUAL);
    VER_SET_CONDITION(ConditionMask, VER_MINORVERSION, VER_LESS_EQUAL);



    Status = RtlVerifyVersionInfo(&VersionInfo,
                                  VER_MAJORVERSION | VER_MINORVERSION,
                                  ConditionMask);
    if (NT_SUCCESS(Status)) {
        g_IsWin8OrGreater = FALSE;
        InfoPrint("DetectOSVersion: This machine is running Windows 7 or an older OS.");
    } else if (Status == STATUS_REVISION_MISMATCH) {
        g_IsWin8OrGreater = TRUE;
        InfoPrint("DetectOSVersion: This machine is running Windows 8 or a newer OS.");
    } else {
        ErrorPrint("RtlVerifyVersionInfo returned unexpected error status 0x%x.",
            Status);

        //
        // default action is to assume this is not win8
        //
        g_IsWin8OrGreater = FALSE;  
    }
    
}



NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    This routine is called by the operating system to initialize the driver. 
    It allocates a device object, initializes the supported Io callbacks, and
    creates a symlink to make the device accessible to Win32.

    It gets the registry callback version and stores it in the global
    variables g_MajorVersion and g_MinorVersion. It also calls
    CreateKTMResourceManager to create a resource manager that is used in 
    the transaction samples.

Arguments:
    
    DriverObject - Supplies the system control object for this test driver.

    RegistryPath - The string location of the driver's corresponding services 
                   key in the registry.

Return value:

    Success or appropriate failure code.

--*/
{
    NTSTATUS Status;
    UNICODE_STRING NtDeviceName;
    UNICODE_STRING DosDevicesLinkName;
    UNICODE_STRING DeviceSDDLString;

    UNREFERENCED_PARAMETER(RegistryPath);

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, 
               DPFLTR_ERROR_LEVEL,
               "RegFltr: DriverEntry()\n");

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, 
               DPFLTR_ERROR_LEVEL,
               "RegFltr: Use ed nt!Kd_IHVDRIVER_Mask 8 to enable more detailed printouts\n");

    //
    //  Default to NonPagedPoolNx for non paged pool allocations where supported.
    //

    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Create our device object.
    //

    RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME);
    RtlInitUnicodeString(&DeviceSDDLString, DEVICE_SDDL);

    Status = IoCreateDeviceSecure(
                            DriverObject,                 // pointer to driver object
                            0,                            // device extension size
                            &NtDeviceName,                // device name
                            FILE_DEVICE_UNKNOWN,          // device type
                            0,                            // device characteristics
                            TRUE,                         // not exclusive
                            &DeviceSDDLString,            // SDDL string specifying access
                            NULL,                         // device class guid
                            &g_DeviceObj);                // returned device object pointer

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // Set dispatch routines.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE]         = DeviceCreate;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DeviceClose;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP]        = DeviceCleanup;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl;
    DriverObject->DriverUnload                         = DeviceUnload;

    //
    // Create a link in the Win32 namespace.
    //
    
    RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME);

    Status = IoCreateSymbolicLink(&DosDevicesLinkName, &NtDeviceName);

    if (!NT_SUCCESS(Status)) {
        IoDeleteDevice(DriverObject->DeviceObject);
        return Status;
    }

    //
    // Get callback version.
    //

    CmGetCallbackVersion(&g_MajorVersion, &g_MinorVersion);
    InfoPrint("Callback version %u.%u", g_MajorVersion, g_MinorVersion);

    //
    // Some variations depend on knowing if the OS is win8 or above
    //
    
    DetectOSVersion();

    //
    // Set up KTM resource manager and pass in RMCallback as our
    // callback routine.
    //

    Status = CreateKTMResourceManager(RMCallback, NULL);

    if (NT_SUCCESS(Status)) {
        g_RMCreated = TRUE;
    }

    //
    // Initialize the callback context list
    //

    InitializeListHead(&g_CallbackCtxListHead);
    ExInitializeFastMutex(&g_CallbackCtxListLock);
    g_NumCallbackCtxListEntries = 0;

    return STATUS_SUCCESS;
    
}



NTSTATUS
DeviceCreate (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
/*++

Routine Description:

    Dispatches file create requests.  
    
Arguments:

    DeviceObject - The device object receiving the request.

    Irp - The request packet.

Return Value:

    STATUS_NOT_IMPLEMENTED

--*/
{
    UNREFERENCED_PARAMETER(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}



NTSTATUS
DeviceClose (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
/*++

Routine Description:

    Dispatches close requests.

Arguments:

    DeviceObject - The device object receiving the request.

    Irp - The request packet.

Return Value:

    STATUS_SUCCESS

--*/
{
    UNREFERENCED_PARAMETER(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}



NTSTATUS
DeviceCleanup (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
/*++

Routine Description:

    Dispatches cleanup requests.  Does nothing right now.

Arguments:

    DeviceObject - The device object receiving the request.

    Irp - The request packet.

Return Value:

    STATUS_SUCCESS

--*/
{
    UNREFERENCED_PARAMETER(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}



NTSTATUS
DeviceControl (
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    )
/*++

Routine Description:

    Dispatches ioctl requests. 

Arguments:

    DeviceObject - The device object receiving the request.

    Irp - The request packet.

Return Value:

    Status returned from the method called.

--*/
{
    PIO_STACK_LOCATION IrpStack;
    ULONG Ioctl;
    NTSTATUS Status;

    UNREFERENCED_PARAMETER(DeviceObject);

    Status = STATUS_SUCCESS;

    IrpStack = IoGetCurrentIrpStackLocation(Irp);
    Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode;

    switch (Ioctl)
    {

    case IOCTL_DO_KERNELMODE_SAMPLES:
        Status = DoCallbackSamples(DeviceObject, Irp);
        break;

    case IOCTL_REGISTER_CALLBACK:
        Status = RegisterCallback(DeviceObject, Irp);
        break;

    case IOCTL_UNREGISTER_CALLBACK:
        Status = UnRegisterCallback(DeviceObject, Irp);
        break;

    case IOCTL_GET_CALLBACK_VERSION:
        Status = GetCallbackVersion(DeviceObject, Irp);
        break;

    default:
        ErrorPrint("Unrecognized ioctl code 0x%x", Ioctl);
    }

    //
    // Complete the irp and return.
    //

    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
    
}


VOID
DeviceUnload (
    _In_ PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

    Cleans up any driver-level allocations and prepares for unload. All 
    this driver needs to do is to delete the device object and the 
    symbolic link between our device name and the Win32 visible name.

Arguments:

    DeviceObject - The device object receiving the request.

    Irp - The request packet.

Return Value:

    STATUS_NOT_IMPLEMENTED

--*/
{
    UNICODE_STRING  DosDevicesLinkName;

    //
    // Clean up the KTM data structures
    //

    DeleteKTMResourceManager();
    
    //
    // Delete the link from our device name to a name in the Win32 namespace.
    //

    RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME);
    IoDeleteSymbolicLink(&DosDevicesLinkName);

    //
    // Finally delete our device object
    //

    IoDeleteDevice(DriverObject->DeviceObject);

    DbgPrintEx(DPFLTR_IHVDRIVER_ID, 
               DPFLTR_ERROR_LEVEL,
               "RegFltr: DeviceUnload\n");
}



<|start_filename|>audio/sysvad/UsbHsDevice.h<|end_filename|>
//
// This class represents a the Bluetooth Hands-Free Profile USB Headset device.
// There is one class for each interface (id = symbolic-link-name).
//
#ifdef SYSVAD_USB_SIDEBAND


//=====================================================================
//
//  USB Sideband Headset class definition.
//

class CAdapterCommon;
class UsbHsDevice;

struct UsbHsWorkItemContext
{
    CAdapterCommon *    Adapter;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    UsbHsWorkItemContext,
    GetUsbHsWorkItemContext
)

// start/stop the device
enum eUsbHsTaskAction
{
    eUsbHsTaskStart    = 1,
    eUsbHsTaskStop     = 2,
};

struct UsbHsWorkTask
{
    LIST_ENTRY          ListEntry;
    UsbHsDevice      * Device;
    eUsbHsTaskAction   Action;
};


//=====================================================================
//
// Device: Bluetooth Hands-Free Profile USB Headset definitions.
//

// BTH HFP device's notification work-item context.
struct UsbHsDeviceNotificationWorkItemContext
{
    UsbHsDevice *  UsbHsDevice;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    UsbHsDeviceNotificationWorkItemContext,
    GetUsbHsDeviceNotificationWorkItemContext
)

// USB HS device's notification request context.
union UsbHsDeviceNotificationBuffer
{
    SIDEBANDAUDIO_VOLUME_PARAMS         Volume;
    SIDEBANDAUDIO_MUTE_PARAMS           Mute;
    SIDEBANDAUDIO_SIDETONE_PARAMS       Sidetone;
    SIDEBANDAUDIO_STREAM_STATUS_PARAMS  streamStatus;
};

struct UsbHsDeviceNotificationReqContext
{
    UsbHsDevice                     *UsbHsDevice;
    LONG                            Errors;
    UsbHsDeviceNotificationBuffer   Buffer;
    WDFMEMORY                       MemIn;
    WDFMEMORY                       MemOut;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    UsbHsDeviceNotificationReqContext,
    GetUsbHsDeviceNotificationReqContext
)

enum eUsbHsState
{
    eUsbHsStateInvalid         = 0,
    eUsbHsStateInitializing    = 1,
    eUsbHsStateRunning         = 2,
    eUsbHsStateStopping        = 3,
    eUsbHsStateStopped         = 4,
    eUsbHsStateFailed          = 5,
};

// To support event notification.
struct UsbHsEventCallback
{
    PFNEVENTNOTIFICATION    Handler;
    PVOID                   Context;
};

typedef struct _USBHSDEVICE_EP_TRANSPORT_RESOURCES
{
    PUSB_INTERFACE_DESCRIPTOR                       pUsbInterfaceDescriptor;
    PUSBD_ENDPOINT_OFFLOAD_INFORMATION              pUsbOffloadInformation;
    PUSBD_ENDPOINT_OFFLOAD_INFORMATION              pSyncUsbOffloadInformation;
    PSIDEBANDAUDIO_EP_USBAUDIO_TRANSPORT_RESOURCES  pUsbAudioTransportResources;
    PSIDEBANDAUDIO_EP_USBAUDIO_TRANSPORT_RESOURCES  pSyncUsbAudioTransportResources;
    PUSB_ENDPOINT_DESCRIPTOR                        pUsbEndpointDescriptor;
    PUSB_ENDPOINT_DESCRIPTOR                        pSyncUsbEndpointDescriptor;
}USBHSDEVICE_EP_TRANSPORT_RESOURCES, *PUSBHSDEVICE_EP_TRANSPORT_RESOURCES;

#endif // SYSVAD_USB_SIDEBAND
class UsbHsDevice : 
    ISidebandDeviceCommon,
    public CUnknown    
{
    private:
        eUsbHsState            m_State;
            
        IAdapterCommon        * m_Adapter;
        WDFIOTARGET             m_WdfIoTarget;
        
        LIST_ENTRY              m_ListEntry;
        UNICODE_STRING          m_SymbolicLinkName;
        WCHAR                   m_SpeakerWaveNameBuffer[USBHS_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_SpeakerWaveRefString;
        WCHAR                   m_SpeakerTopologyNameBuffer[USBHS_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_SpeakerTopologyRefString;
        WCHAR                   m_MicWaveNameBuffer[USBHS_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_MicWaveRefString;
        WCHAR                   m_MicTopologyNameBuffer[USBHS_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_MicTopologyRefString;
        
        //
        // The Topo Filter Desc and Topo Pins structures referenced from
        // the Miniports structure are deep copies: they start as copies of the 
        // static structures and are modified to allow USB-Device pin 
        // categories based on the info obtained from 
        // IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR
        //
        PENDPOINT_MINIPAIR                      m_SpeakerMiniports;
        PENDPOINT_MINIPAIR                      m_MicMiniports;
        
        PUNKNOWN                                m_UnknownSpeakerTopology;
        PUNKNOWN                                m_UnknownSpeakerWave;
        PUNKNOWN                                m_UnknownMicTopology;
        PUNKNOWN                                m_UnknownMicWave;        

        PSIDEBANDAUDIO_DEVICE_DESCRIPTOR        m_Descriptor;

        //
        // Using new Endpoint descriptor structure
        // Old: SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR
        // Use SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR for 20H2 and older OS.
        //
        // New: SIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 - IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2
        //
        // The new struct includes interface properties
        // that need to be added to the KS Filter interface
        //
        // USB Audio class driver supports both old and new IOCTLs
        //
        PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     m_pSpeakerDescriptor;
        PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     m_pMicDescriptor;
        PKSPROPERTY_DESCRIPTION                 m_SpeakerVolumePropValues;
        LONG                                    m_SpeakerVolumeLevel;
        PKSPROPERTY_DESCRIPTION                 m_MicVolumePropValues;
        LONG                                    m_MicVolumeLevel;

        PKSPROPERTY_DESCRIPTION                 m_SpeakerMutePropValues;
        LONG                                    m_SpeakerMute;
        PKSPROPERTY_DESCRIPTION                 m_MicMutePropValues;
        LONG                                    m_MicMute;


        ULONG                                   m_SpeakerEpIndex;
        ULONG                                   m_MicEpIndex;
        
        union{
          NTSTATUS                              m_SpeakerStreamStatus;
          LONG                                  m_SpeakerStreamStatusLong;
        }; // unnamed.
        union{
          NTSTATUS                              m_MicStreamStatus;
          LONG                                  m_MicStreamStatusLong;
        }; // unnamed.

        KEVENT                                  m_SpeakerStreamStatusEvent;
        KEVENT                                  m_MicStreamStatusEvent;

        PSIDEBANDAUDIO_SUPPORTED_FORMATS        m_pSpeakerSupportedFormatsIntersection;
        ULONG                                   m_SpeakerSelectedFormat;
        PSIDEBANDAUDIO_SUPPORTED_FORMATS        m_pMicSupportedFormatsIntersection;
        ULONG                                   m_MicSelectedFormat;

        USBHSDEVICE_EP_TRANSPORT_RESOURCES      m_SpeakerTransportResources;
        USBHSDEVICE_EP_TRANSPORT_RESOURCES      m_MicTransportResources;

        //
        // Set to TRUE when the HF (remote device) wants to disable the 
        // NR + EC of the AG (local system).
        //
        
        WDFREQUEST              m_SpeakerStreamReq;
        WDFREQUEST              m_MicStreamReq;
        WDFREQUEST              m_SpeakerVolumeReq;
        WDFREQUEST              m_MicVolumeReq;
        WDFREQUEST              m_SpeakerMuteReq;
        WDFREQUEST              m_MicMuteReq;
        WDFWORKITEM             m_WorkItem;
        WDFCOLLECTION           m_ReqCollection;
        KSPIN_LOCK              m_Lock;

        LONG                    m_nSpeakerStreamsOpen; // # of open streams.
        LONG                    m_nMicStreamsOpen; // # of open streams.

        LONG                    m_nSpeakerStreamsStart; // # of Started streams.
        LONG                    m_nMicStreamsStart; // # of Started streams.

        UsbHsEventCallback     m_SpeakerVolumeCallback;
        UsbHsEventCallback     m_MicVolumeCallback;

        UsbHsEventCallback     m_SpeakerMuteCallback;
        UsbHsEventCallback     m_MicMuteCallback;

        UsbHsEventCallback     m_SpeakerConnectionStatusCallback;
        UsbHsEventCallback     m_MicConnectionStatusCallback;

        UsbHsEventCallback     m_SpeakerFormatChangeCallback;
        UsbHsEventCallback     m_MicFormatChangeCallback;

    public:
        //=====================================================================
        // Default CUnknown
        DECLARE_STD_UNKNOWN();
        DEFINE_STD_CONSTRUCTOR(UsbHsDevice);
        ~UsbHsDevice();

        NTSTATUS Init
        (
            _In_ IAdapterCommon     * Adapter, 
            _In_ PUNICODE_STRING      SymbolicLinkName
        );

    public:
        //=====================================================================
        //
        // Public functions used by CAdapterCommon object.
        //
        VOID Start();
        VOID Stop();

        PLIST_ENTRY GetListEntry()
        {
            return &m_ListEntry;
        }

        PUNICODE_STRING GetSymbolicLinkName()
        {
            return &m_SymbolicLinkName;
        }

        static
        UsbHsDevice * 
        GetUsbHsDevice
        (
            _In_ PLIST_ENTRY le
        )
        {
            return CONTAINING_RECORD(le, UsbHsDevice, m_ListEntry);
        }

    public:
        //=====================================================================
        //
        // ISidebandDeviceCommon functions. 
        //
        STDMETHODIMP_(BOOL)                 IsVolumeSupported
        (
            _In_        eDeviceType             deviceType
        );
        
        STDMETHODIMP_(PVOID)                GetVolumeSettings
        (
            _In_        eDeviceType             deviceType,
            _Out_       PULONG                  Size 
        );
    
        STDMETHODIMP_(NTSTATUS)             GetVolume
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _Out_       LONG                    *pVolume
        );
     
        STDMETHODIMP_(NTSTATUS)             SetVolume
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _In_        LONG                    Volume
        );

        STDMETHODIMP_(BOOL)                 IsMuteSupported
        (
            _In_        eDeviceType             deviceType
        );
        
        STDMETHODIMP_(PVOID)                GetMuteSettings
        (
            _In_        eDeviceType             deviceType,
            _Out_       PULONG                  Size 
        );
    
        STDMETHODIMP_(LONG)                 GetMute
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel
        );
     
        STDMETHODIMP_(NTSTATUS)             SetMute
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _In_        LONG                    Mute
        );

        STDMETHODIMP_(BOOL)                 GetConnectionStatus();

        STDMETHODIMP_(NTSTATUS)             Connect();

        STDMETHODIMP_(NTSTATUS)             Disconnect();
    
        STDMETHODIMP_(BOOL)                 GetStreamStatus(_In_ eDeviceType deviceType);
        
        STDMETHODIMP_(NTSTATUS)             StreamOpen(_In_ eDeviceType deviceType);

        STDMETHODIMP_(NTSTATUS)             StreamStart(_In_ eDeviceType deviceType);

        STDMETHODIMP_(NTSTATUS)             StreamSuspend(_In_ eDeviceType deviceType);

        STDMETHODIMP_(NTSTATUS)             StreamClose(_In_ eDeviceType deviceType);

        STDMETHODIMP_(GUID)                 GetContainerId(_In_ eDeviceType deviceType);

        STDMETHODIMP_(VOID)                 SetVolumeHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetMuteHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetConnectionStatusHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetFormatChangeHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(PPIN_DEVICE_FORMATS_AND_MODES)    GetFormatsAndModes
        (
            _In_        eDeviceType             deviceType
        );

        _IRQL_requires_max_(DISPATCH_LEVEL)
        STDMETHODIMP_(BOOL)                 IsNRECSupported();
        
        STDMETHODIMP_(BOOL)                 GetNRECDisableStatus();

    private:
        //=====================================================================
        //
        // Helper functions.
        //
        NTSTATUS    SendIoCtrlSynchronously
        (
            _In_opt_    WDFREQUEST  Request,
            _In_        ULONG       IoControlCode,
            _In_        ULONG       InLength,
            _In_        ULONG       OutLength,
            _When_(InLength > 0 || OutLength > 0, _In_)
            _When_(InLength == 0 && OutLength == 0, _In_opt_)
                        PVOID       Buffer
        );

        NTSTATUS    SendIoCtrlSynchronously
        (
            _In_opt_    WDFREQUEST  Request,
            _In_        ULONG       IoControlCode,
            _In_        ULONG       InLength,
            _In_        ULONG       OutLength,
            _When_(InLength > 0, _In_)
            _When_(InLength == 0, _In_opt_)
                        PVOID       InBuffer,
            _When_(OutLength > 0, _In_)
            _When_(OutLength == 0, _In_opt_)
                        PVOID       OutBuffer
        );
        
        NTSTATUS    SendIoCtrlAsynchronously
        (
            _In_        WDFREQUEST      Request,
            _In_        ULONG           IoControlCode,
            _In_opt_    WDFMEMORY       MemIn,
            _In_opt_    WDFMEMORY       MemOut,
            _In_        PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
            _In_        WDFCONTEXT      Context
        );
        
        NTSTATUS    GetUsbHsDescriptor
        (
            _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor
        );
        
        NTSTATUS    GetUsbHsEndpointDescriptor
        (
            _In_    ULONG                                   EpIndex,
            _Out_   PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     *EpDescriptor
        );

        NTSTATUS    GetUsbHsVolumePropertyValues
        (
            _In_  ULONG                     EpIndex,
            _In_  ULONG                     Length,
            _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
        );
        
        NTSTATUS    SetUsbHsSpeakerVolume
        (
            _In_ LONG   Channel,
            _In_ LONG   Volume  
        );
        
        NTSTATUS    GetUsbHsSpeakerVolume
        (
            _In_  LONG  Channel,
            _Out_ LONG  *Volume    
        );
        
        NTSTATUS    EnableUsbHsSpeakerVolumeStatusNotification();
        
        NTSTATUS    SetUsbHsMicVolume
        (
            _In_ LONG  Channel,
            _In_ LONG  Volume  
        );
        
        NTSTATUS    GetUsbHsMicVolume
        (
            _In_  LONG  Channel,
            _Out_ LONG  * Volume    
        );
        
        NTSTATUS    EnableUsbHsMicVolumeStatusNotification();

        NTSTATUS    GetUsbHsMutePropertyValues
        (
            _In_  ULONG                     EpIndex,
            _In_ ULONG                      Length,
            _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
        );
        
        NTSTATUS    SetUsbHsSpeakerMute
        (
            _In_ LONG   Channel,
            _In_ LONG   Mute
        );
        
        NTSTATUS    GetUsbHsSpeakerMute
        (
            _In_  LONG  Channel,
            _Out_ LONG  *Mute
        );
        
        NTSTATUS    EnableUsbHsSpeakerMuteStatusNotification();
        
        NTSTATUS    SetUsbHsMicMute
        (
            _In_ LONG   Channel,
            _In_ LONG   Mute
        );
        
        NTSTATUS    GetUsbHsMicMute
        (
            _In_  LONG  Channel,
            _Out_ LONG  *Mute
        );
        
        NTSTATUS    EnableUsbHsMicMuteStatusNotification();

        NTSTATUS    GetUsbHsEndpointFormatsIntersection
        (
            _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS        pDeviceFormats,
            _Out_   PSIDEBANDAUDIO_SUPPORTED_FORMATS        *ppSupportedFormatsIntersection
        );

        NTSTATUS    VerifyEndpointFormatCompatibility
        (
            _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS        EpSupportedFormats,
            _In_    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE      pHardwareSupportedFormats,
            _In_    ULONG                                   NumHardwareSupportedFormats,
            _Out_   PULONG                                  pHardwareFormatIndex
        );

        NTSTATUS    FreeTransportResources
        (
            _In_    PUSBHSDEVICE_EP_TRANSPORT_RESOURCES     pTransportResources
        );

        NTSTATUS    GetTransportResources
        (
            _In_    ULONG                                   EpIndex,
            _In_    PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     pEndpointDescriptor,
            _Out_   PUSBHSDEVICE_EP_TRANSPORT_RESOURCES     pTransportResources
        );

        NTSTATUS    SetTransportResources
        (
            _In_    PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     pEndpointDescriptor,
            _In_    ULONG                                   EpIndex
        );

        NTSTATUS    GetSiop
        (
            _In_        const GUID  *ParamSet,
            _In_        ULONG       SiopTypeId,
            _In_        ULONG       EpIndex,
            _In_        ULONG       PoolTag,
            _Outptr_    PBYTE       *ppOutputBuffer
        );
        
        static
        NTSTATUS    CreateCustomEndpointMinipair
        (
            _In_        PENDPOINT_MINIPAIR  pBaseMinipair,
            _In_        PUNICODE_STRING     FriendlyName,
            _In_        PGUID               pCategory,
            _Outptr_    PENDPOINT_MINIPAIR  *ppCustomMinipair
        );

        static
        NTSTATUS    UpdateCustomEndpointCategory
        (
            _In_        PPCFILTER_DESCRIPTOR    pCustomMinipairTopoFilter,
            _In_        PPCPIN_DESCRIPTOR       pCustomMinipairTopoPins,
            _In_        PGUID                   pCategory
        );

        static
        VOID        DeleteCustomEndpointMinipair
        (
            _In_        PENDPOINT_MINIPAIR CustomMinipair
        );

        NTSTATUS    SetSidebandClaimed(_In_ BOOL bClaimed);
        
        NTSTATUS    SpeakerStreamOpen();

        NTSTATUS    MicStreamOpen();

        NTSTATUS    SpeakerStreamStart();

        NTSTATUS    MicStreamStart();

        NTSTATUS    SpeakerStreamSuspend();

        NTSTATUS    MicStreamSuspend();

        NTSTATUS    SpeakerStreamClose();

        NTSTATUS    MicStreamClose();

        NTSTATUS    SetUsbHsStreamOpen(ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor);

        NTSTATUS    SetUsbHsStreamStart(ULONG EpIndex);

        NTSTATUS    SetUsbHsStreamSuspend(ULONG EpIndex);

        NTSTATUS    SetUsbHsStreamClose(ULONG EpIndex);

        NTSTATUS    EnableUsbHsSpeakerStreamStatusNotification();
        
        NTSTATUS    StopUsbHsSpeakerStreamStatusNotification();
        
        NTSTATUS    EnableUsbHsMicStreamStatusNotification();
        
        NTSTATUS    StopUsbHsMicStreamStatusNotification();

        NTSTATUS    CreateFilterNames(
            _In_ PUNICODE_STRING UsbHsDeviceSymbolicLinkName
        );


#if 0
        NTSTATUS    GetSpeakerTransportResources();
        NTSTATUS    FreeSpeakerTransportResources();
        NTSTATUS    GetMicTransportResources();
        NTSTATUS    FreeMicTransportResources();
#endif
        
        //
        // WDF I/O Target callback.
        //
        static
        EVT_WDF_IO_TARGET_QUERY_REMOVE    EvtUsbHsTargetQueryRemove;

        static
        EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtUsbHsTargetRemoveCanceled;

        static
        EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtUsbHsTargetRemoveComplete;

        //
        // Status notifications callbacks.
        //
        static 
        EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtUsbHsDeviceStreamStatusCompletion;

        static 
        EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtUsbHsDeviceNotificationStatusCompletion;
        
        static
        EVT_WDF_WORKITEM                   EvtUsbHsDeviceNotificationStatusWorkItem;
};


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/FormatHelpers.h<|end_filename|>
//
// FormatHelpers.h -- Copyright (c) Microsoft Corporation
//
// Author: zaneh
//
// Description:
//
//   Format Helpers for test formats used in APODeviceTest
//
#pragma once

#include <MMDeviceAPIp.h>
#include <DeviceTopologyp.h>
#include <mfapi.h>
#include <AVEndpointKeys.h>
#include <AudioCoreAPO32Params.h>
#include <ksmedia.h>

// Consts used for APO format creation
static const int cFrameRate             = 48000; // 48kHz
static const int cProcessingPeriod      = 10;    // milliseconds
static const int cMaxFrameCount         = cFrameRate * cProcessingPeriod / 1000;
static const int cFrameCountToProcess   = cMaxFrameCount / 2;

// Returns true if the two IAudioMediaType formats are equal.
// False otherwise
bool IsEqualFormat
(
    IAudioMediaType      *pIAMTFormat1,
    IAudioMediaType      *pIAMTFormat2
)
{
    UINT                        cbBuffer1, cbBuffer2;
    WAVEFORMATEX const         *pwfx1, *pwfx2;
    UNCOMPRESSEDAUDIOFORMAT     format1, format2;
    
    pwfx1     = pIAMTFormat1->GetAudioFormat();
    pwfx2     = pIAMTFormat2->GetAudioFormat();
    cbBuffer1 = ((WAVE_FORMAT_PCM == pwfx1->wFormatTag)?sizeof(PCMWAVEFORMAT):(sizeof(WAVEFORMATEX) + pwfx1->cbSize));
    cbBuffer2 = ((WAVE_FORMAT_PCM == pwfx2->wFormatTag)?sizeof(PCMWAVEFORMAT):(sizeof(WAVEFORMATEX) + pwfx2->cbSize));
    
    if (cbBuffer1 != cbBuffer2)
    {
        return false;
    }
    
    //  Two formats don't compare as WAVEFORMATEX...
    if (0 != memcmp(pwfx1, pwfx2, cbBuffer1))
    {
        return false;
    }
    
    SecureZeroMemory(&format1, sizeof(format1));
    SecureZeroMemory(&format2, sizeof(format2));
    
    if (S_OK != pIAMTFormat1->GetUncompressedAudioFormat(&format1))
    {
        return false;
    }
    
    if (S_OK != pIAMTFormat2->GetUncompressedAudioFormat(&format2))
    {
        return false;
    }
    
    //  Two formats don't compare as WAVEFORMATEX...
    if (0 != memcmp(&format1, &format2, sizeof(UNCOMPRESSEDAUDIOFORMAT)))
    {
        return false;
    }
    
    return true;
}

// Creates a KSData format from the given WaveFormatEx
// Returns a struct containing both the KSData format and WaveFormatEx
KSDATAFORMAT_WAVEFORMATEX *CreateKSDataFromWFX
(
    WAVEFORMATEX               *pwfx
)
{
    if (nullptr == pwfx)
    {
        return nullptr;
    }

    UINT cbAlloc;    
    cbAlloc = sizeof(KSDATAFORMAT_WAVEFORMATEX) + pwfx->cbSize;
    
    wil::unique_cotaskmem_ptr<KSDATAFORMAT_WAVEFORMATEX> pKsFormat((KSDATAFORMAT_WAVEFORMATEX*)(CoTaskMemAlloc(cbAlloc)));
    
    if (nullptr == pKsFormat)
    {
        return nullptr;
    }
    
    pKsFormat->DataFormat.FormatSize  = cbAlloc;
    pKsFormat->DataFormat.Flags       = 0;
    pKsFormat->DataFormat.Reserved    = 0;
    pKsFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
    pKsFormat->DataFormat.Specifier   = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
    
    CopyMemory(&(pKsFormat->WaveFormatEx), pwfx, sizeof(WAVEFORMATEX) + pwfx->cbSize);
    
    if (WAVE_FORMAT_EXTENSIBLE == pwfx->wFormatTag)
    {
        pKsFormat->DataFormat.SubFormat = ((WAVEFORMATEXTENSIBLE*)(pwfx))->SubFormat;
    }
    else
    {
        INIT_WAVEFORMATEX_GUID(&(pKsFormat->DataFormat.SubFormat), pwfx->wFormatTag);
    }
    
    return (pKsFormat.release());
}

// Creates an IAudioMediaType given an uncompressed audio format
HRESULT AECreateAudioMediaTypeFromUncompressedAudioFormat(const UNCOMPRESSEDAUDIOFORMAT* pUncompressedAudioFormat,IAudioMediaType** ppIAudioMediaType)
{
    RETURN_IF_FAILED(CTestAudioMediaType::Create(pUncompressedAudioFormat, ppIAudioMediaType));

    return S_OK;
}

// Creates an IAudioMediaType given a WaveFormatEx
HRESULT AECreateAudioMediaType(const WAVEFORMATEX* pWfx, IAudioMediaType** ppIAudioMediaType)
{
    UNCOMPRESSEDAUDIOFORMAT   audioFormat;
   
    audioFormat.dwBytesPerSampleContainer = pWfx->nBlockAlign / pWfx->nChannels;
    audioFormat.dwSamplesPerFrame = pWfx->nChannels;
    if (WAVE_FORMAT_EXTENSIBLE == pWfx->wFormatTag)
    {
        audioFormat.dwValidBitsPerSample = ((WAVEFORMATEXTENSIBLE*)pWfx)->Samples.wValidBitsPerSample;
        audioFormat.fFramesPerSecond = (float)(((WAVEFORMATEXTENSIBLE*)pWfx)->Format.nSamplesPerSec);
        audioFormat.guidFormatType = ((WAVEFORMATEXTENSIBLE*)pWfx)->SubFormat;
    }
    else
    {
        audioFormat.dwValidBitsPerSample = pWfx->wBitsPerSample;
        audioFormat.fFramesPerSecond = (float)(pWfx->nSamplesPerSec);
        INIT_WAVEFORMATEX_GUID(&(audioFormat.guidFormatType), pWfx->wFormatTag);
    }

    RETURN_IF_FAILED(CTestAudioMediaType::Create(&audioFormat, ppIAudioMediaType));
    return S_OK;
}

// Fills an APO connection using the provided uncompressed format.
// If pBuffer is NULL, the connection will be created with an APO_CONNECTION_BUFFER_TYPE_ALLOCATED.
// If pBuffer is not null, APO_CONNECTION_BUFFER_TYPE_EXTERNAL will be used.
// Outptr pConnection returns an APO_CONNECTION_DESCRIPTOR
HRESULT FillConnection
(
    UNCOMPRESSEDAUDIOFORMAT* Format,
    UINT_PTR pBuffer,
    UINT32 /* u32ExtraFrameCount */,
    UINT32 u32MaxFrameCount,
    APO_CONNECTION_DESCRIPTOR* pConnection
)
{
    if (Format == nullptr)
    {
        LOG_ERROR(L"FillConnection recieved null format");
        return E_INVALIDARG;
    }
    if (pConnection == nullptr)
    {
        LOG_ERROR(L"FillConnection recieved null APO connection descriptor");
        return E_INVALIDARG;
    }
    
    IAudioMediaType *pFormat;

    // Attempt to create the AudioMediaType using the standard API
    HRESULT createFormatHr = CreateAudioMediaTypeFromUncompressedAudioFormat(
        Format,
        &pFormat);

    if (FAILED(createFormatHr))
    {
        LOG_OUTPUT(L"Standard AudioMediaType creation failed. Attempting creation using TestMediaType");
        RETURN_IF_FAILED(AECreateAudioMediaTypeFromUncompressedAudioFormat(Format, &pFormat));
    }

    // set connection type based on pBuffer
    // this will get re(set) when the actual allocation occurs
    if (NULL != pBuffer)
    {
        pConnection->Type = APO_CONNECTION_BUFFER_TYPE_EXTERNAL;
    }
    else
    {
        pConnection->Type = APO_CONNECTION_BUFFER_TYPE_ALLOCATED;
    }
    
    pConnection->pFormat = pFormat;
    pConnection->pBuffer = pBuffer;
    pConnection->u32MaxFrameCount = u32MaxFrameCount;
    pConnection->u32Signature = APO_CONNECTION_DESCRIPTOR_SIGNATURE;

    return createFormatHr;
}

// Creates a WaveFormatEx format
void FillFormat
(
    WORD FormatType,
    DWORD dwSamplesPerFrame,
    DWORD /* dwBytesPerSampleContainer */,
    DWORD dwValidBitsPerSample,
    FLOAT32 fFramesPerSecond,
    WAVEFORMATEX* pWfx
)
{
    if (pWfx == nullptr)
    {
        LOG_ERROR(L"WaveformatEx is null");
    }

    if (dwSamplesPerFrame >= 0xffff)
    {
        LOG_ERROR(L"SamplesPerFrame too great");
    }

    if (dwValidBitsPerSample >= 0xffff)
    {
        LOG_ERROR(L"ValidBitsPerSample too great");
    }

    pWfx->wFormatTag = FormatType;
    pWfx->nChannels = (WORD)(dwSamplesPerFrame & 0xffff);
    pWfx->wBitsPerSample = (WORD)dwValidBitsPerSample;
    pWfx->nBlockAlign = pWfx->nChannels * (pWfx->wBitsPerSample / 8);
    pWfx->nSamplesPerSec = (DWORD)fFramesPerSecond;
    pWfx->nAvgBytesPerSec = pWfx->nSamplesPerSec * pWfx->nBlockAlign;
    pWfx->cbSize = 0;
}

// Creates an UncompressedAudioFormat 
void FillFormat
(
    GUID FormatType,
    DWORD dwSamplesPerFrame,
    DWORD dwBytesPerSampleContainer,
    DWORD dwValidBitsPerSample,
    FLOAT32 fFramesPerSecond,
    DWORD dwChannelMask,
    UNCOMPRESSEDAUDIOFORMAT* pFormat
)
{
    if (pFormat == nullptr)
    {
        LOG_ERROR(L"WaveformatEx is null");
    }

    if (dwSamplesPerFrame >= 0xffff)
    {
        LOG_ERROR(L"SamplesPerFrame too great");
    }

    if (dwValidBitsPerSample >= 0xffff)
    {
        LOG_ERROR(L"ValidBitsPerSample too great");
    }

    pFormat->guidFormatType = FormatType;
    pFormat->dwSamplesPerFrame = dwSamplesPerFrame;
    pFormat->dwBytesPerSampleContainer = dwBytesPerSampleContainer;
    pFormat->dwValidBitsPerSample = dwValidBitsPerSample;
    pFormat->fFramesPerSecond = fFramesPerSecond;
    pFormat->dwChannelMask = dwChannelMask;
}

HRESULT SetupConnection
(
    APO_CONNECTION_DESCRIPTOR* pInputConnDesc,
    APO_CONNECTION_DESCRIPTOR* pOutputConnDesc,
    APO_CONNECTION_PROPERTY* pInputConnProp,
    APO_CONNECTION_PROPERTY* pOutputConnProp,
    UNCOMPRESSEDAUDIOFORMAT* pDefaultFormat
)
{
    if (pInputConnDesc == nullptr)
    {
        LOG_ERROR(L"SetupConnection recieved null APO connection descriptor for input");
        return E_INVALIDARG;
    }

    if (pOutputConnDesc == nullptr)
    {
        LOG_ERROR(L"SetupConnection recieved null APO connection descriptor for output");
        return E_INVALIDARG;
    }

    if (pInputConnProp == nullptr)
    {
        LOG_ERROR(L"SetupConnection recieved null APO connection property for input");
        return E_INVALIDARG;
    }

    if (pOutputConnProp == nullptr)
    {
        LOG_ERROR(L"SetupConnection recieved null APO connection property for output");
        return E_INVALIDARG;
    }
    
    if (pDefaultFormat == nullptr)
    {
        LOG_ERROR(L"SetupConnection received null format");
        return E_INVALIDARG;
    }

    LOG_RETURN_IF_FAILED(FillConnection(pDefaultFormat, NULL, 0, cMaxFrameCount, pInputConnDesc),
                        L"\tFillConnection for input descriptor failed");

    LOG_RETURN_IF_FAILED(CreateConnection(pInputConnDesc, pInputConnProp),
                        L"\tCreateConnection for input descriptor failed");

    pInputConnProp->u32ValidFrameCount = cFrameCountToProcess;
    pInputConnProp->u32BufferFlags = BUFFER_VALID;

    LOG_RETURN_IF_FAILED(FillConnection(pDefaultFormat, NULL, 0, cMaxFrameCount, pOutputConnDesc),
                        L"\tFillConnection for output descriptor failed");

    LOG_RETURN_IF_FAILED(CreateConnection(pOutputConnDesc, pOutputConnProp),
                        L"\tCreateConnection for output descriptor failed");

    return S_OK;
}

<|start_filename|>audio/sysvad/TabletAudioSample/hdmitopo.h<|end_filename|>

/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    hdmitopo.h

Abstract:

    Declaration of topology miniport for the hdmi endpoint.

--*/

#ifndef _SYSVAD_HDMITOPO_H_
#define _SYSVAD_HDMITOPO_H_

#include "basetopo.h"

//=============================================================================
// Classes
//=============================================================================

///////////////////////////////////////////////////////////////////////////////
// CHdmiMiniportTopology 
//   

#pragma code_seg()
class CHdmiMiniportTopology : 
    public CMiniportTopologySYSVAD,
    public IMiniportTopology,
    public CUnknown
{
  public:
    DECLARE_STD_UNKNOWN();
    CHdmiMiniportTopology
    (
        _In_opt_    PUNKNOWN                UnknownOuter,
        _In_        PCFILTER_DESCRIPTOR    *FilterDesc,
        _In_        USHORT                  DeviceMaxChannels
    ) 
    : CUnknown(UnknownOuter), 
      CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels)
    {}
    
    ~CHdmiMiniportTopology();

    IMP_IMiniportTopology;

    NTSTATUS            PropertyHandlerJackSinkInfo
    (
        _In_ PPCPROPERTY_REQUEST  PropertyRequest
    );
    
    NTSTATUS            PropertyHandlerJackDescription
    (
        _In_ PPCPROPERTY_REQUEST  PropertyRequest
    );

    NTSTATUS            PropertyHandlerJackDescription2
    (
        _In_ PPCPROPERTY_REQUEST  PropertyRequest
    );
};
    
typedef CHdmiMiniportTopology *PCHdmiMiniportTopology;


NTSTATUS
CreateHdmiMiniportTopology( 
    _Out_           PUNKNOWN                              * Unknown,
    _In_            REFCLSID,
    _In_opt_        PUNKNOWN                                UnknownOuter,
    _In_            POOL_FLAGS                              PoolFlags,
    _In_            PUNKNOWN                                UnknownAdapter,
    _In_opt_        PVOID                                   DeviceContext,
    _In_            PENDPOINT_MINIPAIR                      MiniportPair
    );

NTSTATUS PropertyHandler_HdmiTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest);

#endif // _SYSVAD_HDMITOPO_H_



<|start_filename|>gnss/gnssUmdf/Queue.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    queue.cpp

Abstract:

    This file contains the queue entry points and callbacks.

Environment:

    Windows User-Mode Driver Framework

--*/

#include "precomp.h"
#include "Trace.h"
#include "Defaults.h"
#include "Device.h"
#include "FixSession.h"
#include "FixHandler.h"
#include "Queue.h"

#include "Queue.tmh"


CQueue::CQueue(
    WDFQUEUE Queue
) :
    _Queue(Queue),
    _ForcedDriverVersion(GNSS_DRIVER_DDK_VERSION)
{
    InitializeCriticalSection(&_Lock);
}

CQueue::~CQueue()
{
    DeleteCriticalSection(&_Lock);
    _Queue = nullptr;
}


void
CQueue::OnCleanup(
    _In_ WDFOBJECT Object
)
{
    CQueue *pQueue = GetQueueObject(Object);

    // Queue object constructed using placement 'new' so explicitly invoke destructor
    pQueue->~CQueue();
}

NTSTATUS
CQueue::Initialize()
{
    NTSTATUS status = STATUS_SUCCESS;

    // Initialize the Fix Handler
    status = _FixHandler.Initialize(_Queue);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "_FixHandler->Initialize failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

void
CQueue::OnIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
)
{
    CQueue *pQueue = GetQueueObject(Queue);

    pQueue->OnIoDeviceControl(Request, IoControlCode, InputBufferLength, OutputBufferLength);
}

NTSTATUS
CQueue::AddQueueToDevice(
    _In_ WDFDEVICE Device,
    _Outptr_ CQueue** Queue
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Create a sequential IO Queue for serial operation and supply all the callbacks.
    // A sequential queue will cause all IO requests to be serialized to the device (i.e., only one request will sent at a time).
    WDF_IO_QUEUE_CONFIG queueConfig;
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,
                                           WdfIoQueueDispatchSequential);

    queueConfig.PowerManaged = WdfFalse;
    queueConfig.EvtIoDeviceControl = CQueue::OnIoDeviceControl;

    WDF_OBJECT_ATTRIBUTES queueAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes,
                                            CQueue);

    queueAttributes.EvtCleanupCallback = CQueue::OnCleanup;

    WDFQUEUE queue = nullptr;
    status = WdfIoQueueCreate(Device,
                              &queueConfig,
                              &queueAttributes,
                              &queue);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "WdfIoQueueCreate failed with Status %!STATUS!", status);
        goto Exit;
    }

    // Construct a queue object on the preallocated buffer using placement new operation
    *Queue = new (GetQueueObject(queue)) CQueue(queue);

    status = (*Queue)->Initialize();
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "CQueue::Initialize failed with %!STATUS!", status);
        goto Exit;
    }
 
Exit:
    return status;
}

void
CQueue::OnIoDeviceControl(
    _In_ WDFREQUEST Request,
    _In_ ULONG IoControlCode,
    _In_ size_t InputBufferLength,
    _In_ size_t OutputBufferLength)
{
    TraceEvents(TRACE_LEVEL_INFORMATION,
                TRACE_QUEUE,
                "Request = %p, IoControlCode = 0x%x InputBufferLength=%d OutputBufferLength=%d",
                Request, IoControlCode, (int)InputBufferLength, (int)OutputBufferLength);

    NTSTATUS status = STATUS_SUCCESS;
    
    switch (IoControlCode)
    {
        // Capability Exchange
    case IOCTL_GNSS_SEND_PLATFORM_CAPABILITY:
        break;
    case IOCTL_GNSS_GET_DEVICE_CAPABILITY:
        status = GetDeviceCapability(Request);
        break;

    case IOCTL_GNSS_GET_CHIPSETINFO:
        status = GetChipsetInfo(Request);
        break;

        // Fix
    case IOCTL_GNSS_START_FIXSESSION:
        status = _FixHandler.StartFix(Request, _InternalState.SetLocationServiceEnabled);
        break;
    case IOCTL_GNSS_STOP_FIXSESSION:
        status = _FixHandler.StopFix(Request);
        break;
    case IOCTL_GNSS_MODIFY_FIXSESSION:
        status = _FixHandler.ModifyFix(Request);
        break;
    case IOCTL_GNSS_GET_FIXDATA:
        status = _FixHandler.GetFixRequest(Request);
        break;

        // Driver Command
    case IOCTL_GNSS_SEND_DRIVERCOMMAND:
        status = HandleDriverCommand(Request);
        break;

        //
        // FIX ME:
        // The below is optional IOCTLs. Driver developer may choose to implement for advanced functionality.
        //

        // Agnss
    case IOCTL_GNSS_INJECT_AGNSS:
    case IOCTL_GNSS_LISTEN_AGNSS:
        // Supl
    case IOCTL_GNSS_SET_SUPL_HSLP:
    case IOCTL_GNSS_CONFIG_SUPL_CERT:
    case IOCTL_GNSS_SET_V2UPL_CONFIG:
        //Ni
    case IOCTL_GNSS_LISTEN_DRIVER_REQUEST:
    case IOCTL_GNSS_RESPOND_NI:
    case IOCTL_GNSS_LISTEN_NI:
        //Geofence
    case IOCTL_GNSS_LISTEN_GEOFENCE_ALERT:
    case IOCTL_GNSS_LISTEN_GEOFENCES_TRACKINGSTATUS:
    case IOCTL_GNSS_CREATE_GEOFENCE:
    case IOCTL_GNSS_DELETE_GEOFENCE:
        // Breadcrumbing
    case IOCTL_GNSS_START_BREADCRUMBING:
    case IOCTL_GNSS_STOP_BREADCRUMBING:
    case IOCTL_GNSS_LISTEN_BREADCRUMBING_ALERT:
    case IOCTL_GNSS_POP_BREADCRUMBS:
        // Ioctls that are not implemented
    case IOCTL_GNSS_LISTEN_ERROR:
    case IOCTL_GNSS_EXECUTE_CWTEST:
    case IOCTL_GNSS_EXECUTE_SELFTEST:
    case IOCTL_GNSS_LISTEN_NMEA:
        status = STATUS_NOT_IMPLEMENTED;
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    if (status != STATUS_PENDING)
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Completing Request with status %!STATUS!", status);
        WdfRequestComplete(Request, status);
    }
}

NTSTATUS
CQueue::GetDeviceCapability(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PGNSS_DEVICE_CAPABILITY outputParam;

    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*outputParam), (void**)&outputParam, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Could not retrieve output buffer for request with status = %!STATUS!", status);
        goto Exit;
    }

    INIT_POD_GNSS_STRUCT(outputParam);

    //
    // FIX ME:
    // The functionality below is optional. Driver developer may choose to implement for advanced functionality/better performance.
    //
    {
        outputParam->SupportContinuousTracking = FALSE;
        
        outputParam->SupportDistanceTracking = FALSE;
        
        outputParam->GeofencingSupport = FALSE;
        outputParam->MaxGeofencesSupported = 0;
        
        outputParam->RequireAGnssInjection = FALSE;
        outputParam->AgnssFormatSupported = FALSE;
        outputParam->AgnssFormatPreferred = FALSE;

        outputParam->SupportCpLocation = FALSE;
        outputParam->SupportSuplV2 = FALSE;
        outputParam->SupportSuplV1 = FALSE;
        outputParam->SupportUplV2 = FALSE;
        outputParam->SupportedSuplVersion.MajorVersion = 0;
        outputParam->SupportedSuplVersion.MinorVersion = 0;

        outputParam->MaxGnssBreadCrumbFixes = 0;
        outputParam->GnssBreadCrumbPayloadVersion = BREADCRUMBING_UNSUPPORTED;

        // Update the Driver version if forced through IOCTL
        outputParam->Version = _ForcedDriverVersion;
    }

    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(*outputParam));
    status = STATUS_PENDING;

Exit:
    return status;
}

NTSTATUS
CQueue::GetChipsetInfo(
    _In_ WDFREQUEST Request
)
{
    //
    // FIX ME:
    // The following is an example. Replace the values accordingly
    //
    static const wchar_t s_ManufacturerID[] = L"Microsoft Test";
    static const wchar_t s_HardwareID[] = L"UMDF Gnss";
    static const wchar_t s_FirmwareVersion[] = L"1.0.0.0";

    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PGNSS_CHIPSETINFO outputParam;

    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*outputParam), (void**)&outputParam, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Could not retrieve output buffer for request with status = %!STATUS!", status);
        goto Exit;
    }

    INIT_POD_GNSS_STRUCT(outputParam);
        
    memset(&(outputParam->ManufacturerID), 0, sizeof(outputParam->ManufacturerID));
    memset(&(outputParam->HardwareID), 0, sizeof(outputParam->HardwareID));
    memset(&(outputParam->FirmwareVersion), 0, sizeof(outputParam->FirmwareVersion));

    memcpy_s(&(outputParam->ManufacturerID), sizeof(outputParam->ManufacturerID), &s_ManufacturerID, sizeof(s_ManufacturerID));
    memcpy_s(&(outputParam->HardwareID), sizeof(outputParam->HardwareID), &s_HardwareID, sizeof(s_HardwareID));
    memcpy_s(&(outputParam->FirmwareVersion), sizeof(outputParam->FirmwareVersion), &s_FirmwareVersion, sizeof(s_FirmwareVersion));

    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(*outputParam));
    status = STATUS_PENDING;

Exit:
    return status;
}

NTSTATUS
CQueue::HandleDriverCommand(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    PGNSS_DRIVERCOMMAND_PARAM command = nullptr;

    // Check that there is enough room to output the returned GNSS_DEVICE_CAPABILITY
    status = WdfRequestRetrieveInputBuffer(Request, sizeof(*command), (void**)&command, nullptr);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Could not retrieve input buffer for DriverCommand request with status = %!STATUS!", status);
        goto Exit;
    }

    // Check that the sizes are well-formed
    if ((command->Size < (ULONG)FIELD_OFFSET(GNSS_DRIVERCOMMAND_PARAM, CommandData[command->CommandDataSize])))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "Command is not formatted correctly : unexpected sizes specified");
        goto Exit;
    }

    switch (command->CommandType)
    {
    case GNSS_ClearAgnssData:
        status = ClearAgnssData(command);
    break;
    case GNSS_ResetEngine:
        status = ResetEngine(command);
        break;
    case GNSS_SetLocationServiceEnabled:
        status = SetLocationServiceStatus(command);
        break;
    case GNSS_ForceOperationMode:
        status = SetOperationMode(command);
        break;

    //
    // FIX ME:
    // Below are optional driver commands. Driver developer may choose to implement for advanced functionality.
    //
    case GNSS_SetLocationNIRequestAllowed:
    case GNSS_ResetGeofencesTracking:
    case GNSS_SetNiTimeoutInterval:
    case GNSS_SetSuplVersion:
    case GNSS_SetUplServerAccessInterval:
        status = STATUS_NOT_IMPLEMENTED;
        break;
    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

Exit:
    return status;
}

NTSTATUS
CQueue::SetLocationServiceStatus(
    _In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam
)
{
    NTSTATUS status = STATUS_SUCCESS;

    status = GetDataFromCommand(CommandParam, &_InternalState.SetLocationServiceEnabled) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
    
    if (!_InternalState.SetLocationServiceEnabled)
    {
        //
        // FIX ME:
        // We must stop any ongoing location sessions of any kind (geofence tracking, assistance data refresh, etc. if any).
        // For example, if you have distance tracking fix session, stop the session as well.
        //
        status = _FixHandler.StopExistingSinglsShotFix();
    }

    return status;
}

NTSTATUS
CQueue::ResetEngine(
    _In_ PGNSS_DRIVERCOMMAND_PARAM /*CommandParam*/
)
{
    NTSTATUS status = STATUS_SUCCESS;

    //
    // FIX ME:
    // This is mandatory command, but sample driver cannot support Reset engine command because this is vendor/HW-specific feature.
    // You must customize this accordingly. The sample below demonstrates how it might work by resetting internal structure.
    //
    _AgnssNeeded[AGNSS_TIME_IDX] = TRUE;
    _AgnssNeeded[AGNSS_POS_IDX] = TRUE;
    _AgnssNeeded[AGNSS_BLOB_IDX] = TRUE;

    return status;
}

NTSTATUS
CQueue::ClearAgnssData(
    _In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam
)
{
    NTSTATUS status = STATUS_SUCCESS;
    DWORD data = 0;
    
    //
    // FIX ME:
    // This is mandatory command, but sample driver cannot support Clear AGNSS data because this is vendor/HW-specific feature.
    // You must customize this accordingly. The sample below demonstrates how it might work by resetting internal structure.
    //
    if (GetDataFromCommand(CommandParam, &data))
    {
        GNSS_AGNSS_REQUEST_TYPE reqType = (GNSS_AGNSS_REQUEST_TYPE)data;
        _AgnssNeeded[reqType] = TRUE;
    }
    else
    {
        status = STATUS_UNSUCCESSFUL;
    }

    return status;
}

NTSTATUS
CQueue::SetOperationMode(
    _In_ PGNSS_DRIVERCOMMAND_PARAM CommandParam
)
{
    NTSTATUS status = STATUS_SUCCESS;
 
    //
    // FIX ME:
    // This is mandatory only if SUPL is supported. SUPL is not mandatory unless it is required by mobile operator.
    // You must customize this code accordingly, depending on the requirement.
    //
    status = GetDataFromCommand(CommandParam, &_InternalState.OperationMode) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;

    return status;
}

template <class DataT>
BOOL CQueue::GetDataFromCommand(
    _In_ PGNSS_DRIVERCOMMAND_PARAM Command,
    _Out_ DataT* Data)
{
    size_t dataLen = sizeof(*Data);

    memset(Data, 0, dataLen);

    if (!Command || (Command->CommandDataSize != dataLen))
    {
        return FALSE;
    }

    memcpy_s(Data, dataLen, Command->CommandData, dataLen);
    return TRUE;
}


<|start_filename|>general/SimpleMediaSource/SimpleMediaSourceDriver/Driver.h<|end_filename|>
/*++

Module Name:

    driver.h

Abstract:

    This file contains the driver definitions.

Environment:

    User-mode Driver Framework 2

--*/

#include <windows.h>
#include <wdf.h>
#include <initguid.h>
#include <ks.h>
#include <ksmedia.h>

#include "device.h"
#include "queue.h"
#include "trace.h"

EXTERN_C_START

//
// WDFDRIVER Events
//

DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD SimpleMediaSourceDriverEvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP SimpleMediaSourceDriverEvtDriverContextCleanup;

EXTERN_C_END


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/sinewave.h<|end_filename|>
#pragma once

class SineWave
{
    struct wave
    {
        float amp{}, freq{}, phase{}, dc{};
    };
public:
    SineWave() {}

    template<typename _ty1, typename _ty2>
    SineWave(_ty1 numChannels, _ty2 samplingRate) : _channel(unsigned(numChannels)), _sr(float(samplingRate)) {}

    template<typename _ty1, typename _ty2>
    inline void Initialize(_ty1 numChannels, _ty2 samplingRate)
    {
        _channel.resize(unsigned(numChannels));
        _sr = unsigned(samplingRate);
    }

    template<typename _ty1>
    inline void SetChannel(_ty1 channel, float freq, float amplitude = 0.f, float dc = 0.f, float phase = 0.f)
    {
        _channel[unsigned(channel)].amp = amplitude;
        _channel[unsigned(channel)].freq = freq;
        _channel[unsigned(channel)].phase = phase;
        _channel[unsigned(channel)].dc = dc;
    }


    template<typename _ty1>
    void FillFrames(void* buffer, _ty1 numFrames)
    {
        auto fb = reinterpret_cast<float*>(buffer);
        for (unsigned i = 0; i < unsigned(numFrames); ++i)
        {
            for (unsigned ch = 0; ch < _channel.size(); ++ch)
            {
                fb[i * _channel.size() + ch] = Calculate(ch);
            }
        }
    }

protected:
    float Calculate(unsigned ch)
    {
        auto& w = _channel[ch];
        float sample = w.amp * sinf(w.phase) + w.dc;

        w.phase += 2 * float(M_PI) * w.freq / _sr;
        w.phase = fmodf(w.phase, 2 * float(M_PI));

        return sample;
    }

private:
    std::vector<wave> _channel;
    float _sr{};
};

<|start_filename|>audio/sysvad/EndpointsCommon/a2dphpspeakertoptable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphpspeakertoptable.h

Abstract:

    Declaration of topology tables for the A2DP Headphone speaker (external).

--*/

#ifndef _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_
#define _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_

#include "a2dphpspeakertopo.h"

//=============================================================================
static
KSDATARANGE A2dpHpSpeakerTopoPinDataRangesBridge[] =
{
 {
   sizeof(KSDATARANGE),
   0,
   0,
   0,
   STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
   STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
   STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
 }
};

//=============================================================================
static
PKSDATARANGE A2dpHpSpeakerTopoPinDataRangePointersBridge[] =
{
  &A2dpHpSpeakerTopoPinDataRangesBridge[0]
};

//=============================================================================
static
PCPIN_DESCRIPTOR A2dpHpSpeakerTopoMiniportPins[] =
{
  // KSPIN_TOPO_WAVEOUT_SOURCE
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(A2dpHpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      A2dpHpSpeakerTopoPinDataRangePointersBridge,      // DataRanges
      KSPIN_DATAFLOW_IN,                                // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSCATEGORY_AUDIO,                                // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  },
  // KSPIN_TOPO_LINEOUT_DEST
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(A2dpHpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      A2dpHpSpeakerTopoPinDataRangePointersBridge,      // DataRanges
      KSPIN_DATAFLOW_OUT,                               // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSNODETYPE_HEADSET_SPEAKERS,                     // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  }
};

//=============================================================================
static
KSJACK_DESCRIPTION A2dpHpSpeakerJackDesc =
{
    KSAUDIO_SPEAKER_STEREO,
    0,                      // color
    eConnTypeOtherDigital,
    eGeoLocBottom,
    eGenLocPrimaryBox,
    ePortConnJack,
    FALSE                   // run-time code sets this value.
};

// Only return a KSJACK_DESCRIPTION for the physical bridge pin.
static 
PKSJACK_DESCRIPTION A2dpHpSpeakerJackDescriptions[] =
{
    NULL,
    &A2dpHpSpeakerJackDesc
};

//=============================================================================
static
PCCONNECTION_DESCRIPTOR A2dpHpSpeakerTopoMiniportConnections[] =
{
  //  FromNode,             FromPin,                    ToNode,                 ToPin
  {   PCFILTER_NODE,        KSPIN_TOPO_WAVEOUT_SOURCE,  PCFILTER_NODE,          KSPIN_TOPO_LINEOUT_DEST }
};

//=============================================================================
static
PCPROPERTY_ITEM A2dpHpSpeakerPropertiesTopoFilter[] =
{
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpSpeakerTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION2,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpSpeakerTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_CONTAINERID,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpSpeakerTopoFilter
    },
    {
        &KSPROPSETID_BtAudio,
        KSPROPERTY_ONESHOT_RECONNECT,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpSpeakerTopoFilter
    },
    {
        &KSPROPSETID_BtAudio,
        KSPROPERTY_ONESHOT_DISCONNECT,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpSpeakerTopoFilter
    }
};

static 
PCEVENT_ITEM A2dpHpSpeakerPropertiesTopoFilterEvent[] =
{
  {
    &KSEVENTSETID_PinCapsChange,                        // Something changed!
    KSEVENT_PINCAPS_JACKINFOCHANGE,                     // The only event-property defined.
    KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
    PropertyHandler_A2dpHpSpeakerTopoFilterEvent
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationA2dpHpSpeakerTopoFilterWithEvent, 
                               A2dpHpSpeakerPropertiesTopoFilter,
                               A2dpHpSpeakerPropertiesTopoFilterEvent);

//=============================================================================
static
PCFILTER_DESCRIPTOR A2dpHpSpeakerTopoMiniportFilterDescriptor =
{
  0,                                                    // Version
  &AutomationA2dpHpSpeakerTopoFilterWithEvent,          // AutomationTable
  sizeof(PCPIN_DESCRIPTOR),                             // PinSize
  SIZEOF_ARRAY(A2dpHpSpeakerTopoMiniportPins),          // PinCount
  A2dpHpSpeakerTopoMiniportPins,                        // Pins
  sizeof(PCNODE_DESCRIPTOR),                            // NodeSize
  0,                                                    // NodeCount
  NULL,                                                 // Nodes
  SIZEOF_ARRAY(A2dpHpSpeakerTopoMiniportConnections),   // ConnectionCount
  A2dpHpSpeakerTopoMiniportConnections,                 // Connections
  0,                                                    // CategoryCount
  NULL                                                  // Categories
};

#endif // _SYSVAD_A2DPHPSPEAKERTOPTABLE_H_
    



<|start_filename|>general/SimpleMediaSource/SimpleMediaSourceDriver/Device.h<|end_filename|>
/*++

Module Name:

    device.h

Abstract:

    This file contains the device definitions.

Environment:

    User-mode Driver Framework 2

--*/

#include "public.h"

EXTERN_C_START

//
// The device context performs the same job as
// a WDM device extension in the driver frameworks
//
typedef struct _DEVICE_CONTEXT
{
    ULONG PrivateDeviceData;  // just a placeholder

} DEVICE_CONTEXT, *PDEVICE_CONTEXT;

//
// This macro will generate an inline function called DeviceGetContext
// which will be used to get a pointer to the device context memory
// in a type safe manner.
//
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext)

//
// Function to initialize the device and its callbacks
//
NTSTATUS
SimpleMediaSourceDriverCreateDevice(
    _Inout_ PWDFDEVICE_INIT DeviceInit
    );

EXTERN_C_END


<|start_filename|>general/ioctl/wdm/exe/testapp.c<|end_filename|>
/*++

Copyright (c) 1990-98  Microsoft Corporation All Rights Reserved

Module Name:

    testapp.c

Abstract:

Environment:

    Win32 console multi-threaded application

--*/
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strsafe.h>
#include <sys\sioctl.h>


BOOLEAN
ManageDriver(
    _In_ LPCTSTR  DriverName,
    _In_ LPCTSTR  ServiceName,
    _In_ USHORT   Function
    );

BOOLEAN
SetupDriverName(
    _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation,
    _In_ ULONG BufferLength
    );

char OutputBuffer[100];
char InputBuffer[100];

VOID __cdecl
main(
    _In_ ULONG argc,
    _In_reads_(argc) PCHAR argv[]
    )
{
    HANDLE hDevice;
    BOOL bRc;
    ULONG bytesReturned;
    DWORD errNum = 0;
    TCHAR driverLocation[MAX_PATH];

    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    //
    // open the device
    //

    if ((hDevice = CreateFile( "\\\\.\\IoctlTest",
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            NULL,
                            CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL)) == INVALID_HANDLE_VALUE) {

        errNum = GetLastError();

        if (errNum != ERROR_FILE_NOT_FOUND) {

            printf("CreateFile failed : %d\n", errNum);

            return ;
        }

        //
        // The driver is not started yet so let us the install the driver.
        // First setup full path to driver name.
        //

        if (!SetupDriverName(driverLocation, sizeof(driverLocation))) {

            return ;
        }

        if (!ManageDriver(DRIVER_NAME,
                          driverLocation,
                          DRIVER_FUNC_INSTALL
                          )) {

            printf("Unable to install driver.\n");

            //
            // Error - remove driver.
            //

            ManageDriver(DRIVER_NAME,
                         driverLocation,
                         DRIVER_FUNC_REMOVE
                         );

            return;
        }

        hDevice = CreateFile( "\\\\.\\IoctlTest",
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            NULL,
                            CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL);

        if ( hDevice == INVALID_HANDLE_VALUE ){
            printf ( "Error: CreatFile Failed : %d\n", GetLastError());
            return;
        }

    }

    //
    // Printing Input & Output buffer pointers and size
    //

    printf("InputBuffer Pointer = %p, BufLength = %Iu\n", InputBuffer,
                        sizeof(InputBuffer));
    printf("OutputBuffer Pointer = %p BufLength = %Iu\n", OutputBuffer,
                                sizeof(OutputBuffer));
    //
    // Performing METHOD_BUFFERED
    //

    StringCbCopy(InputBuffer, sizeof(InputBuffer),
        "This String is from User Application; using METHOD_BUFFERED");

    printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");

    memset(OutputBuffer, 0, sizeof(OutputBuffer));

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_SIOCTL_METHOD_BUFFERED,
                            &InputBuffer,
                            (DWORD) strlen ( InputBuffer )+1,
                            &OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d", GetLastError());
        return;

    }
    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);

    //
    // Performing METHOD_NEITHER
    //

    printf("\nCalling DeviceIoControl METHOD_NEITHER\n");

    StringCbCopy(InputBuffer, sizeof(InputBuffer),
               "This String is from User Application; using METHOD_NEITHER");
    memset(OutputBuffer, 0, sizeof(OutputBuffer));

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_SIOCTL_METHOD_NEITHER,
                            &InputBuffer,
                            (DWORD) strlen ( InputBuffer )+1,
                            &OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d\n", GetLastError());
        return;

    }

    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);

    //
    // Performing METHOD_IN_DIRECT
    //

    printf("\nCalling DeviceIoControl METHOD_IN_DIRECT\n");

    StringCbCopy(InputBuffer, sizeof(InputBuffer),
               "This String is from User Application; using METHOD_IN_DIRECT");
    StringCbCopy(OutputBuffer, sizeof(OutputBuffer),
               "This String is from User Application in OutBuffer; using METHOD_IN_DIRECT");

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_SIOCTL_METHOD_IN_DIRECT,
                            &InputBuffer,
                            (DWORD) strlen ( InputBuffer )+1,
                            &OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d", GetLastError());
        return;
    }

    printf("    Number of bytes transfered from OutBuffer: %d\n",
                                    bytesReturned);

    //
    // Performing METHOD_OUT_DIRECT
    //

    printf("\nCalling DeviceIoControl METHOD_OUT_DIRECT\n");
    StringCbCopy(InputBuffer, sizeof(InputBuffer),
               "This String is from User Application; using METHOD_OUT_DIRECT");
    memset(OutputBuffer, 0, sizeof(OutputBuffer));
    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_SIOCTL_METHOD_OUT_DIRECT,
                            &InputBuffer,
                            (DWORD) strlen ( InputBuffer )+1,
                            &OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d", GetLastError());
        return;
    }

    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);

    CloseHandle ( hDevice );

    //
    // Unload the driver.  Ignore any errors.
    //

    ManageDriver(DRIVER_NAME,
                 driverLocation,
                 DRIVER_FUNC_REMOVE
                 );


    //
    // close the handle to the device.
    //

}




<|start_filename|>avstream/avscamera/sys/CameraProfile.h<|end_filename|>
/**************************************************************************

    AvsCam - An AVStream Simulated Camera Device.

    Copyright (c) 2014, Microsoft Corporation.

    File:

        CameraProfile.h

    Abstract:

        This file contains the helper class CExtendedProfile.

    History:

        created 3/19/2015

**************************************************************************/

//
//  Helper class for CameraProfile
//
class CExtendedProfile : public CExtendedHeader
{
public:
    KSCAMERA_EXTENDEDPROP_PROFILE   m_Profile;

public:
    CExtendedProfile( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS )
        : CExtendedHeader( flags, result )
    {
        Size = sizeof(*this);
        m_Profile.ProfileId = KSCAMERAPROFILE_Legacy;
        m_Profile.Index = 0;
        m_Profile.Reserved = 0;
    }

    CExtendedProfile( KSCAMERA_EXTENDEDPROP_HEADER &hdr )
        : CExtendedHeader(hdr)
    {
        Size = sizeof(*this);
        m_Profile.ProfileId = KSCAMERAPROFILE_Legacy;
        m_Profile.Index = 0;
        m_Profile.Reserved = 0;
    }

    bool isValid()
    {
        return CExtendedHeader::isValid() && (Size >= sizeof(*this));
    }
};




<|start_filename|>avstream/sampledevicemft/custompin.cpp<|end_filename|>
#include "stdafx.h"
#include "multipinmft.h"
#include "basepin.h"
#include "custompin.h"
#ifdef MF_WPP
#include "custompin.tmh"    //--REF_ANALYZER_DONT_REMOVE--
#endif


//
// Implementation notes
// Implement this file only if you have custom media pins streaming 
// This implementation just streams from the driver and sends it back
// to the pipeline. This method can be used to stream 3A/Statistic Pins. 
// 

CCustomPin::CCustomPin(
    _In_ IMFAttributes *pAttributes,
    _In_ ULONG PinId,
    _In_ CMultipinMft *pParent
    )
    :CInPin( pAttributes, PinId, pParent )
{

}
//
// Process input calls the pin here.. In this code sample we
// don't send it anywhere. It is simply shortcircuited back to the pipeline
//
STDMETHODIMP CCustomPin::SendSample(
    _In_ IMFSample *pSample
    )
{
    //
    // Log sample and exit.. The pipeline will just keep on churning more samples till 
    // We go into the stop state
    //
    DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Custom Pin %d received Sample %p", streamId(), pSample);

    return S_OK;
}

//
// State change transitions on the custom pins are different than the other pins
// The other known pins i.e. preview, record, image etc are exposed out to the pipeline
// and the pipeline will set state on it, reset state, For the other pins we tell the 
// pipeline that we have samples and the pipeline picks it up. 
//
// The CUSTOM pin differs in that it's state has to be managed to by the device MFT. 
// This code snippet just varies state transitions on the custom pin when the Device MFT
// changes state to STREAMING/END_STREAMING on it's exposed pins. 
//
// The below function tells the pipeline to change the state on the custom pin by sending
// METransformInputStreamStateChanged event with the streamid attribute. The pipeline will
// call back in the custom pin at getprefferedmediatype and finally setinputstreamstate
// which will signal back here singnalling sucessful state transition.
//

STDMETHODIMP_(DeviceStreamState) CCustomPin::SetState(
    _In_ DeviceStreamState State
    )
{
    HRESULT hr = S_OK;
    
    DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Id:%d Transition into state: %d ", streamId(), State);

    DeviceStreamState oldState = setPreferredStreamState( State );

    if ( oldState != State )
    {
        ComPtr<IMFMediaType> preferredMediaType = nullptr;

        if (State == DeviceStreamState_Run)
        {
            GetMediaTypeAt(0, preferredMediaType.ReleaseAndGetAddressOf());
        }

        setPreferredMediaType(preferredMediaType.Get());
        setPreferredStreamState( State );
        
        {
            //
            // Notify the device transform manager that we have changed state
            //
            ComPtr<IMFMediaEvent> pEvent = nullptr;
            DMFTCHECKHR_GOTO( MFCreateMediaEvent(METransformInputStreamStateChanged, GUID_NULL, S_OK, NULL, pEvent.ReleaseAndGetAddressOf()), done );
            DMFTCHECKHR_GOTO( pEvent->SetUINT32(MF_EVENT_MFT_INPUT_STREAM_ID, streamId()), done );
            DMFTCHECKHR_GOTO( Parent()->QueueEvent(pEvent.Get()), done );
        }
        //
        // Wait to be notified back from the pipeline. 
        //
        DMFTCHECKHR_GOTO( WaitForSetInputPinMediaChange(),done );
    }
done:
    DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr);
    return oldState;
}


<|start_filename|>audio/sysvad/A2dpHpDevice.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    A2dpHpDevice.cpp

Abstract:

    Implementation of the A2dpHpDevice class.

--*/

#pragma warning (disable : 4127)

#include <initguid.h>
#include <sysvad.h>
#include "hw.h"
#include "savedata.h"
#include "IHVPrivatePropertySet.h"
#include "simple.h"

#ifdef SYSVAD_A2DP_SIDEBAND
#include <limits.h>
#include <SidebandAudio.h>
#include <A2DPSidebandAudio.h>
#include <wdmguid.h>    // guild-arrival/removal
#include <devpkey.h>
#include "a2dphpminipairs.h"
#include "A2dpHpDevice.h"
#include "A2dpHpDeviceFormats.h"

//
// A2dpHpDevice implementation.
//

// # of sec before sync request is cancelled.
#define A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC         60 
#define A2DP_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT    5

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP
A2dpHpDevice::NonDelegatingQueryInterface
( 
    _In_ REFIID                 Interface,
    _COM_Outptr_ PVOID *        Object 
)
/*++

Routine Description:

  QueryInterface routine for A2dpHpDevice

Arguments:

  Interface - 

  Object -

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(Object);

    if (IsEqualGUIDAligned(Interface, IID_IUnknown))
    {
        *Object = PVOID(PUNKNOWN(PSIDEBANDDEVICECOMMON(this)));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IA2dpHpDeviceCommon))
    {
        *Object = PVOID(PSIDEBANDDEVICECOMMON(this));
    }
    else
    {
        *Object = NULL;
    }

    if (*Object)
    {
        PUNKNOWN(*Object)->AddRef();
        return STATUS_SUCCESS;
    }

    return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::Init
(
    _In_ IAdapterCommon     * Adapter, 
    _In_ PUNICODE_STRING      SymbolicLinkName
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                                ntStatus        = STATUS_SUCCESS;
    A2dpHpDeviceNotificationWorkItemContext *wiCtx          = NULL;
    A2dpHpDeviceNotificationReqContext      *reqCtx         = NULL;
    WDF_OBJECT_ATTRIBUTES                   attributes;
    WDF_IO_TARGET_OPEN_PARAMS               openParams;
    WDF_WORKITEM_CONFIG                     wiConfig;

    AddRef(); // first ref.
    
    //
    // Basic init of all the class' members.
    //
    m_State                         = eA2dpHpStateInitializing;
    m_Adapter                       = Adapter;

    // Static config.
    m_WdfIoTarget                   = NULL;
    m_SpeakerMiniports              = NULL;
    m_UnknownSpeakerTopology        = NULL;
    m_UnknownSpeakerWave            = NULL;
    m_Descriptor                    = NULL;
    m_SpeakerVolumePropValues       = NULL;
    m_SpeakerMutePropValues         = NULL;

    // Notification updates. 
    m_SpeakerVolumeLevel            = 0;
    m_SpeakerMute                   = 0;
    RtlZeroMemory(&m_ConnectedCodecCaps, sizeof(A2DPHPDEVICE_CODEC_CAPABILITIES));
    ExInitializeFastMutex(&m_ConnectedCodecLock);
    m_ConnectionStatusLong          = FALSE;
    m_SpeakerStreamStatusLong       = STATUS_SUCCESS; // A2DP stream is not open.
        
    m_SpeakerStreamReq              = NULL;
    m_SpeakerVolumeReq              = NULL;
    m_SpeakerMuteReq                = NULL;
    m_ConnectionReq                 = NULL;
    m_SiopUpdateReq                 = NULL;

    m_WorkItem                      = NULL;
    m_ReqCollection                 = NULL;
    
    m_nSpeakerStreams               = 0;
    m_nSpeakerStartedStreams        = 0;

    m_pSpeakerSupportedFormatsIntersection  = NULL;

    KeInitializeEvent(&m_SpeakerStreamStatusEvent, NotificationEvent, TRUE);

    InitializeListHead(&m_ListEntry);
    KeInitializeSpinLock(&m_Lock);
    
    RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));
    
    RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback));
    RtlZeroMemory(&m_SpeakerMuteCallback, sizeof(m_SpeakerMuteCallback));
    RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback));
    RtlZeroMemory(&m_SpeakerFormatChangeCallback, sizeof(m_SpeakerFormatChangeCallback));

    RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES));
    
    //
    // Allocate a notification WDF work-item.
    //
    WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtA2dpHpDeviceNotificationStatusWorkItem);
    wiConfig.AutomaticSerialization = FALSE;

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationWorkItemContext);
    attributes.ParentObject = Adapter->GetWdfDevice();
    ntStatus = WdfWorkItemCreate( &wiConfig,
                                  &attributes,
                                  &m_WorkItem);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfWorkItemCreate failed: 0x%x", ntStatus)),
        Done);

    wiCtx = GetA2dpHpDeviceNotificationWorkItemContext(m_WorkItem);
    wiCtx->A2dpHpDevice = this; // weak ref.

    //
    // Allocate a collection to hold notification requests for the notification work-item.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfCollectionCreate(
        &attributes,
        &m_ReqCollection);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfCollectionCreate failed: 0x%x", ntStatus)),
        Done);
    
    //
    // Open the target interface.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(),
                                 &attributes,
                                 &m_WdfIoTarget);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfIoTargetCreate failed: 0x%x", ntStatus)),
        Done);

    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
        &openParams,
        SymbolicLinkName,
        STANDARD_RIGHTS_ALL);

    ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)),
        Done);
    
    //
    // Make a copy of the symbolic link name.
    //
    m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength;
    m_SymbolicLinkName.Length = SymbolicLinkName->Length;
    m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePool2(POOL_FLAG_NON_PAGED,
                                                        SymbolicLinkName->MaximumLength,
                                                        MINADAPTER_POOLTAG);
    if (m_SymbolicLinkName.Buffer == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);
    
    RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName);

    //
    // Allocate the WDF requests for status notifications.
    //

    //
    // IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE 
    // SPEAKER
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_SpeakerVolumeReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq);
    reqCtx->A2dpHpDevice = this; // weak ref.
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Volume),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Volume),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);
    
    //
    // IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE 
    // SPEAKER
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_SpeakerMuteReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Speaker-Mute) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq);
    reqCtx->A2dpHpDevice = this; // weak ref.
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Mute),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Mute),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_ConnectionReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Connection-Status) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq);
    reqCtx->A2dpHpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.ConnectionStatus),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.ConnectionStatus),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);
    
    //
    // IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_SpeakerStreamReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)),
        Done);
    
    // Init context.
    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq);
    reqCtx->A2dpHpDevice = this; // weak ref.
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.StreamStatus),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);
    
    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.StreamStatus),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_SBAUD_GET_SIOP_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, A2dpHpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_SiopUpdateReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate(SIOP Update) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq);
    reqCtx->A2dpHpDevice = this;

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.SiopUpdate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC! WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.SiopUpdate),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC! WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // Create wave and filter names unique to this HFP device
    //
    ntStatus = CreateFilterNames(SymbolicLinkName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: Creating unique filter names for the A2DP device failed, 0x%x", ntStatus)),
        Done);

   //
   // This remote device is now in running state. No need to use interlock operations
   // b/c at this time this is the only thread accessing this info.
   //
   m_State = eA2dpHpStateRunning;
   
   //
   // Init successful.
   //
   ntStatus = STATUS_SUCCESS;

Done:
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
A2dpHpDevice::~A2dpHpDevice
( 
    void 
)
/*++

Routine Description:

  Destructor for A2dpHpDevice.

Arguments:

Return Value:

  void

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    ASSERT(m_State != eA2dpHpStateRunning);
    ASSERT(IsListEmpty(&m_ListEntry));
    
    //
    // Release ref to remote stack.
    //
    if (m_WdfIoTarget != NULL)
    {
        WdfObjectDelete(m_WdfIoTarget);
        m_WdfIoTarget = NULL;
    }

    //
    // Free symbolic links.
    //
    if (m_SymbolicLinkName.Buffer != NULL)
    {
        ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG);
        RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));
    }

    DeleteCustomEndpointMinipair(m_SpeakerMiniports);
    m_SpeakerMiniports = NULL;

    if (m_Descriptor != NULL)
    {
        ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG);
        m_Descriptor = NULL;
    }

    SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerDescriptor, A2DPSIDEBANDTEST_POOLTAG02);

    SAFE_DELETE_PTR_WITH_TAG(m_pSpeakerSupportedFormatsIntersection, A2DPSIDEBANDTEST_POOLTAG03);

    RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES));

    if (m_SpeakerVolumePropValues != NULL)
    {
        ExFreePoolWithTag(m_SpeakerVolumePropValues, A2DPSIDEBANDTEST_POOLTAG02);
        m_SpeakerVolumePropValues = NULL;
    }

    if (m_SpeakerMutePropValues != NULL)
    {
        ExFreePoolWithTag(m_SpeakerMutePropValues, A2DPSIDEBANDTEST_POOLTAG06);
        m_SpeakerMutePropValues = NULL;
    }

    if (m_ConnectedCodecCaps)
    {
        ExFreePoolWithTag(m_ConnectedCodecCaps, A2DPSIDEBANDTEST_POOLTAG07);
        m_ConnectedCodecCaps = NULL;
    }

    //
    // Free Irps.
    //
    if (m_SpeakerVolumeReq != NULL)
    {
        A2dpHpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }
        
        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_SpeakerVolumeReq);
        m_SpeakerVolumeReq = NULL;
    }

    if (m_SpeakerMuteReq != NULL)
    {
        A2dpHpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }
        
        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_SpeakerMuteReq);
        m_SpeakerMuteReq = NULL;
    }

    if (m_ConnectionReq != NULL)
    {
        A2dpHpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_ConnectionReq);
        m_ConnectionReq = NULL;
    }
    
    if (m_SpeakerStreamReq != NULL)
    {
        A2dpHpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }
        
        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_SpeakerStreamReq);
        m_SpeakerStreamReq = NULL;
    }

    if (m_SiopUpdateReq != NULL)
    {
        A2dpHpDeviceNotificationReqContext* ctx;

        // Delete the associated memory objects.
        ctx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_SiopUpdateReq);
        m_SiopUpdateReq = NULL;
    }

    //
    // Notification work-item.
    //
    if (m_WorkItem != NULL)
    {
        WdfObjectDelete(m_WorkItem);
        m_WorkItem = NULL;
    }

    //
    // Notification req. collection.
    //
    if (m_ReqCollection != NULL)
    {
        WdfObjectDelete(m_ReqCollection);
        m_ReqCollection = NULL;
    }

    ASSERT(m_UnknownSpeakerTopology == NULL);
    SAFE_RELEASE(m_UnknownSpeakerTopology);
    
    ASSERT(m_UnknownSpeakerWave == NULL);
    SAFE_RELEASE(m_UnknownSpeakerWave);
    
    ASSERT(m_nSpeakerStreams == 0);

    ASSERT(m_SpeakerVolumeCallback.Handler == NULL);
    ASSERT(m_SpeakerMuteCallback.Handler == NULL);
    ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL);
    ASSERT(m_SpeakerFormatChangeCallback.Handler == NULL);
} // ~A2dpHpDevice  

//
// ISidebandDeviceCommon implementation. 
//

//=============================================================================
#pragma code_seg("PAGE")
BOOL
A2dpHpDevice::IsVolumeSupported
(
    _In_ eDeviceType deviceType
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return m_pSpeakerDescriptor->Capabilities.Volume;
    }

    return FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
PVOID
A2dpHpDevice::GetVolumeSettings
(
    _In_  eDeviceType deviceType,
    _Out_ PULONG    Size 
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    ASSERT(Size != NULL);
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        *Size = m_pSpeakerDescriptor->VolumePropertyValuesSize;
        return m_SpeakerVolumePropValues;
    }

    return NULL;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::GetVolume
(
    _In_    eDeviceType     deviceType,
    _In_    LONG            Channel,
    _Out_   LONG            *pVolume
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(Channel);
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS status = STATUS_SUCCESS;

    ASSERT(pVolume);

    if (eA2dpHpSpeakerDevice == deviceType)
    {
        status = GetA2dpHpSpeakerVolume(Channel, pVolume);
        IF_FAILED_ACTION_JUMP(
            status,
            DPF(D_ERROR, ("Start: GetA2dpHpSpeakerVolume: failed, 0x%x", status)),
            Done);

        InterlockedExchange(&m_SpeakerVolumeLevel, *pVolume);
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }

Done:
    return status;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SetVolume
(
    _In_    eDeviceType     deviceType,
    _In_    LONG            Channel,
    _In_    LONG            Volume
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS status = STATUS_SUCCESS;

    if (eA2dpHpSpeakerDevice == deviceType)
    {
        status = SetA2dpHpSpeakerVolume(Channel, Volume);
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }
    
    return status;
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
A2dpHpDevice::IsMuteSupported
(
    _In_ eDeviceType deviceType
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return m_pSpeakerDescriptor->Capabilities.Mute;
    }

    return FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
PVOID
A2dpHpDevice::GetMuteSettings
(
    _In_  eDeviceType deviceType,
    _Out_ PULONG    Size 
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    ASSERT(Size != NULL);
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        *Size = m_pSpeakerDescriptor->MutePropertyValuesSize;
        return m_SpeakerMutePropValues;
    }

    return NULL;
}

//=============================================================================
#pragma code_seg("PAGE")
LONG
A2dpHpDevice::GetMute
(
    _In_    eDeviceType     deviceType,
    _In_    LONG            Channel
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(Channel);
    DPF_ENTER(("[%!FUNC!]"));

    LONG ulMute = 0;
    NTSTATUS status = STATUS_SUCCESS;

    if (eA2dpHpSpeakerDevice == deviceType)
    {
        status = GetA2dpHpSpeakerMute(Channel, &ulMute);
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }

    if (NT_SUCCESS(status))
    {
        return ulMute;
    }

    return 0;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SetMute
(
    _In_    eDeviceType     deviceType,
    _In_    LONG            Channel,
    _In_    LONG            Mute
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS status = STATUS_SUCCESS;

    if (eA2dpHpSpeakerDevice == deviceType)
    {
        status = SetA2dpHpSpeakerMute(Channel, Mute);
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }
    
    return status;
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
A2dpHpDevice::GetConnectionStatus()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0);
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::GetA2dpHpCodecCaps
(
    _Outptr_ PA2DPHPDEVICE_CODEC_CAPABILITIES* ppCodecCaps
)
{
    PAGED_CODE();
    DPF_ENTER(("[A2dpHpDevice::GetA2dpHpCodecCaps]"));

    ASSERT(ppCodecCaps != NULL);

    NTSTATUS ntStatus = STATUS_SUCCESS;
    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_REQUEST_REUSE_PARAMS reuseParams;
    WDFREQUEST req = NULL;
    ULONG_PTR information = 0;
    PBYTE pOutput = NULL;
    SIDEBANDAUDIO_SIOP_REQUEST_PARAM rp = { 0 };

    *ppCodecCaps = NULL;    
    rp.EpIndex = m_SpeakerEpIndex;
    rp.RequestedSiop.ParamSet   = SIDEBANDAUDIO_PARAMS_SET_A2DP;
    rp.RequestedSiop.TypeId     = SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC;
    rp.RequestedSiop.Size       = 0;

    //
    // Allocate and format a WDF request.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &req);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Get the SIOP buffer size.
    //
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_SIOP,
        sizeof(rp),
        0,
        &rp,
        NULL);

    if (ntStatus == STATUS_BUFFER_TOO_SMALL)
    {
        information = WdfRequestGetInformation(req);
        if (information == 0 || information > ULONG_MAX)
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
            DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SIOP invalid buffer size (%Id): 0x%x", information, ntStatus));
            goto Done;
        }

        rp.RequestedSiop.Size = (ULONG)information;
        ntStatus = STATUS_SUCCESS;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetA2dpHpCodecCaps: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP, SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC) failed, 0x%x", ntStatus)),
        Done);

    pOutput = (PBYTE)ExAllocatePool2(POOL_FLAG_NON_PAGED, rp.RequestedSiop.Size, A2DPSIDEBANDTEST_POOLTAG07);
    if (pOutput == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetA2dpHpCodecCaps: out of memory")),
        Done);

    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);
    ntStatus = WdfRequestReuse(req, &reuseParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)),
        Done);

    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_SIOP,
        sizeof(rp),
        rp.RequestedSiop.Size,
        &rp,
        pOutput);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetA2dpHpCodecCaps: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SIOP, SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *ppCodecCaps = (PA2DPHPDEVICE_CODEC_CAPABILITIES)pOutput;
Done:
    if (req != NULL)
    {
        WdfObjectDelete(req);
        req = NULL;
    }

    if (!NT_SUCCESS(ntStatus))
    {
        if (pOutput)
        {
            ExFreePoolWithTag(pOutput, A2DPSIDEBANDTEST_POOLTAG07);
            pOutput = NULL;
        }
    }

    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::UpdateA2dpHpCodecCaps
(
    _In_ PA2DPHPDEVICE_CODEC_CAPABILITIES pCodecCaps
)
{
    PAGED_CODE();
    PA2DPHPDEVICE_CODEC_CAPABILITIES oldCodecCaps = NULL;
    BOOL capabilitiesChanged = FALSE;

    ExAcquireFastMutex(&m_ConnectedCodecLock);
    oldCodecCaps = m_ConnectedCodecCaps;
    m_ConnectedCodecCaps = pCodecCaps;
    //
    // Check if any codec configuration parameters changed.
    //
    if ((m_ConnectedCodecCaps != NULL) && (oldCodecCaps != NULL))
    {
        capabilitiesChanged = (RtlCompareMemory(oldCodecCaps, m_ConnectedCodecCaps, sizeof(*oldCodecCaps)) != sizeof(*oldCodecCaps));
    }
    ExReleaseFastMutex(&m_ConnectedCodecLock);

    if (capabilitiesChanged)
    {
        // Notify audio miniport about this change.
        if (m_SpeakerFormatChangeCallback.Handler != NULL)
        {
            m_SpeakerFormatChangeCallback.Handler(m_SpeakerFormatChangeCallback.Context);
        }
    }

    if (oldCodecCaps != NULL)
    {
        ExFreePoolWithTag(oldCodecCaps, A2DPSIDEBANDTEST_POOLTAG07);
        oldCodecCaps = NULL;
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::Connect()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    return STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::Disconnect()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    return STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
}

//=============================================================================
#pragma code_seg()
BOOL
A2dpHpDevice::GetStreamStatus(_In_ eDeviceType deviceType)
{
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS ntStatus = STATUS_SUCCESS;

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_SpeakerStreamStatusLong, 0, 0);
    }

    return NT_SUCCESS(ntStatus) ? TRUE : FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SpeakerStreamOpen()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    NTSTATUS    ntStatus    = STATUS_SUCCESS;
    LONG        nStreams    = 0;

    ASSERT(m_nSpeakerStreams >= 0);
    
    nStreams = InterlockedIncrement(&m_nSpeakerStreams);
    if (nStreams == 1)
    {
        BOOLEAN  streamOpen = FALSE;
        
        ntStatus = SetA2dpHpStreamOpen(m_SpeakerEpIndex,
                                      &A2dpHpSpeakerSupportedDeviceFormats[m_SpeakerSelectedFormat],
                                      m_pSpeakerDescriptor);

        if (NT_SUCCESS(ntStatus))
        {
            streamOpen = TRUE;
            m_SpeakerStreamStatus = STATUS_SUCCESS;
            ntStatus = EnableA2dpHpSpeakerStreamStatusNotification();
        }

        //
        // Cleanup if any error.
        //
        if (!NT_SUCCESS(ntStatus))
        {
            nStreams = InterlockedDecrement(&m_nSpeakerStreams);
            ASSERT(nStreams == 0);
            UNREFERENCED_VAR(nStreams);

            RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES));

            if (streamOpen)
            {
                SetA2dpHpStreamClose();
            }

            m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE;
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::StreamOpen(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return SpeakerStreamOpen();
    }

    return STATUS_INVALID_PARAMETER;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::SpeakerStreamStart()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS ntStatus = STATUS_SUCCESS;
    LONG nStreams = 0;

    ASSERT(m_nSpeakerStartedStreams >= 0);
    nStreams = InterlockedIncrement(&m_nSpeakerStartedStreams);

    if (nStreams == 1)
    {
        ntStatus = SetA2dpHpStreamStart();
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::StreamStart(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return SpeakerStreamStart();
    }

    return STATUS_INVALID_PARAMETER;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::StreamSuspend(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return SpeakerStreamSuspend();
    }

    return STATUS_INVALID_PARAMETER;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::SpeakerStreamSuspend()
{
    PAGED_CODE();
    NTSTATUS ntStatus = STATUS_SUCCESS;
    LONG nStreams = 0;
    ASSERT(m_nSpeakerStartedStreams > 0);
    nStreams = InterlockedDecrement(&m_nSpeakerStartedStreams);

    if (m_nSpeakerStartedStreams == 0)
    {
        ntStatus = SetA2dpHpStreamSuspend();
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SpeakerStreamClose()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS    ntStatus    = STATUS_SUCCESS;
    LONG        nStreams    = 0;

    ASSERT(m_nSpeakerStreams > 0);
    
    nStreams = InterlockedDecrement(&m_nSpeakerStreams);
    if (nStreams == 0)
    {
        ntStatus = SetA2dpHpStreamClose();

        RtlZeroMemory(&m_SpeakerTransportResources, sizeof(A2DPHPDEVICE_EP_TRANSPORT_RESOURCES));
        
        StopA2dpHpSpeakerStreamStatusNotification();
        
        m_SpeakerStreamStatus = STATUS_INVALID_DEVICE_STATE;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::StreamClose(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return SpeakerStreamClose();
    }

    return STATUS_INVALID_PARAMETER;
}

//=============================================================================
#pragma code_seg("PAGE")
GUID
A2dpHpDevice::GetContainerId(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        return m_pSpeakerDescriptor->ContainerId;
    }
    else
        return { 0 };
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
A2dpHpDevice::SetVolumeHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL);

        m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref.
        m_SpeakerVolumeCallback.Context = EventHandlerContext;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
A2dpHpDevice::SetMuteHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerMuteCallback.Handler == NULL);

        m_SpeakerMuteCallback.Handler = EventHandler; // weak ref.
        m_SpeakerMuteCallback.Context = EventHandlerContext;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
A2dpHpDevice::SetConnectionStatusHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    if (deviceType == eA2dpHpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL);

        m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref.
        m_SpeakerConnectionStatusCallback.Context = EventHandlerContext;
    }

}

//=============================================================================
#pragma code_seg("PAGE")
VOID
A2dpHpDevice::SetFormatChangeHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    //m_SpeakerFormatChangeCallback
    //m_MicFormatChangeCallback
    if (deviceType == eA2dpHpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerFormatChangeCallback.Handler == NULL);

        m_SpeakerFormatChangeCallback.Handler = EventHandler; // weak ref.
        m_SpeakerFormatChangeCallback.Context = EventHandlerContext;
    }

}

//=============================================================================
#pragma code_seg("PAGE")
PPIN_DEVICE_FORMATS_AND_MODES
A2dpHpDevice::GetFormatsAndModes
(
    _In_        eDeviceType             deviceType
)
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(deviceType);
    PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL;
    return pFormatsAndModes;
}

//=============================================================================
#pragma code_seg()
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOL
A2dpHpDevice::IsNRECSupported()
{
    DPF_ENTER(("[%!FUNC!]"));

    return FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
A2dpHpDevice::GetNRECDisableStatus()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    // Return TRUE if HF wants to disable the NREC on the AG.
    return FALSE;
}

//
// Helper functions.
//

//=============================================================================
#pragma code_seg()
NTSTATUS 
A2dpHpDevice::SendIoCtrlAsynchronously
(
    _In_        WDFREQUEST      Request,
    _In_        ULONG           IoControlCode,
    _In_opt_    WDFMEMORY       MemIn,
    _In_opt_    WDFMEMORY       MemOut,
    _In_        PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
    _In_        WDFCONTEXT      Context
)
/*++

Routine Description:

  This function aynchronously sends an I/O Ctrl request to the A2DP Headphone
  device.

--*/
{
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS    ntStatus    = STATUS_SUCCESS;
    BOOLEAN     fSent       = FALSE;

    //
    // Format and send the request.
    //
    ntStatus = WdfIoTargetFormatRequestForIoctl(
        m_WdfIoTarget,
        Request,
        IoControlCode,
        MemIn,
        NULL,
        MemOut,
        NULL);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)),
        Done);

    WdfRequestSetCompletionRoutine(
        Request,
        CompletionRoutine,
        Context);

    fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL);  // no options.
    if (fSent == FALSE)
    {
        ntStatus = WdfRequestGetStatus(Request);
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }
        
        DPF(D_ERROR, ("%!FUNC!: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus));
        goto Done;
    }

    //
    // All Done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SendIoCtrlSynchronously
(
    _In_opt_    WDFREQUEST  Request,
    _In_        ULONG       IoControlCode,
    _In_        ULONG       InLength,
    _In_        ULONG       OutLength,
    _When_(InLength > 0 || OutLength > 0, _In_)
    _When_(InLength == 0 && OutLength == 0, _In_opt_)
                PVOID       Buffer
)
/*++

Routine Description:

  This function inits and synchronously sends an I/O Ctrl request to the A2DP Headphone
  device.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                    ntStatus    = STATUS_SUCCESS;
    PWDF_MEMORY_DESCRIPTOR      memInPtr    = NULL;
    PWDF_MEMORY_DESCRIPTOR      memOutPtr   = NULL;
    WDF_MEMORY_DESCRIPTOR       memIn;
    WDF_MEMORY_DESCRIPTOR       memOut;
    WDF_REQUEST_SEND_OPTIONS    reqOpts;

    //
    // Format and send the request.
    //
    if (InLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn,  Buffer, InLength);
        memInPtr = &memIn;
    }

    if (OutLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength);
        memOutPtr = &memOut;
    }
    
    WDF_REQUEST_SEND_OPTIONS_INIT(
        &reqOpts, 
        WDF_REQUEST_SEND_OPTION_TIMEOUT | 
         WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);

    reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC);
        
    ntStatus = WdfIoTargetSendIoctlSynchronously(
        m_WdfIoTarget,
        Request,
        IoControlCode,
        memInPtr,
        memOutPtr,
        &reqOpts,
        NULL);      // bytes returned.
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)),
        Done);

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SendIoCtrlSynchronously
(
    _In_opt_    WDFREQUEST  Request,
    _In_        ULONG       IoControlCode,
    _In_        ULONG       InLength,
    _In_        ULONG       OutLength,
    _When_(InLength > 0, _In_)
    _When_(InLength == 0, _In_opt_)
                PVOID       InBuffer,
    _When_(OutLength > 0, _In_)
    _When_(OutLength == 0, _In_opt_)
                PVOID       OutBuffer
)
/*++

Routine Description:

  This function inits and synchronously sends an I/O Ctrl request to the A2DP Headphone
  device.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                    ntStatus    = STATUS_SUCCESS;
    PWDF_MEMORY_DESCRIPTOR      memInPtr    = NULL;
    PWDF_MEMORY_DESCRIPTOR      memOutPtr   = NULL;
    WDF_MEMORY_DESCRIPTOR       memIn;
    WDF_MEMORY_DESCRIPTOR       memOut;
    WDF_REQUEST_SEND_OPTIONS    reqOpts;

    //
    // Format and send the request.
    //
    if (InLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn,  InBuffer, InLength);
        memInPtr = &memIn;
    }

    if (OutLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, OutBuffer, OutLength);
        memOutPtr = &memOut;
    }
    
    WDF_REQUEST_SEND_OPTIONS_INIT(
        &reqOpts, 
        WDF_REQUEST_SEND_OPTION_TIMEOUT | 
         WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);

    reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(A2DP_SIDEBAND_SYNC_REQ_TIMEOUT_IN_SEC);
        
    ntStatus = WdfIoTargetSendIoctlSynchronously(
        m_WdfIoTarget,
        Request,
        IoControlCode,
        memInPtr,
        memOutPtr,
        &reqOpts,
        NULL);      // bytes returned.
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_VERBOSE, ("%!FUNC!: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)),
        Done);

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
VOID
A2dpHpDevice::EvtA2dpHpDeviceNotificationStatusWorkItem
(
    _In_    WDFWORKITEM WorkItem
)
/*++

Routine Description:

  The function processes status notification updates.

Arguments:

  WorkItem    - WDF work-item object.
  
--*/
{
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS            ntStatus = STATUS_SUCCESS;
    A2dpHpDevice      * This;
    KIRQL               oldIrql;
    
    if (WorkItem == NULL) 
    {
        return;
    }

    This = GetA2dpHpDeviceNotificationWorkItemContext(WorkItem)->A2dpHpDevice;
    ASSERT(This != NULL);

    for (;;)
    {
        BOOL                                    resend  = TRUE; 
        WDFREQUEST                              req     = NULL;
        A2dpHpDeviceNotificationReqContext    * reqCtx;
        WDF_REQUEST_COMPLETION_PARAMS           params;
            
        //
        // Retrieve a task.
        //
        KeAcquireSpinLock(&This->m_Lock, &oldIrql);
        req = (WDFREQUEST) WdfCollectionGetFirstItem(This->m_ReqCollection);
        if (req != NULL)
        {
            WdfCollectionRemove(This->m_ReqCollection, req);
        }
        KeReleaseSpinLock(&This->m_Lock, oldIrql);

        if (req == NULL)
        {
            break;
        }

        //
        // Get request parameters and context.
        //
        WDF_REQUEST_COMPLETION_PARAMS_INIT(&params);
        WdfRequestGetCompletionParams(req, &params);
        
        reqCtx = GetA2dpHpDeviceNotificationReqContext(req);
        ASSERT(reqCtx != NULL);
        
        //
        // Handle this notification.
        //
        if (NT_SUCCESS(params.IoStatus.Status))
        {
            switch(params.Parameters.Ioctl.IoControlCode)
            {
            case IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE:
                {
                    LONG oldVolume;

                    // Prepare for request resent
                    reqCtx->Buffer.Volume.Immediate = FALSE;

                    if (reqCtx->Buffer.Volume.EpIndex == This->m_SpeakerEpIndex)
                    {
                        oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume.Value);
                        if (reqCtx->Buffer.Volume.Value != oldVolume)
                        {
                            // Notify audio miniport about this change.
                            if (This->m_SpeakerVolumeCallback.Handler != NULL)
                            {
                                This->m_SpeakerVolumeCallback.Handler(
                                    This->m_SpeakerVolumeCallback.Context);
                            }
                        }
                    }

                }
                break;

            case IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE:
                {
                    LONG oldMute;

                    // Prepare for request resent
                    reqCtx->Buffer.Mute.Immediate = FALSE;

                    if (reqCtx->Buffer.Mute.EpIndex == This->m_SpeakerEpIndex)
                    {
                        oldMute = InterlockedExchange(&This->m_SpeakerMute, reqCtx->Buffer.Mute.Value);
                        if (reqCtx->Buffer.Mute.Value != oldMute)
                        {
                            // Notify audio miniport about this change.
                            if (This->m_SpeakerMuteCallback.Handler != NULL)
                            {
                                This->m_SpeakerMuteCallback.Handler(
                                    This->m_SpeakerMuteCallback.Context);
                            }
                        }
                    }

                }
                break;

            case IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE:
            {
                BOOL oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.ConnectionStatus.Connected);
                if (reqCtx->Buffer.ConnectionStatus.Connected != oldStatus)
                {
                    // Notify audio miniport about this change.
                    if (This->m_SpeakerConnectionStatusCallback.Handler != NULL)
                    {
                        This->m_SpeakerConnectionStatusCallback.Handler(
                            This->m_SpeakerConnectionStatusCallback.Context);
                    }
                }
            }
            break;

            case IOCTL_SBAUD_GET_SIOP_UPDATE:
            {
                if ((reqCtx->Buffer.SiopUpdate.EpIndex == This->m_SpeakerEpIndex) &&
                    (reqCtx->Buffer.SiopUpdate.RequestedSiop.ParamSet == SIDEBANDAUDIO_PARAMS_SET_A2DP) &&
                    (reqCtx->Buffer.SiopUpdate.RequestedSiop.TypeId == SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC))
                {
                    PA2DPHPDEVICE_CODEC_CAPABILITIES codecCaps = NULL;
                    ntStatus = This->GetA2dpHpCodecCaps(&codecCaps);
                    if (NT_SUCCESS(ntStatus))
                    {
                        ntStatus = This->UpdateA2dpHpCodecCaps(codecCaps);
                    }
                }
            }
            break;

            default:
                // This should never happen.
                resend = FALSE;
                DPF(D_ERROR, ("%!FUNC!: invalid request ctrl 0x%x", 
                    params.Parameters.Ioctl.IoControlCode));
                break;
            }
        }

        if (resend)
        {
            WDF_REQUEST_REUSE_PARAMS    reuseParams;
            
            WDF_REQUEST_REUSE_PARAMS_INIT(
                &reuseParams,   
                WDF_REQUEST_REUSE_NO_FLAGS,
                STATUS_SUCCESS);
            
            ntStatus = WdfRequestReuse(req, &reuseParams);
            if (!NT_SUCCESS(ntStatus))
            {
                DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus));
                break;
            }
        
            // Resend status notification request.
            ntStatus = This->SendIoCtrlAsynchronously(
                req,
                params.Parameters.Ioctl.IoControlCode,
                reqCtx->MemIn,
                reqCtx->MemOut,
                EvtA2dpHpDeviceNotificationStatusCompletion,
                This);
            
            if (!NT_SUCCESS(ntStatus))
            {
                DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously"
                              "(0x%x) failed, 0x%x", 
                              params.Parameters.Ioctl.IoControlCode, ntStatus));
                break;
            }
        }
    }
}

//=============================================================================
#pragma code_seg()
void 
A2dpHpDevice::EvtA2dpHpDeviceNotificationStatusCompletion
(
  _In_  WDFREQUEST Request,
  _In_  WDFIOTARGET Target,
  _In_  PWDF_REQUEST_COMPLETION_PARAMS Params,
  _In_  WDFCONTEXT Context
)
{
    DPF_ENTER(("[%!FUNC!]"));
   
    NTSTATUS                            ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext   *ctx        = NULL;
    A2dpHpDevice                         *This       = NULL;
    KIRQL                               oldIrql;
    
    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);
    
    ctx = GetA2dpHpDeviceNotificationReqContext(Request);
    This = ctx->A2dpHpDevice;
    ASSERT(This != NULL);

    ntStatus = Params->IoStatus.Status;
    if (ntStatus == STATUS_CANCELLED)
    {
        // A2DP device is shutting down. Do not re-send this request.
        goto Done;
    }

    //
    // If something is wrong with the A2DP interface, do not loop forever.
    //
    if (!NT_SUCCESS(ntStatus))
    {
        if (++ctx->Errors > A2DP_SIDEBAND_NOTIFICATION_MAX_ERROR_COUNT)
        {
            // Too many errors. Do not re-send this request.
            goto Done;
        }
    }
    else
    {
        // reset the # of errors.
        ctx->Errors = 0;
    }

    // 
    // Let the work-item thread process this request.
    //
    KeAcquireSpinLock(&This->m_Lock, &oldIrql);

    ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request);
    if (NT_SUCCESS(ntStatus))
    {
        WdfWorkItemEnqueue(This->m_WorkItem);
    }

    KeReleaseSpinLock(&This->m_Lock, oldIrql);

Done:;
}

//=============================================================================

#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SetA2dpHpDeviceCapabilities
(
)
/*++

Routine Description:

This function synchronously informs the Audio Driver capabilities to A2DP stack.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[A2dpHpDevice::SetA2dpHpDeviceCapabilities]"));

    NTSTATUS                            ntStatus = STATUS_SUCCESS;

    typedef struct _SidebandDeviceSupportedCodecs
    {
        A2DP_SBC_MEDIA_HEADER SupportedSbcCodec;
        A2DP_AAC_MEDIA_HEADER SupportedAacCodec;
    } SidebandDeviceSupportedCodecs, * PSidebandDeviceSupportedCodecs;

    typedef struct _SidebandDeviceCapsInputParams
    {
        SIDEBANDAUDIO_SIOP_REQUEST_PARAM DeviceCapsHeader;
        SidebandDeviceSupportedCodecs SupportedCodecs;
    }SidebandDeviceCapsInputParams, * PSidebandDeviceCapsInputParams;

    SidebandDeviceCapsInputParams deviceCapsParams = { 0 };
    deviceCapsParams.DeviceCapsHeader.RequestedSiop = { SIDEBANDAUDIO_PARAMS_SET_A2DP, SIDEBANDAUDIO_PARAM_A2DP_CODECS, sizeof(deviceCapsParams.SupportedCodecs) };
    deviceCapsParams.DeviceCapsHeader.EpIndex = 0;
    //
    // Set SBC codec settings.
    //
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CategoryCodecHeader.SvcCategory = 0x7; // Service category is media codec.
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CategoryCodecHeader.Losc = A2DP_SBC_MEDIA_HEADER::CorrectLosc();
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.CodecType = 0x0; // Codec type is SBC
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MediaType = 0x0; // Media type is audio
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.ChannelMode = 0x1; // Channel Mode is joint stereo
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.SamplingFrequency = 0x3; // 48 and 44.1 kHz
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.AllocMethod = 0x1; // Allocation method is loudness
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.Subbands = 0x1; // 8 subbands
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.BlockLength = 0x1; 
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MinBitpoolVal = 0x2;
    deviceCapsParams.SupportedCodecs.SupportedSbcCodec.MaxBitpoolVal = 0x53;

    //
    // Set AAC codec settings.
    //
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.CategoryCodecHeader.SvcCategory = 0x7; // Service category is media codec.
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.CategoryCodecHeader.Losc = A2DP_AAC_MEDIA_HEADER::CorrectLosc();
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.MediaType = 0x0;     // Media type is audio.
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.CodecType = 0x2;     // Codec type is MPEG 2 AAC
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.ObjectType = 0x80;   // Object type is AAC MPEG 2 LC
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.SamplingFrequencyLower4Bits = 0x8; // Sampling frequency is 48kHz.
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.Channels = 0x1; // Dual channel.
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.VariableBitRate = 1; // Vbr is enabled.
    // Set bitrate to 256kbps
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateUpper7Bits = 0x3;
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateMiddle8Bits = 0xe8;
    deviceCapsParams.SupportedCodecs.SupportedAacCodec.BitRateLower8Bits = 0;

    //
    // Set the audio codec capabilities SIOP.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_SET_SIOP,
        sizeof(deviceCapsParams),
        0,
        &deviceCapsParams);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetA2dpHpDeviceCapabilities: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_SIOP) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::GetA2dpHpDescriptor
(
    _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor
)
/*++

Routine Description:

  This function synchronously gets the remote A2DP Headphone Device descriptor.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    WDFREQUEST                              req         = NULL;
    PSIDEBANDAUDIO_DEVICE_DESCRIPTOR     descriptor  = NULL;
    ULONG                                   length      = 0;
    ULONG_PTR                               information = 0;
    WDF_REQUEST_REUSE_PARAMS                reuseParams;   
    WDF_OBJECT_ATTRIBUTES                   attributes;
    
    *Descriptor = NULL;

    //
    // Allocate and format a WDF request.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &req);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Get the size of the buffer.
    //
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR,
        NULL,
        NULL,
        NULL);
        
    if (ntStatus != STATUS_BUFFER_TOO_SMALL)
    {
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }
        
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR): failed, 0x%x", ntStatus));
        goto Done;
    }
    
    ntStatus = STATUS_SUCCESS;

    information = WdfRequestGetInformation(req);
    if (information == 0 || information > ULONG_MAX)
    {
        ntStatus = STATUS_INVALID_DEVICE_STATE;        
        DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus));
        goto Done;
    }

    length = (ULONG)information;
        
    //
    // Allocate memory needed to hold the info.
    //
    descriptor  = (PSIDEBANDAUDIO_DEVICE_DESCRIPTOR) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG);
    if (descriptor == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Get the A2DP Headphone descriptor.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,   
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);
    
    ntStatus = WdfRequestReuse(req, &reuseParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)),
        Done);
    
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR,
        NULL,
        length,
        descriptor);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_DEVICE_DESCRIPTOR) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Descriptor = descriptor;
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (descriptor != NULL)
        {
            ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG);
        }   
    }

    if (req != NULL)
    {
        WdfObjectDelete(req);
        req = NULL;
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::GetA2dpHpEndpointDescriptor
(
    _In_    ULONG                               EpIndex,
    _Out_ PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2  *EpDescriptor
)
/*++

Routine Description:

  This function synchronously gets the A2DP Headphone Endpoint Descriptor.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    WDFREQUEST                              req         = NULL;
    PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     descriptor  = NULL;
    ULONG                                   length      = 0;
    ULONG_PTR                               information = 0;
    WDF_REQUEST_REUSE_PARAMS                reuseParams;   
    WDF_OBJECT_ATTRIBUTES                   attributes;
    
    *EpDescriptor = NULL;

    //
    // Allocate and format a WDF request.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &req);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Get the size of the buffer.
    //
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2,
        sizeof(EpIndex),
        NULL,
        &EpIndex);
        
    if (ntStatus != STATUS_BUFFER_TOO_SMALL)
    {
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }
        
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR): failed, 0x%x", ntStatus));
        goto Done;
    }
    
    ntStatus = STATUS_SUCCESS;

    information = WdfRequestGetInformation(req);
    if (information == 0 || information > ULONG_MAX)
    {
        ntStatus = STATUS_INVALID_DEVICE_STATE;        
        DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR buffer too big (%Id): 0x%x", information, ntStatus));
        goto Done;
    }

    length = (ULONG)information;
        
    //
    // Allocate memory needed to hold the info.
    //
    descriptor  = (PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG);
    if (descriptor == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Get the A2DP Headphone descriptor.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,   
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);
    
    ntStatus = WdfRequestReuse(req, &reuseParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)),
        Done);

    // Same buffer used for input and output
    // Cast into ULONG to provide EpIndex
    *((ULONG *)descriptor) = EpIndex;
    
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR2,
        sizeof(EpIndex),
        length,
        descriptor);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *EpDescriptor = descriptor;
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (descriptor != NULL)
        {
            ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG);
        }   
    }

    if (req != NULL)
    {
        WdfObjectDelete(req);
        req = NULL;
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::VerifyEndpointFormatCompatibility
(
    _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS    EpSupportedFormats,
    _In_    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE  pHardwareSupportedFormats,
    _In_    ULONG                               NumHardwareSupportedFormats,
    _Out_   PULONG                              pHardwareFormatIndex
)
{
    PAGED_CODE();

    NTSTATUS status = STATUS_NOT_SUPPORTED;
    ULONG iHardwareFormats = 0;
    ULONG iEpDataFormats = 0;
    BOOL bFound = FALSE;

    // Verify here whether hardware/dsp mixer can support the A2DP device formats
    // This format will be later used to open the ISOCH channel

    for (iHardwareFormats = 0; iHardwareFormats < NumHardwareSupportedFormats; iHardwareFormats++)
    {
        // for each format supported by hardware, search in EP data formats
        for (iEpDataFormats = 0; iEpDataFormats < EpSupportedFormats->NumFormats; iEpDataFormats++)
        {
            PKSDATAFORMAT pEpDataFormat = (PKSDATAFORMAT)EpSupportedFormats->Formats[iEpDataFormats];

            // Verify KSDATAFORMAT
            if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT))
            {
                if (pEpDataFormat->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX))
                {
                    // Check other parameters as well
                    // Ignoring for simplicity
                    if ((((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nSamplesPerSec == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nSamplesPerSec) &&
                        (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.nChannels == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.nChannels) &&
                        (((PKSDATAFORMAT_WAVEFORMATEX)pEpDataFormat)->WaveFormatEx.wBitsPerSample == pHardwareSupportedFormats[iHardwareFormats].WaveFormatExt.Format.wBitsPerSample))
                    {
                        bFound = TRUE;
                        *pHardwareFormatIndex = iHardwareFormats;
                        status = STATUS_SUCCESS;
                        break;
                    }
                }
            }
        }
    }

    // Iterate through Sideband Device data range to figure out
    // intersection with hardware supported formats

    return status;
}


//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::EnableA2dpHpConnectionStatusNotification()
{
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                                ntStatus = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext* ctx = NULL;

    ctx = GetA2dpHpDeviceNotificationReqContext(m_ConnectionReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.ConnectionStatus.Immediate = FALSE;
    ctx->Buffer.ConnectionStatus.EpIndex = m_SpeakerEpIndex;
    ctx->Buffer.ConnectionStatus.Connected = FALSE;

    //
    // Register for connection status updates.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_ConnectionReq,
        IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtA2dpHpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

NTSTATUS
A2dpHpDevice::GetA2dpHpEndpointFormatsIntersection
(
    _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS    pDeviceFormats,
    _Out_   PSIDEBANDAUDIO_SUPPORTED_FORMATS    *ppSupportedFormatsIntersection
)
/*++

Routine Description:

  This function synchronously gets the A2DP Headphone supported formats for
  a given endpoint.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    WDFREQUEST                              req         = NULL;
    PSIDEBANDAUDIO_SUPPORTED_FORMATS        formats     = NULL;
    ULONG                                   length      = 0;
    ULONG_PTR                               information = 0;
    WDF_REQUEST_REUSE_PARAMS                reuseParams;   
    WDF_OBJECT_ATTRIBUTES                   attributes;
    
    *ppSupportedFormatsIntersection = NULL;

    //
    // Allocate and format a WDF request.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &req);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Get the size of the buffer.
    //
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_SUPPORTED_FORMATS,
        pDeviceFormats->CbSize,
        0,
        pDeviceFormats,
        NULL);
        
    if (ntStatus != STATUS_BUFFER_TOO_SMALL)
    {
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }
        
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS): failed, 0x%x", ntStatus));
        goto Done;
    }
    
    ntStatus = STATUS_SUCCESS;

    information = WdfRequestGetInformation(req);
    if (information == 0 || information > ULONG_MAX)
    {
        ntStatus = STATUS_INVALID_DEVICE_STATE;        
        DPF(D_ERROR, ("%!FUNC!: IOCTL_SBAUD_GET_SUPPORTED_FORMATS buffer too big (%Id): 0x%x", information, ntStatus));
        goto Done;
    }

    length = (ULONG)information;
        
    //
    // Allocate memory needed to hold the info.
    //
    formats  = (PSIDEBANDAUDIO_SUPPORTED_FORMATS) ExAllocatePool2(POOL_FLAG_NON_PAGED, length, A2DPSIDEBANDTEST_POOLTAG03);
    if (formats == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Get the A2DP Headphone descriptor.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,   
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);
    
    ntStatus = WdfRequestReuse(req, &reuseParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus)),
        Done);

    // Same buffer used for input and output
    // Cast into ULONG to provide EpIndex
    
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_SBAUD_GET_SUPPORTED_FORMATS,
        pDeviceFormats->CbSize,
        length,
        pDeviceFormats,
        formats);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_SUPPORTED_FORMATS) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *ppSupportedFormatsIntersection = formats;
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (formats != NULL)
        {
            ExFreePoolWithTag(formats, A2DPSIDEBANDTEST_POOLTAG03);
        }   
    }

    if (req != NULL)
    {
        WdfObjectDelete(req);
        req = NULL;
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::GetA2dpHpVolumePropertyValues
(
    _In_  ULONG                     EpIndex,
    _In_  ULONG                     Length,
    _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
)
/*++

Routine Description:

  This function synchronously gets the remote A2DP Headphone volume values.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                    ntStatus    = STATUS_SUCCESS;
    PKSPROPERTY_DESCRIPTION     propValues  = NULL;
    
    *PropValues = NULL;

    //
    // Allocate memory.
    //
    propValues  = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, A2DPSIDEBANDTEST_POOLTAG02);
    if (propValues == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Send EpIndex in same buffer as input param
    //
    *((ULONG *)propValues) = EpIndex;

    //
    // Get the A2DP Headphone volume property values.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES,
        sizeof(EpIndex), 
        Length, 
        propValues);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *PropValues = propValues;
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (propValues != NULL)
        {
            ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG);
        }   
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SetA2dpHpSpeakerVolume
(
    _In_ LONG   Channel,
    _In_ LONG   Volume  
)
/*++

Routine Description:

  This function synchronously sets the remote A2DP Headphone speaker volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    SIDEBANDAUDIO_VOLUME_PARAMS  vp;
    
    //
    // Set the A2DP Headphone speaker volume.
    //

    vp.EpIndex = m_SpeakerEpIndex;
    vp.Value = Volume;
    vp.Channel = Channel;
    vp.Immediate = TRUE; // not required for SET
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_SET_VOLUME,
        sizeof(vp),
        0,
        &vp);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_VOLUME) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::GetA2dpHpSpeakerVolume
(
    _In_  LONG  Channel,
    _Out_ LONG  * Volume    
)
/*++

Routine Description:

  This function synchronously gets the remote A2DP Headphone speaker volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationBuffer   buffer      = {0};

    *Volume = 0;
    
    buffer.Volume.Immediate = TRUE;
    buffer.Volume.EpIndex = m_SpeakerEpIndex;
    buffer.Volume.Channel = Channel;
    buffer.Volume.Value = 0;
    
    //
    // Get the A2DP Headphone speaker volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE,
        sizeof(buffer.Volume),
        sizeof(buffer.Volume),
        &buffer);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Volume = buffer.Volume.Value;
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS 
A2dpHpDevice::EnableA2dpHpSpeakerVolumeStatusNotification()
/*++

Routine Description:

  This function registers for A2DP Headphone speaker 
  volume change notification. 

--*/
{
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext       *ctx         = NULL;

    ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerVolumeReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.Volume.Immediate    = FALSE; 
    ctx->Buffer.Volume.EpIndex      = m_SpeakerEpIndex;
    ctx->Buffer.Volume.Value        = 0;
    
    //
    // Register for speaker volume updates.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_SpeakerVolumeReq,
        IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtA2dpHpDeviceNotificationStatusCompletion,
        this);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::GetA2dpHpMutePropertyValues
(
    _In_  ULONG                     EpIndex,
    _In_  ULONG                     Length,
    _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
)
/*++

Routine Description:

  This function synchronously gets the remote A2DP Headphone mute values.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                    ntStatus = STATUS_SUCCESS;
    PKSPROPERTY_DESCRIPTION     propValues = NULL;
    *PropValues = NULL;

    //
    // Allocate memory
    //
    propValues = (PKSPROPERTY_DESCRIPTION) ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, A2DPSIDEBANDTEST_POOLTAG06);
    if (propValues == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Send EpIndex in same buffer as input param
    //
    *((ULONG*)propValues) = EpIndex;

    //
    // Get the A2DP Headphone volume property values.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_GET_MUTEPROPERTYVALUES,
        sizeof(EpIndex),
        Length,
        propValues);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *PropValues = propValues;
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (propValues != NULL)
        {
            ExFreePoolWithTag(propValues, A2DPSIDEBANDTEST_POOLTAG06);
        }   
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SetA2dpHpSpeakerMute
(
    _In_ LONG   Channel,
    _In_ LONG   Mute
)
/*++

Routine Description:

  This function synchronously sets the remote A2DP Headphone speaker mute.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    SIDEBANDAUDIO_MUTE_PARAMS       mp;
    
    //
    // Set the A2DP Headphone speaker mute.
    //

    mp.EpIndex      = m_SpeakerEpIndex;
    mp.Value        = Mute;
    mp.Channel      = Channel;
    mp.Immediate    = TRUE; // not required for SET
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_SET_MUTE,
        sizeof(mp),
        0,
        &mp);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_MUTE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::GetA2dpHpSpeakerMute
(
    _In_  LONG  Channel,
    _Out_ LONG  *Mute
)
/*++

Routine Description:

  This function synchronously gets the remote A2DP Headphone speaker mute.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationBuffer   buffer      = {0};

    *Mute = 0;
    
    buffer.Mute.Immediate = TRUE;
    buffer.Mute.EpIndex = m_SpeakerEpIndex;
    buffer.Mute.Channel = Channel;
    buffer.Mute.Value = 0;
    
    //
    // Get the A2DP Headphone speaker mute.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE,
        sizeof(buffer.Mute),
        sizeof(buffer.Mute),
        &buffer);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Mute = buffer.Mute.Value;
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS 
A2dpHpDevice::EnableA2dpHpSpeakerMuteStatusNotification()
/*++

Routine Description:

  This function registers for A2DP Headphone speaker 
  mute change notification. 

--*/
{
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext       *ctx         = NULL;

    ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerMuteReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.Mute.Immediate    = FALSE; 
    ctx->Buffer.Mute.EpIndex      = m_SpeakerEpIndex;
    ctx->Buffer.Mute.Value        = 0;
    
    //
    // Register for speaker mute updates.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_SpeakerMuteReq,
        IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtA2dpHpDeviceNotificationStatusCompletion,
        this);
    
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_MUTE_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::GetA2dpHpConnectionStatus(BOOL* ConnectionStatus)
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                        ntStatus = STATUS_SUCCESS;
    A2dpHpDeviceNotificationBuffer   buffer = { 0 };

    *ConnectionStatus = FALSE;

    buffer.ConnectionStatus.Immediate = TRUE;
    buffer.ConnectionStatus.EpIndex = m_SpeakerEpIndex;

    //
    // Get the A2DP connection status.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE,
        sizeof(buffer.ConnectionStatus),
        sizeof(buffer.ConnectionStatus),
        &buffer);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *ConnectionStatus = buffer.ConnectionStatus.Connected;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SetSidebandClaimed(_In_ BOOL bClaimed)
/*++

Routine Description:

  This function synchronously requests an open ISOCH channel to transmit audio
  data over transport.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS    ntStatus    = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_SET_DEVICE_CLAIMED,
        sizeof(BOOL),
        0,
        &bClaimed);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_SET_DEVICE_CLAIMED) failed, 0x%x", ntStatus)),
        Done);

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
void 
A2dpHpDevice::EvtA2dpHpDeviceStreamStatusCompletion
(
  _In_  WDFREQUEST Request,
  _In_  WDFIOTARGET Target,
  _In_  PWDF_REQUEST_COMPLETION_PARAMS Params,
  _In_  WDFCONTEXT Context
)
/*++

Routine Description:

  Completion callback for the A2DP Headphone stream status notification. 

--*/
{
    DPF_ENTER(("[%!FUNC!]"));
   
    NTSTATUS                             ntStatus   = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext    *reqCtx    = NULL;
    A2dpHpDevice                          *This      = NULL;
    NTSTATUS                             ntResult   = STATUS_SUCCESS;
    
    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);

    //
    // Get the A2DP stream status.
    //
    reqCtx = GetA2dpHpDeviceNotificationReqContext(Request);
    This = reqCtx->A2dpHpDevice;
    ASSERT(This != NULL);

    ntStatus = Params->IoStatus.Status;
    if (!NT_SUCCESS(ntStatus))
    {
        ntResult = STATUS_INVALID_DEVICE_STATE;
    }
    else 
    {
        ntResult = reqCtx->Buffer.StreamStatus.Status;
    }

    if (reqCtx->Buffer.StreamStatus.EpIndex == This->m_SpeakerEpIndex)
    {
        InterlockedExchange(&This->m_SpeakerStreamStatusLong, (LONG)ntResult);

        //
        // Let the stop routine know we are done. Stop routine will 
        // re-init the request.
        //
        KeSetEvent(&This->m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE);
    }
}

//=============================================================================
#pragma code_seg()
NTSTATUS 
A2dpHpDevice::EnableA2dpHpSpeakerStreamStatusNotification()
/*++

Routine Description:

  This function registers for A2DP Headphone stream status notification. 

--*/
{
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                            ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext   *ctx        = NULL;

    ASSERT(m_nSpeakerStreams > 0);
    
    ctx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq);
    ctx->Buffer.StreamStatus.Immediate = FALSE; 
    ctx->Buffer.StreamStatus.EpIndex = m_SpeakerEpIndex;

    KeClearEvent(&m_SpeakerStreamStatusEvent);
    
    //
    // Get the A2DP Headphone speaker stream status.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_SpeakerStreamReq,
        IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtA2dpHpDeviceStreamStatusCompletion,
        this);
    
    if (!NT_SUCCESS(ntStatus))
    {
        KeSetEvent(&m_SpeakerStreamStatusEvent, IO_NO_INCREMENT, FALSE);
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_STREAM_STATUS_UPDATE) failed, 0x%x", ntStatus));
        goto Done;
    }

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

NTSTATUS
A2dpHpDevice::EnableA2dpHpSpeakerSiopNotification()
/*++

Routine Description:

    This function registers for A2DP Headphone updates for the configured codec SIOP.

--*/
{
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS                            ntStatus    = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext* ctx         = NULL;

    ctx = GetA2dpHpDeviceNotificationReqContext(m_SiopUpdateReq);
    ctx->Buffer.SiopUpdate.EpIndex = m_SpeakerEpIndex;
    ctx->Buffer.SiopUpdate.RequestedSiop.ParamSet = SIDEBANDAUDIO_PARAMS_SET_A2DP;
    ctx->Buffer.SiopUpdate.RequestedSiop.Size = 0;
    ctx->Buffer.SiopUpdate.RequestedSiop.TypeId = SIDEANDAUDIO_PARAM_A2DP_CONFIGURED_CODEC;

    //
    // Register for updates to the A2DP configured codec SIOP.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_SiopUpdateReq,
        IOCTL_SBAUD_GET_SIOP_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtA2dpHpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlAsynchronously(IOCTL_SBAUD_GET_SIOP_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::StopA2dpHpSpeakerStreamStatusNotification()
/*++

Routine Description:

  This function stops the A2DP Headphone speaker stream status notification.
  The function waits for the request to be done before returning.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS                            ntStatus        = STATUS_SUCCESS;
    A2dpHpDeviceNotificationReqContext   *reqCtx         = NULL;
    WDF_REQUEST_REUSE_PARAMS            reuseParams;  
   
    WdfRequestCancelSentRequest(m_SpeakerStreamReq);
    KeWaitForSingleObject(&m_SpeakerStreamStatusEvent, Executive, KernelMode, FALSE, NULL);

    reqCtx = GetA2dpHpDeviceNotificationReqContext(m_SpeakerStreamReq);
    ASSERT(reqCtx != NULL);
    UNREFERENCED_VAR(reqCtx);
    
    // 
    // Re-init the request for later.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,   
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);
    
    ntStatus = WdfRequestReuse(m_SpeakerStreamReq, &reuseParams);
    if (!NT_SUCCESS(ntStatus))
    {
        DPF(D_ERROR, ("%!FUNC!: WdfRequestReuse failed, 0x%x", ntStatus));
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::SetA2dpHpStreamOpen
(
    ULONG EpIndex,
    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE  pStreamFormat,
    PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor
)
/*++

Routine Description:

This function synchronously requests an open ISOCH channel to transmit audio
data over transport.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[A2dpHpDevice::SetA2dpHpStreamOpen]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(pEndpointDescriptor);

    SIDEBANDAUDIO_STREAM_OPEN_PARAMS sp = { 0 };
    sp.EpIndex = EpIndex;
    sp.Format = (PKSDATAFORMAT)pStreamFormat;
    sp.SiopCount = 0;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_STREAM_OPEN,
        sizeof(sp),
        0,
        &sp);

    if (ntStatus == STATUS_DEVICE_BUSY)
    {
        // The stream channel is already open.
        DPF(D_VERBOSE, ("SetA2dpHpStreamOpen: the stream channel is already open"));
        ntStatus = STATUS_SUCCESS;
    }

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetA2dpHpStreamOpen: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_OPEN) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::SetA2dpHpStreamStart()
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    NTSTATUS ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_STREAM_START,
        sizeof(m_SpeakerEpIndex),
        0,
        &m_SpeakerEpIndex);

    if (ntStatus == STATUS_DEVICE_BUSY)
    {
        DPF(D_VERBOSE, ("%!FUNC!: the stream channel is already started"));
        ntStatus = STATUS_SUCCESS;
    }

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_START) failes, 0x%x", ntStatus)),
        Done);

    ntStatus = STATUS_SUCCESS;

Done:
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::SetA2dpHpStreamSuspend()
{
    PAGED_CODE();

    DPF_ENTER(("[%!FUNC!]"));
    NTSTATUS ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_STREAM_SUSPEND,
        sizeof(m_SpeakerEpIndex),
        0,
        &m_SpeakerEpIndex);

    if (ntStatus == STATUS_DEVICE_BUSY)
    {
        DPF(D_VERBOSE, ("%!FUNC!: the stream channel is already suspended"));
        ntStatus = STATUS_SUCCESS;
    }

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_SUSPEND) failes, 0x%x", ntStatus)),
        Done);

    ntStatus = STATUS_SUCCESS;

Done:
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::SetA2dpHpStreamClose()
/*++

Routine Description:

  This function synchronously requests to close the ISOCH channel.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));
    
    NTSTATUS        ntStatus    = STATUS_SUCCESS;
    
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_SBAUD_STREAM_CLOSE,
        sizeof(m_SpeakerEpIndex),
        0,
        &m_SpeakerEpIndex);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SendIoCtrlSynchronously(IOCTL_SBAUD_STREAM_CLOSE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
VOID 
A2dpHpDevice::Start()
/*++

Routine Description:

  Asynchronously called to start the audio device.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS            ntStatus            = STATUS_SUCCESS;
    ULONG               speakerEndpoints    = 0;

    // Indicate audio driver capabilities indicating support for 16KHz sampling rate
    ntStatus = SetA2dpHpDeviceCapabilities();
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: SetA2dpHpDeviceCapabilities: failed to set Wideband Support, 0x%x", ntStatus)),
        Done);

    //
    // Get A2DP descriptor
    //
    ntStatus = GetA2dpHpDescriptor(&m_Descriptor);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: GetA2dpHpDescriptor: failed to retrieve A2DP Headphone Descriptor, 0x%x", ntStatus)),
        Done);

    for (ULONG i = 0; i < m_Descriptor->NumberOfEndpoints; i++)
    {
        PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 ed;
        ntStatus = GetA2dpHpEndpointDescriptor(i, &ed);
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: GetA2dpHpEndpointDescriptor: failed to retrieve A2DP Headphone Endpoint %d Descriptor, 0x%x", i, ntStatus)),
            Done);

        if (ed->Direction == KSPIN_DATAFLOW_OUT)
        {
            speakerEndpoints++;
            m_SpeakerEpIndex = i;
            m_pSpeakerDescriptor = ed;
        }
    }

    if (speakerEndpoints > 1)
    {
        ntStatus = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
        m_SpeakerEpIndex = 0;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: Number of endpoints on A2DP device not supported, 0x%x", ntStatus)),
        Done);

    //
    // Get volume settings.
    //
    if (m_pSpeakerDescriptor)
    {
        if (m_pSpeakerDescriptor->Capabilities.Volume)
        {
            PKSPROPERTY_DESCRIPTION     volumePropValues = NULL;
            LONG                        volume = 0;

            // Volume settings.
            ntStatus = GetA2dpHpVolumePropertyValues(
                m_SpeakerEpIndex,
                m_pSpeakerDescriptor->VolumePropertyValuesSize,
                &volumePropValues);

            IF_FAILED_ACTION_JUMP(
                ntStatus,
                DPF(D_ERROR, ("%!FUNC!: GetA2dpHpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)),
                Done);

            m_SpeakerVolumePropValues = volumePropValues;

            // Speaker volume.
            ntStatus = GetA2dpHpSpeakerVolume(0, &volume);
            IF_FAILED_ACTION_JUMP(
                ntStatus,
                DPF(D_ERROR, ("%!FUNC!: GetA2dpHpSpeakerVolume: failed, 0x%x", ntStatus)),
                Done);

            m_SpeakerVolumeLevel = volume;
        }

        if (m_pSpeakerDescriptor->Capabilities.Mute)
        {
            PKSPROPERTY_DESCRIPTION     mutePropValues = NULL;
            LONG                        mute = 0;

            // Mute settings.
            ntStatus = GetA2dpHpMutePropertyValues(
                m_SpeakerEpIndex,
                m_pSpeakerDescriptor->MutePropertyValuesSize,
                &mutePropValues);

            IF_FAILED_ACTION_JUMP(
                ntStatus,
                DPF(D_ERROR, ("%!FUNC!: GetA2dpHpMutePropertyValues: failed to retrieve KSPROPERTY_DESCRIPTION, 0x%x", ntStatus)),
                Done);

            m_SpeakerMutePropValues         = mutePropValues;

            // Speaker mute.
            ntStatus = GetA2dpHpSpeakerMute(0, &mute);
            IF_FAILED_ACTION_JUMP(
                ntStatus,
                DPF(D_ERROR, ("%!FUNC!: GetA2dpHpSpeakerMute: failed, 0x%x", ntStatus)),
                Done);

            m_SpeakerMute = mute;
        }

        // Speaker Formats
        PKSDATAFORMAT speakerFormatsArray[SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats)];
        for (ULONG i = 0; i < SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats); i++)
        {
            speakerFormatsArray[i] = (PKSDATAFORMAT)(&A2dpHpSpeakerSupportedDeviceFormats[i]);
        }
        SIDEBANDAUDIO_SUPPORTED_FORMATS speakerDeviceFormats =
        {
            sizeof(SIDEBANDAUDIO_SUPPORTED_FORMATS) + SIZEOF_ARRAY(speakerFormatsArray)*sizeof(*speakerFormatsArray),
            m_SpeakerEpIndex,
            SIZEOF_ARRAY(speakerFormatsArray),
            speakerFormatsArray
        };

        m_pSpeakerSupportedFormatsIntersection = (PSIDEBANDAUDIO_SUPPORTED_FORMATS)ExAllocatePool2(POOL_FLAG_NON_PAGED, speakerDeviceFormats.CbSize, SYSVAD_POOLTAG);
        RtlCopyMemory(m_pSpeakerSupportedFormatsIntersection, &speakerDeviceFormats, sizeof(speakerDeviceFormats));
        RtlCopyMemory(m_pSpeakerSupportedFormatsIntersection->Formats, speakerFormatsArray, SIZEOF_ARRAY(speakerFormatsArray)*sizeof(*speakerFormatsArray));

        //
        // Customize the topology/wave descriptors for this instance
        //
        ntStatus = CreateCustomEndpointMinipair(
            g_A2dpHpRenderEndpoints[0],
            &m_pSpeakerDescriptor->FriendlyName,
            &m_pSpeakerDescriptor->Category,
            m_pSpeakerDescriptor->FilterInterfacePropertyCount,
            m_pSpeakerDescriptor->FilterInterfaceProperties,
            &m_SpeakerMiniports);
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)),
            Done);
        m_SpeakerMiniports->TopoName = m_SpeakerTopologyNameBuffer;
        m_SpeakerMiniports->WaveName = m_SpeakerWaveNameBuffer;

        // Verify format compat
        ntStatus = VerifyEndpointFormatCompatibility(
            m_pSpeakerSupportedFormatsIntersection,
            A2dpHpSpeakerSupportedDeviceFormats,
            SIZEOF_ARRAY(A2dpHpSpeakerSupportedDeviceFormats),
            &m_SpeakerSelectedFormat);
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: VerifyEndpointFormatCompatibility : failed for speaker, 0x%x", ntStatus)),
            Done);

        ASSERT(m_SpeakerMiniports != NULL);
        _Analysis_assume_(m_SpeakerMiniports != NULL);

        //
        // Register topology and wave filters.
        //
        ntStatus = m_Adapter->InstallEndpointFilters(
            NULL,
            m_SpeakerMiniports,
            PSIDEBANDDEVICECOMMON(this),
            &m_UnknownSpeakerTopology,
            &m_UnknownSpeakerWave, NULL, NULL
        );

        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: InstallEndpointRenderFilters (A2DP Headphone): failed, 0x%x", ntStatus)),
            Done);

        //
        // Pend status notifications.
        //

        ntStatus = EnableA2dpHpConnectionStatusNotification();
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpConnectionStatusNotification: failed, 0x%x", ntStatus)),
            Done);

        ntStatus = EnableA2dpHpSpeakerSiopNotification();
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpSpeakerSiopNotification: failed, 0x%x", ntStatus)),
            Done);

        if (m_pSpeakerDescriptor->Capabilities.Volume)
        {
            // Volume speaker status.
            ntStatus = EnableA2dpHpSpeakerVolumeStatusNotification();
            IF_FAILED_ACTION_JUMP(
                ntStatus,
                DPF(D_ERROR, ("%!FUNC!: EnableA2dpHpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)),
                Done);
        }
    }

    // Set Sideband claimed
    ntStatus = SetSidebandClaimed(TRUE);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("%!FUNC!: SetSidebandClaimed TRUE: failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:
    if (!NT_SUCCESS(ntStatus))
    {
        ntStatus = SetSidebandClaimed(FALSE);
        InterlockedExchange((PLONG)&m_State, eA2dpHpStateFailed);
    }
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
A2dpHpDevice::CreateCustomEndpointMinipair
(
    _In_                                           PENDPOINT_MINIPAIR pBaseMinipair,
    _In_                                           PUNICODE_STRING FriendlyName,
    _In_                                           PGUID pCategory,
    _In_                                           ULONG customFilterInterfacePropertyCount,
    _In_count_(customFilterInterfacePropertyCount) DEVPROPERTY* customFilterInterfaceProperties,
    _Outptr_                                       PENDPOINT_MINIPAIR *ppCustomMinipair
)
{
    NTSTATUS ntStatus;
    PENDPOINT_MINIPAIR  pNewMinipair = NULL;
    SYSVAD_DEVPROPERTY* pTopoProperties = NULL;
    PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL;
    PPCPIN_DESCRIPTOR   pNewTopoPins = NULL;
    ULONG cTopoProperties;
    ULONG cTopoPins;
    ULONG cWaveProperties;
    SYSVAD_DEVPROPERTY* pWaveProperties = NULL;

    PAGED_CODE();

    //
    // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties for the friendly name.
    // Additional properties will be added to the topo filter interface for custom properties set by the profile driver.
    // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the a2dp device
    //
    cTopoPins = pBaseMinipair->TopoDescriptor->PinCount;
    // Additional topo properties consist of the friendly name and custom filter properties set by the profile driver.
    cTopoProperties = pBaseMinipair->TopoInterfacePropertyCount + 1 + customFilterInterfacePropertyCount;
    pTopoProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG);
    pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG);
    pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG);
    pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG);
    cWaveProperties = pBaseMinipair->WaveInterfacePropertyCount;
    pWaveProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cWaveProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG);

    if ((pTopoProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL) && (pWaveProperties != NULL))
    {
        SYSVAD_DEVPROPERTY *pLastProperty;

        // Copy base minipair properties to new property list
        if (pBaseMinipair->TopoInterfacePropertyCount > 0)
        {
            RtlCopyMemory(pTopoProperties, pBaseMinipair->TopoInterfaceProperties, pBaseMinipair->TopoInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY));
        }

        // Add friendly name property to the topo property list
        NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength);  // Assuming NULL terminated string
        pLastProperty = pTopoProperties + pBaseMinipair->TopoInterfacePropertyCount;
        pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName;
        pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT;
        pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL);
        pLastProperty->Buffer = FriendlyName->Buffer;
        pLastProperty++;

        // Add custom filter interface properties to the topo property list.
        for (ULONG i = 0; i < customFilterInterfacePropertyCount; i++)
        {
            pLastProperty->PropertyKey = &customFilterInterfaceProperties[i].CompKey.Key;
            pLastProperty->Type = customFilterInterfaceProperties[i].Type;
            pLastProperty->BufferSize = customFilterInterfaceProperties[i].BufferSize;
            pLastProperty->Buffer = customFilterInterfaceProperties[i].Buffer;
            pLastProperty++;
        }

        if (pBaseMinipair->WaveInterfacePropertyCount > 0)
        {
            RtlCopyMemory(pWaveProperties, pBaseMinipair->WaveInterfaceProperties, pBaseMinipair->WaveInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY));
        }

        // Copy base minipair structure
        RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR));

        RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR));
        RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR));

        pNewTopoFilterDesc->Pins = pNewTopoPins;
        pNewMinipair->TopoDescriptor = pNewTopoFilterDesc;

        // Update it to point to new property list
        pNewMinipair->TopoInterfacePropertyCount = cTopoProperties;
        pNewMinipair->TopoInterfaceProperties = pTopoProperties;
        pNewMinipair->WaveInterfacePropertyCount = cWaveProperties;
        pNewMinipair->WaveInterfaceProperties = pWaveProperties;

        ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory);
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_ERROR, ("%!FUNC!: UpdateCustomEndpointCategory failed, 0x%x", ntStatus));
        }
        else
        {
            *ppCustomMinipair = pNewMinipair;

            pTopoProperties = NULL;
            pWaveProperties = NULL;
            pNewMinipair = NULL;
            pNewTopoFilterDesc = NULL;
            pNewTopoPins = NULL;

            ntStatus = STATUS_SUCCESS;
        }
    }
    else
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (pTopoProperties != NULL)
    {
        ExFreePoolWithTag(pTopoProperties, SYSVAD_POOLTAG);
    }
    if (pWaveProperties != NULL)
    {
        ExFreePoolWithTag(pWaveProperties, SYSVAD_POOLTAG);
    }
    if (pNewMinipair != NULL)
    {
        ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG);
    }
    if (pNewTopoFilterDesc != NULL)
    {
        ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG);
    }
    if (pNewTopoPins != NULL)
    {
        ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG);
    }

    return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
A2dpHpDevice::UpdateCustomEndpointCategory
(
    _In_        PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter,
    _In_        PPCPIN_DESCRIPTOR pCustomMinipairTopoPins,
    _In_        PGUID pCategory
)
{
    NTSTATUS ntStatus = STATUS_NOT_FOUND;
    ULONG cPinCount = 0;
    BOOL FoundCategoryAudio = FALSE;
    BOOL FoundNodeType = FALSE;

    PAGED_CODE();

    cPinCount = pCustomMinipairTopoFilter->PinCount;

    // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO,
    // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category.
    for (ULONG i = 0; i < cPinCount; ++i)
    {
        if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO))
        {
            ASSERT(FoundCategoryAudio == FALSE);
            if (FoundCategoryAudio)
            {
                ntStatus = STATUS_INVALID_DEVICE_STATE;
                DPF(D_ERROR, ("%!FUNC!: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus));
                break;
            }

            FoundCategoryAudio = TRUE;
            continue;
        }

        ASSERT(FoundNodeType == FALSE);
        if (FoundNodeType)
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
            DPF(D_ERROR, ("%!FUNC!: Found more than one applicable Pin, 0x%x", ntStatus));
            break;
        }

        pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory;
        FoundNodeType = TRUE;
        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
VOID
A2dpHpDevice::DeleteCustomEndpointMinipair
(
    _In_        PENDPOINT_MINIPAIR pCustomMinipair
)
{
    PAGED_CODE();

    if (pCustomMinipair != NULL)
    {
        if (pCustomMinipair->TopoInterfaceProperties != NULL)
        {
            ExFreePoolWithTag(const_cast<SYSVAD_DEVPROPERTY*>(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG);
            pCustomMinipair->TopoInterfaceProperties = NULL;
        }
        if (pCustomMinipair->TopoDescriptor != NULL)
        {
            if (pCustomMinipair->TopoDescriptor->Pins != NULL)
            {
                ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG);
                pCustomMinipair->TopoDescriptor->Pins = NULL;
            }
            ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG);
            pCustomMinipair->TopoDescriptor = NULL;
        }
        ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG);
    }
}
    
//=============================================================================
#pragma code_seg("PAGE")
VOID 
A2dpHpDevice::Stop()
/*++

Routine Description:

  Asynchronously called to stop the audio device.
  After returning from this function, there are no more async notifications
  pending (volume, etc.).
  
--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS        ntStatus    = STATUS_SUCCESS;
    eA2dpHpState    state       = eA2dpHpStateInvalid;

    state = (eA2dpHpState) InterlockedExchange((PLONG)&m_State, eA2dpHpStateStopping);
    ASSERT(state == eA2dpHpStateRunning || state == eA2dpHpStateFailed);
    UNREFERENCED_VAR(state);
    
    //
    // Stop async notifications.
    //
    WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait);

    //
    // Wait for work-item.
    //
    WdfWorkItemFlush(m_WorkItem);
    
    //
    // Remove the topology and wave render filters.
    //
    if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave)
    {
        ntStatus = m_Adapter->RemoveEndpointFilters(
            m_SpeakerMiniports,
            m_UnknownSpeakerTopology,
            m_UnknownSpeakerWave);
        
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_ERROR, ("%!FUNC!: RemoveEndpointFilters (A2DP Headphone Speaker): failed, 0x%x", ntStatus));
        }
    }

    //
    // Release port/miniport pointers.
    //
    SAFE_RELEASE(m_UnknownSpeakerTopology);
    SAFE_RELEASE(m_UnknownSpeakerWave);

    //
    // The device is in the stopped state.
    //
    InterlockedExchange((PLONG)&m_State, eA2dpHpStateStopped);

}

#pragma code_seg("PAGE")
NTSTATUS A2dpHpDevice::CreateFilterNames(
    PUNICODE_STRING A2dpHpDeviceSymbolicLinkName
)
/*++

Routine Description:

Creates unique wave and topology filter reference names.

Since each subdevice representing a A2DP sideband interface arrival needs to have a unique
reference string, this function generates a hash on 'A2dpHpDeviceSymbolicLinkName' parameter
and appends to static interface strings.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[%!FUNC!]"));

    NTSTATUS status = STATUS_SUCCESS;
    ULONG hashValue = 0;

    status = RtlHashUnicodeString(A2dpHpDeviceSymbolicLinkName, TRUE, HASH_STRING_ALGORITHM_X65599, &hashValue);
    IF_FAILED_ACTION_JUMP(
        status,
        DPF(D_ERROR, ("%!FUNC!: Failed to create KSCATEGORY_REALTIME wave interface: 0x%x", status)),
        End);

    // create wave interfaces for speaker
    RtlStringCbPrintfW(m_SpeakerWaveNameBuffer, sizeof(m_SpeakerWaveNameBuffer), L"%s-%lu", A2DPHP_SPEAKER_WAVE_NAME, hashValue);
    RtlUnicodeStringInit(&m_SpeakerWaveRefString, m_SpeakerWaveNameBuffer);

    // create topology interfaces for speaker
    RtlStringCbPrintfW(m_SpeakerTopologyNameBuffer, sizeof(m_SpeakerTopologyNameBuffer), L"%s-%lu", A2DPHP_SPEAKER_TOPO_NAME, hashValue);
    RtlUnicodeStringInit(&m_SpeakerTopologyRefString, m_SpeakerTopologyNameBuffer);

End:
    return status;
}
#endif // SYSVAD_A2DP_SIDEBAND


<|start_filename|>general/SimpleMediaSource/SimpleMediaSourceDriver/Queue.h<|end_filename|>
/*++

Module Name:

    queue.h

Abstract:

    This file contains the queue definitions.

Environment:

    User-mode Driver Framework 2

--*/

EXTERN_C_START

//
// This is the context that can be placed per queue
// and would contain per queue information.
//
typedef struct _QUEUE_CONTEXT {

    ULONG PrivateDeviceData;  // just a placeholder

} QUEUE_CONTEXT, *PQUEUE_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)

NTSTATUS
SimpleMediaSourceDriverQueueInitialize(
    _In_ WDFDEVICE Device
    );

//
// Events from the IoQueue object
//
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SimpleMediaSourceDriverEvtIoDeviceControl;
EVT_WDF_IO_QUEUE_IO_STOP SimpleMediaSourceDriverEvtIoStop;

EXTERN_C_END


<|start_filename|>video/IndirectDisplay/IddSampleApp/main.cpp<|end_filename|>
#include <iostream>
#include <vector>

#include <windows.h>
#include <swdevice.h>
#include <conio.h>
#include <wrl.h>

VOID WINAPI
CreationCallback(
    _In_ HSWDEVICE hSwDevice,
    _In_ HRESULT hrCreateResult,
    _In_opt_ PVOID pContext,
    _In_opt_ PCWSTR pszDeviceInstanceId
    )
{
    HANDLE hEvent = *(HANDLE*) pContext;

    SetEvent(hEvent);
    UNREFERENCED_PARAMETER(hSwDevice);
    UNREFERENCED_PARAMETER(hrCreateResult);
    UNREFERENCED_PARAMETER(pszDeviceInstanceId);
}

int __cdecl main(int argc, wchar_t *argv[])
{
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    HANDLE hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    HSWDEVICE hSwDevice;
    SW_DEVICE_CREATE_INFO createInfo = { 0 };
    PCWSTR description = L"Idd Sample Driver";

    // These match the Pnp id's in the inf file so OS will load the driver when the device is created    
    PCWSTR instanceId = L"IddSampleDriver";
    PCWSTR hardwareIds = L"IddSampleDriver\0\0";
    PCWSTR compatibleIds = L"IddSampleDriver\0\0";

    createInfo.cbSize = sizeof(createInfo);
    createInfo.pszzCompatibleIds = compatibleIds;
    createInfo.pszInstanceId = instanceId;
    createInfo.pszzHardwareIds = hardwareIds;
    createInfo.pszDeviceDescription = description;

    createInfo.CapabilityFlags = SWDeviceCapabilitiesRemovable |
                                 SWDeviceCapabilitiesSilentInstall |
                                 SWDeviceCapabilitiesDriverRequired;

    // Create the device
    HRESULT hr = SwDeviceCreate(L"IddSampleDriver",
                                L"HTREE\\ROOT\\0",
                                &createInfo,
                                0,
                                nullptr,
                                CreationCallback,
                                &hEvent,
                                &hSwDevice);
    if (FAILED(hr))
    {
        printf("SwDeviceCreate failed with 0x%lx\n", hr);
        return 1;
    }

    // Wait for callback to signal that the device has been created
    printf("Waiting for device to be created....\n");
    DWORD waitResult = WaitForSingleObject(hEvent, 10*1000);
    if (waitResult != WAIT_OBJECT_0)
    {
        printf("Wait for device creation failed\n");
        return 1;
    }
    printf("Device created\n\n");
    
    // Now wait for user to indicate the device should be stopped
    printf("Press 'x' to exit and destory the software device\n");
    bool bExit = false;
    do
    {
        // Wait for key press
        int key = _getch();

        if (key == 'x' || key == 'X')
        {
            bExit = true;
        }
    }while (!bExit);
    
    // Stop the device, this will cause the sample to be unloaded
    SwDeviceClose(hSwDevice);

    return 0;
}

<|start_filename|>gnss/gnssUmdf/Defaults.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    defaults.h

Abstract:

    This module contains the defaults used within the Umdf Gnss Driver.

Environment:

    driver and application

--*/

#pragma once

#define GNSS_DRIVER_DDK_VERSION GNSS_DRIVER_VERSION_4
#define INVALID_SESSION_ID (0xFFFFFFFF)
#define DEFAULT_FIX_INTERVAL_SECONDS   1

static const double c_MaximumLatitude = 90.0;
static const double c_MinimumLatitude = -90.0;
static const double c_MaximumLongitude = 180.0;
static const double c_MinimumLongitude = -180.0;

#define INIT_POD_GNSS_STRUCT(pName) \
    memset((pName), 0, sizeof(*(pName))); \
    (pName)->Size = sizeof(*(pName)); \
    (pName)->Version = GNSS_DRIVER_DDK_VERSION

const GNSS_FIXDATA g_DefaultGnssFixData =
{
    sizeof(GNSS_FIXDATA),
    GNSS_DRIVER_DDK_VERSION,
    INVALID_SESSION_ID,
    { 0xCD700000, 0x1D48908 }, // an example date/time, Dec/01/2018
    TRUE,
    STATUS_SUCCESS,
    GNSS_FIXDETAIL_BASIC | GNSS_FIXDETAIL_ACCURACY,
    {
        sizeof(GNSS_FIXDATA_BASIC),
        GNSS_DRIVER_DDK_VERSION,
        // Location: Mount Rainier National Park
        46.852273,
        -121.757468,
        0.0,
        0.0,
        0.0
    },
    {
        sizeof(GNSS_FIXDATA_ACCURACY),
        GNSS_DRIVER_DDK_VERSION,
        10 // Meters accuracy
    }
};


<|start_filename|>audio/sysvad/ContosoKeywordDetector.h<|end_filename|>
/*
    Copyright (c) Microsoft Corporation All Rights Reserved

    Contoso voice activation driver definitions

    Hardware manufacturers define identifiers and data structures specific to
    their detection technology to pass voice models, keyword data, speaker
    data, or any other data relevant to their technology.

*/

//
// Identifier for Contoso keyword configuration data.
//
// {6F7DBCC1-202E-498D-99C5-61C36C4EB2DC}
DEFINE_GUID(CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER, 0x6f7dbcc1, 0x202e, 0x498d, 0x99, 0xc5, 0x61, 0xc3, 0x6c, 0x4e, 0xb2, 0xdc);

// {207F3D0C-5C79-496F-A94C-D3D2934DBFA9}
DEFINE_GUID(CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER2, 0x207f3d0c, 0x5c79, 0x496f, 0xa9, 0x4c, 0xd3, 0xd2, 0x93, 0x4d, 0xbf, 0xa9);

// {A537F559-2D67-463B-B10E-BEB750A21F31}
DEFINE_GUID(CONTOSO_KEYWORD1, 0xa537f559, 0x2d67, 0x463b, 0xb1, 0xe, 0xbe, 0xb7, 0x50, 0xa2, 0x1f, 0x31);
// {655E417A-80A5-4A77-B3F1-512EAF67ABCF}
DEFINE_GUID(CONTOSO_KEYWORD2, 0x655e417a, 0x80a5, 0x4a77, 0xb3, 0xf1, 0x51, 0x2e, 0xaf, 0x67, 0xab, 0xcf);

//
// The format of the Contoso keyword pattern matching data.
//
typedef struct
{
    SOUNDDETECTOR_PATTERNHEADER Header;
    LONGLONG                    ContosoDetectorConfigurationData;
} CONTOSO_KEYWORDCONFIGURATION;

//
// The format of the Contoso match result data.
//
typedef struct
{
    SOUNDDETECTOR_PATTERNHEADER Header;
    LONGLONG                    ContosoDetectorResultData;
    ULONGLONG                   KeywordStartTimestamp;
    ULONGLONG                   KeywordStopTimestamp;
    GUID                        EventId;
} CONTOSO_KEYWORDDETECTIONRESULT;


<|start_filename|>audio/sysvad/TabletAudioSample/micinwavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    micinwavtable.h

Abstract:

    Declaration of wave miniport tables for the mic (external: headphone).

--*/

#ifndef _SYSVAD_MICINWAVTABLE_H_
#define _SYSVAD_MICINWAVTABLE_H_

//
// Mic in (external: headphone) range.
//
#define MICIN_DEVICE_MAX_CHANNELS           1       // Max Channels.
#define MICIN_MIN_BITS_PER_SAMPLE_PCM       16      // Min Bits Per Sample
#define MICIN_MAX_BITS_PER_SAMPLE_PCM       16      // Max Bits Per Sample
#define MICIN_MIN_SAMPLE_RATE               8000    // Min Sample Rate
#define MICIN_MAX_SAMPLE_RATE               48000   // Max Sample Rate

//
// Max # of pin instances.
//
#define MICIN_MAX_INPUT_STREAMS             5

//=============================================================================
static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicInPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                8000,
                16000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                11025,
                22050,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                16000,
                32000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                22050,
                44100,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                24000,
                48000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 5
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                32000,
                64000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 6
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                44100,
                88200,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 7
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                48000,
                96000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

//
// Supported modes (only on streaming pins).
//
static
MODE_AND_DEFAULT_FORMAT MicInPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat,
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat,
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH,
        &MicInPinSupportedDeviceFormats[2].DataFormat, // 16KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS,
        &MicInPinSupportedDeviceFormats[4].DataFormat, // 24KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_FAR_FIELD_SPEECH,
        &MicInPinSupportedDeviceFormats[2].DataFormat, // 16KHz
    },
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
static 
PIN_DEVICE_FORMATS_AND_MODES MicInPinDeviceFormatsAndModes[] = 
{
    { 
        BridgePin,
        NULL,
        0,
        NULL,
        0
    },
    {
        SystemCapturePin,
        MicInPinSupportedDeviceFormats,
        SIZEOF_ARRAY(MicInPinSupportedDeviceFormats),
        MicInPinSupportedDeviceModes,
        SIZEOF_ARRAY(MicInPinSupportedDeviceModes)
    }
};

//=============================================================================
static
KSDATARANGE MicInPinDataRangesBridge[] =
{
    {
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE MicInPinDataRangePointersBridge[] =
{
    &MicInPinDataRangesBridge[0]
};

//=============================================================================
static
KSDATARANGE_AUDIO MicInPinDataRangesStream[] =
{
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICIN_DEVICE_MAX_CHANNELS,           
        MICIN_MIN_BITS_PER_SAMPLE_PCM,    
        MICIN_MAX_BITS_PER_SAMPLE_PCM,    
        MICIN_MIN_SAMPLE_RATE,            
        MICIN_MAX_SAMPLE_RATE             
    },
};

static
PKSDATARANGE MicInPinDataRangePointersStream[] =
{
    PKSDATARANGE(&MicInPinDataRangesStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
};

//=============================================================================
static
PCPIN_DESCRIPTOR MicInWaveMiniportPins[] =
{
    // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicInPinDataRangePointersBridge),
            MicInPinDataRangePointersBridge,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },

    // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST
    {
        MICIN_MAX_INPUT_STREAMS,
        MICIN_MAX_INPUT_STREAMS,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicInPinDataRangePointersStream),
            MicInPinDataRangePointersStream,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            &KSAUDFNAME_RECORDING_CONTROL,  
            0
        }
    }
};

//=============================================================================
static
PCNODE_DESCRIPTOR MicInWaveMiniportNodes[] =
{
    // KSNODE_WAVE_ADC
    {
        0,                      // Flags
        NULL,                   // AutomationTable
        &KSNODETYPE_ADC,        // Type
        NULL                    // Name
    }
};

//=============================================================================
static
PCCONNECTION_DESCRIPTOR MicInWaveMiniportConnections[] =
{
    { PCFILTER_NODE,        KSPIN_WAVE_BRIDGE,      KSNODE_WAVE_ADC,     1 },    
    { KSNODE_WAVE_ADC,      0,                      PCFILTER_NODE,       KSPIN_WAVEIN_HOST },
};

//=============================================================================
static
PCPROPERTY_ITEM PropertiesMicInWaveFilter[] =
{
    {
        &KSPROPSETID_General,
        KSPROPERTY_GENERAL_COMPONENTID,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_WaveFilter
    },
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT,
        KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_WaveFilter
    },
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT2,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_WaveFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInWaveFilter, PropertiesMicInWaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR MicInWaveMiniportFilterDescriptor =
{
    0,                                          // Version
    &AutomationMicInWaveFilter,                 // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                   // PinSize
    SIZEOF_ARRAY(MicInWaveMiniportPins),        // PinCount
    MicInWaveMiniportPins,                      // Pins
    sizeof(PCNODE_DESCRIPTOR),                  // NodeSize
    SIZEOF_ARRAY(MicInWaveMiniportNodes),       // NodeCount
    MicInWaveMiniportNodes,                     // Nodes
    SIZEOF_ARRAY(MicInWaveMiniportConnections), // ConnectionCount
    MicInWaveMiniportConnections,               // Connections
    0,                                          // CategoryCount
    NULL                                        // Categories  - use defaults (audio, render, capture)
};

#endif // _SYSVAD_MICINWAVTABLE_H_


<|start_filename|>audio/sysvad/EndpointsCommon/a2dphptopo.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphptopo.h

Abstract:

    Declaration of topology miniport for the mic (external: A2DP headphone).

--*/

#ifndef _SYSVAD_A2DPHPTOPO_H_
#define _SYSVAD_A2DPHPTOPO_H_

NTSTATUS
PropertyHandler_A2dpHpVolumeLevel_BasicSupport( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_A2dpHpMicVolumeLevel(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_A2dpHpMute_BasicSupport( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_A2dpHpMicMute(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
    );

NTSTATUS
PropertyHandler_A2dpHpJackDescription( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest, 
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *    JackDescriptions
    );

NTSTATUS
PropertyHandler_A2dpHpJackDescription2( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *    JackDescriptions
    );

NTSTATUS
PropertyHandler_A2dpHpJackContainerId( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest,
    _In_ ULONG                    cJackDescriptions,
    _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *     JackDescriptions
    );

NTSTATUS 
PropertyHandler_A2dpHpTopoNodeEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

NTSTATUS
PropertyHandler_A2dpHpOneShotReconnect(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
);

NTSTATUS
PropertyHandler_A2dpHpOneShotDisconnect(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
);

#endif // _SYSVAD_A2DPHPTOPO_H_



<|start_filename|>general/toaster/umdf2/func/featured/toaster.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    Toaster.c

Abstract:

    This is a featured version of the toaster function driver. This version
    shows how to register for PNP and Power events, handle create & close
    file requests.

Environment:

    Kernel mode

--*/

#include "toaster.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, ToasterEvtDeviceAdd)
#pragma alloc_text (PAGE, ToasterEvtDeviceFileCreate)
#pragma alloc_text (PAGE, ToasterEvtFileClose)
#pragma alloc_text (PAGE, ToasterEvtDevicePrepareHardware)
#pragma alloc_text (PAGE, ToasterEvtDeviceReleaseHardware)
#pragma alloc_text (PAGE, ToasterEvtDeviceContextCleanup)
#pragma alloc_text (PAGE, ToasterEvtIoDeviceControl)
#pragma alloc_text (PAGE, ToasterEvtIoRead)
#pragma alloc_text (PAGE, ToasterEvtIoWrite)
#endif

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:
    DriverEntry initializes the driver and is the first routine called by the
    system after the driver is loaded. DriverEntry specifies the other entry
    points in the function driver, such as ToasterAddDevice and ToasterUnload.

Parameters Description:

    DriverObject - represents the instance of the function driver that is loaded
    into memory. DriverEntry must initialize members of DriverObject before it
    returns to the caller. DriverObject is allocated by the system before the
    driver is loaded, and it is released by the system after the system unloads
    the function driver from memory.

    RegistryPath - represents the driver specific path in the Registry.
    The function driver can use the path to store driver related data between
    reboots. The path does not store hardware instance specific data.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise.

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    WDF_DRIVER_CONFIG   config;

    KdPrint(("WDF Toaster Function Driver Sample - Featured version\n"));

    //
    // Initialize driver config to control the attributes that
    // are global to the driver. Note that framework by default
    // provides a driver unload routine. If you create any resources
    // in the DriverEntry and want to be cleaned in driver unload,
    // you can override that by manually setting the EvtDriverUnload in the
    // config structure. In general xxx_CONFIG_INIT macros are provided to
    // initialize most commonly used members.
    //

    WDF_DRIVER_CONFIG_INIT(
                            &config,
                            ToasterEvtDeviceAdd
                            );

    //
    // Create a framework driver object to represent our driver.
    //
    status = WdfDriverCreate(
                            DriverObject,
                            RegistryPath,
                            WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
                            &config,          // Driver Config Info
                            WDF_NO_HANDLE
                            );

    if (!NT_SUCCESS(status)) {
        KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status));
    }

    return status;
}


NTSTATUS
ToasterEvtDeviceAdd(
    IN WDFDRIVER       Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
/*++
Routine Description:

    ToasterEvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager. We create and initialize a device object to
    represent a new instance of toaster device.

Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                              status = STATUS_SUCCESS;
    WDF_PNPPOWER_EVENT_CALLBACKS          pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES                 fdoAttributes;
    WDFDEVICE                             device;
    WDF_FILEOBJECT_CONFIG                 fileConfig;
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
    WDF_POWER_POLICY_EVENT_CALLBACKS      powerPolicyCallbacks;
    WDF_IO_QUEUE_CONFIG                   queueConfig;
    //PFDO_DATA                             fdoData;
    WDFQUEUE                              queue;

    UNREFERENCED_PARAMETER(Driver);

    PAGED_CODE();

    KdPrint(("ToasterEvtDeviceAdd called\n"));

    //
    // Initialize the pnpPowerCallbacks structure.  Callback events for PNP
    // and Power are specified here.  If you don't supply any callbacks,
    // the Framework will take appropriate default actions based on whether
    // DeviceInit is initialized to be an FDO, a PDO or a filter device
    // object.
    //

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    //
    // Register PNP callbacks.
    //
    pnpPowerCallbacks.EvtDevicePrepareHardware = ToasterEvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = ToasterEvtDeviceReleaseHardware;
    pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = ToasterEvtDeviceSelfManagedIoInit;

    //
    // Register Power callbacks.
    //
    pnpPowerCallbacks.EvtDeviceD0Entry = ToasterEvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit = ToasterEvtDeviceD0Exit;


    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    //
    // Register power policy event callbacks so that we would know when to
    // arm/disarm the hardware to handle wait-wake and when the wake event
    // is triggered by the hardware.
    //
    WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks);

    //
    // This group of three callbacks allows this sample driver to manage
    // arming the device for wake from the S0 or Sx state.  We don't really
    // differentiate between S0 and Sx state..
    //
    powerPolicyCallbacks.EvtDeviceArmWakeFromS0 = ToasterEvtDeviceArmWakeFromS0;
    powerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = ToasterEvtDeviceDisarmWakeFromS0;
    powerPolicyCallbacks.EvtDeviceWakeFromS0Triggered = ToasterEvtDeviceWakeFromS0Triggered;
    powerPolicyCallbacks.EvtDeviceArmWakeFromSx = ToasterEvtDeviceArmWakeFromSx;
    powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = ToasterEvtDeviceDisarmWakeFromSx;
    powerPolicyCallbacks.EvtDeviceWakeFromSxTriggered = ToasterEvtDeviceWakeFromSxTriggered;

    //
    // Register the power policy callbacks.
    //
    WdfDeviceInitSetPowerPolicyEventCallbacks(DeviceInit, &powerPolicyCallbacks);

    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // framework whether you are interested in handling Create, Close and
    // Cleanup requests that gets genereate when an application or another
    // kernel component opens an handle to the device. If you don't register,
    // the framework default behaviour would be complete these requests
    // with STATUS_SUCCESS. A driver might be interested in registering these
    // events if it wants to do security validation and also wants to maintain
    // per handle (fileobject) context.
    //

    WDF_FILEOBJECT_CONFIG_INIT(
                            &fileConfig,
                            ToasterEvtDeviceFileCreate,
                            ToasterEvtFileClose,
                            WDF_NO_EVENT_CALLBACK // not interested in Cleanup
                            );

    WdfDeviceInitSetFileObjectConfig(DeviceInit,
                                       &fileConfig,
                                       WDF_NO_OBJECT_ATTRIBUTES);

    //
    // Now specify the size of device extension where we track per device
    // context. Along with setting the context type as shown below, you should also
    // specify the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME in header to specify the
    // accessor function name.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA);

    //
    // Set a context cleanup routine to cleanup any resources that are not
    // parent to this device. This cleanup will be called in the context of
    // pnp remove-device when the framework deletes the device object.
    //
    fdoAttributes.EvtCleanupCallback = ToasterEvtDeviceContextCleanup;

    //
    // DeviceInit is completely initialized. So call the framework to create the
    // device and attach it to the lower stack.
    //
    status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device);
    if (!NT_SUCCESS(status)) {
        KdPrint( ("WdfDeviceCreate failed with Status code 0x%x\n", status));
        return status;
    }

    //
    // Get the device context by using accessor function specified in
    // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for FDO_DATA.
    //
    //fdoData = ToasterFdoGetData(device);

    //
    // Tell the Framework that this device will need an interface so that
    // application can find our device and talk to it.
    //
    status = WdfDeviceCreateDeviceInterface(
                 device,
                 (LPGUID) &GUID_DEVINTERFACE_TOASTER,
                 NULL
             );

    if (!NT_SUCCESS (status)) {
        KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
        return status;
    }

    //
    // Register I/O callbacks to tell the framework that you are interested
    // in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests.
    // In case a specific handler is not specified for one of these,
    // the request will be dispatched to the EvtIoDefault handler, if any.
    // If there is no EvtIoDefault handler, the request will be failed with
    // STATUS_INVALID_DEVICE_REQUEST.
    // WdfIoQueueDispatchParallel means that we are capable of handling
    // all the I/O request simultaneously and we are responsible for protecting
    // data that could be accessed by these callbacks simultaneously.
    // A default queue gets all the requests that are not
    // configure-fowarded using WdfDeviceConfigureRequestDispatching.
    //
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,
                             WdfIoQueueDispatchParallel); // EvtIoCancel

    queueConfig.EvtIoRead = ToasterEvtIoRead;
    queueConfig.EvtIoWrite = ToasterEvtIoWrite;
    queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl;

    //
    // By default, Static Driver Verifier (SDV) displays a warning if it 
    // doesn't find the EvtIoStop callback on a power-managed queue. 
    // The 'assume' below causes SDV to suppress this warning. If the driver 
    // has not explicitly set PowerManaged to WdfFalse, the framework creates
    // power-managed queues when the device is not a filter driver.  Normally 
    // the EvtIoStop is required for power-managed queues, but for this driver
    // it is not needed b/c the driver doesn't hold on to the requests or 
    // forward them to other drivers. This driver completes the requests 
    // directly in the queue's handlers. If the EvtIoStop callback is not 
    // implemented, the framework waits for all driver-owned requests to be
    // done before moving in the Dx/sleep states or before removing the 
    // device, which is the correct behavior for this type of driver.
    // If the requests were taking an indeterminate amount of time to complete,
    // or if the driver forwarded the requests to a lower driver/another stack,
    // the queue should have an EvtIoStop/EvtIoResume.
    //
    __analysis_assume(queueConfig.EvtIoStop != 0);
    status = WdfIoQueueCreate(device,
                              &queueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &queue
                              );
    __analysis_assume(queueConfig.EvtIoStop == 0);

    if (!NT_SUCCESS (status)) {

        KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
        return status;
    }

    //
    // Set the idle power policy to put the device to Dx if the device is not used
    // for the specified IdleTimeout time. Since this is a virtual device we
    // tell the framework that we cannot wake ourself if we sleep in S0. Only
    // way the device can be brought to D0 is if the device recieves an I/O from
    // the system.
    //
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0);
    idleSettings.IdleTimeout = 60000; // 60 secs idle timeout
    status = WdfDeviceAssignS0IdleSettings(device, &idleSettings);
    if (!NT_SUCCESS(status)) {
        KdPrint( ("WdfDeviceAssignS0IdleSettings failed 0x%x\n", status));
        return status;
    }

    //
    // Set the wait-wake policy.
    //

    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
    status = WdfDeviceAssignSxWakeSettings(device, &wakeSettings);
    if (!NT_SUCCESS(status)) {
        //
        // We are probably enumerated on a bus that doesn't support Sx-wake.
        // Let us not fail the device add just because we aren't able to support
        // wait-wake. I will let the user of this sample decide how important it's
        // to support wait-wake for their hardware and return appropriate status.
        //
        KdPrint( ("WdfDeviceAssignSxWakeSettings failed 0x%x\n", status));
        status = STATUS_SUCCESS;
    }

    return status;
}

NTSTATUS
ToasterEvtDevicePrepareHardware(
    WDFDEVICE      Device,
    WDFCMRESLIST   ResourcesRaw,
    WDFCMRESLIST   ResourcesTranslated
    )
/*++

Routine Description:

    EvtDevicePrepareHardware event callback performs operations that are
    necessary to make the driver's device operational. The framework calls the
    driver's EvtDevicePrepareHardware callback when the PnP manager sends an
    IRP_MN_START_DEVICE request to the driver stack.

    Specifically, most drivers will use this callback to map resources.  USB
    drivers may use it to get device descriptors, config descriptors and to
    select configs.

    Some drivers may choose to download firmware to a device in this callback,
    but that is usually only a good choice if the device firmware won't be
    destroyed by a D0 to D3 transition.  If firmware will be gone after D3,
    then firmware downloads should be done in EvtDeviceD0Entry, not here.

Arguments:

    Device - Handle to a framework device object.

    ResourcesRaw - Handle to a collection of framework resource objects.
                This collection identifies the raw (bus-relative) hardware
                resources that have been assigned to the device.

    ResourcesTranslated - Handle to a collection of framework resource objects.
                This collection identifies the translated (system-physical)
                hardware resources that have been assigned to the device.
                The resources appear from the CPU's point of view.
                Use this list of resources to map I/O space and
                device-accessible memory into virtual address space

Return Value:

    WDF status code

--*/
{
    //PFDO_DATA   fdoData;
    NTSTATUS status = STATUS_SUCCESS;
    ULONG i;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;

    //fdoData = ToasterFdoGetData(Device);

    UNREFERENCED_PARAMETER(Device);
    UNREFERENCED_PARAMETER(ResourcesRaw);

    KdPrint(("ToasterEvtDevicePrepareHardware called\n"));

    PAGED_CODE();
    //
    // Get the number item that are currently in Resources collection and
    // iterate thru as many times to get more information about the each items
    //
    for (i=0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) {

        descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

        switch(descriptor->Type) {

        case CmResourceTypePort:

            KdPrint(("I/O Port: (%x) Length: (%d)\n",
                    descriptor->u.Port.Start.LowPart,
                    descriptor->u.Port.Length));
            break;

        case CmResourceTypeMemory:

            KdPrint(("Memory: (%x) Length: (%d)\n",
                    descriptor->u.Memory.Start.LowPart,
                    descriptor->u.Memory.Length));
            break;
        case CmResourceTypeInterrupt:

            KdPrint(("Interrupt level: 0x%0x, Vector: 0x%0x, Affinity: 0x%0Ix\n",
                descriptor->u.Interrupt.Level,
                descriptor->u.Interrupt.Vector,
                descriptor->u.Interrupt.Affinity));
            break;

        default:
            break;
        }

    }

    return status;

}

NTSTATUS
ToasterEvtDeviceReleaseHardware(
    IN  WDFDEVICE    Device,
    IN  WDFCMRESLIST ResourcesTranslated
    )
/*++

Routine Description:

    EvtDeviceReleaseHardware is called by the framework whenever the PnP manager
    is revoking ownership of our resources.  This may be in response to either
    IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE.  The callback is made before
    passing down the IRP to the lower driver.

    In this callback, do anything necessary to free those resources.

Arguments:

    Device - Handle to a framework device object.

    ResourcesTranslated - Handle to a collection of framework resource objects.
                This collection identifies the translated (system-physical)
                hardware resources that have been assigned to the device.
                The resources appear from the CPU's point of view.
                Use this list of resources to map I/O space and
                device-accessible memory into virtual address space

Return Value:

    NTSTATUS - Failures will be logged, but not acted on.

--*/
{
    //PFDO_DATA   fdoData;

    UNREFERENCED_PARAMETER(Device);
    UNREFERENCED_PARAMETER(ResourcesTranslated);

    KdPrint(("ToasterEvtDeviceReleaseHardware called\n"));

    PAGED_CODE();

    //fdoData = ToasterFdoGetData(Device);
    //
    // Unmap any I/O ports, registers that you mapped in PrepareHardware.
    // Disconnecting from the interrupt will be done automatically by the framework.
    //
    return STATUS_SUCCESS;
}

NTSTATUS
ToasterEvtDeviceSelfManagedIoInit(
    IN  WDFDEVICE Device
    )
/*++

Routine Description:

    EvtDeviceSelfManagedIoInit is called it once for each device,
    after the framework has called the driver's EvtDeviceD0Entry
    callback function for the first time. The framework does not
    call the EvtDeviceSelfManagedIoInit callback function again for
    that device, unless the device is removed and reconnected, or
    the drivers are reloaded.

    The EvtDeviceSelfManagedIoInit callback function must initialize
    the self-managed I/O operations that the driver will handle
    for the device.

    This function is not marked pageable because this function is in the
    device power up path. When a function is marked pagable and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behavior because the client driver will have to wait
    until the system drivers can service this page fault.

Arguments:

    Device - Handle to a framework device object.

Return Value:

    NTSTATUS - Failures will result in the device stack being torn down.

--*/
{
    UNREFERENCED_PARAMETER(Device);
    
    KdPrint(("ToasterEvtDeviceSelfManagedIoInit called\n"));

    return STATUS_SUCCESS;
}


VOID
ToasterEvtDeviceContextCleanup(
    IN WDFOBJECT Device
    )
/*++

Routine Description:

   EvtDeviceContextCleanup event callback must perform any operations that are
   necessary before the specified device is removed. The framework calls
   the driver's EvtDeviceContextCleanup callback when the device is deleted in response
   to IRP_MN_REMOVE_DEVICE request.

Arguments:

    Device - Handle to a framework device object.

Return Value:

    None

--*/
{
    //PFDO_DATA   fdoData;
    UNREFERENCED_PARAMETER(Device);
    KdPrint( ("ToasterEvtDeviceContextCleanup called\n"));

    PAGED_CODE();

    //fdoData = ToasterFdoGetData((WDFDEVICE)Device);

    return;
}

VOID
ToasterEvtDeviceFileCreate (
    IN WDFDEVICE Device,
    IN WDFREQUEST Request,
    IN WDFFILEOBJECT FileObject
    )
/*++

Routine Description:

    The framework calls a driver's EvtDeviceFileCreate callback
    when the framework receives an IRP_MJ_CREATE request.
    The system sends this request when a user application opens the
    device to perform an I/O operation, such as reading or writing to a device.
    This callback is called in the context of the thread
    that created the IRP_MJ_CREATE request.

Arguments:

    Device - Handle to a framework device object.
    FileObject - Pointer to fileobject that represents the open handle.
    CreateParams - Parameters for create

Return Value:

    None

--*/
{
    //PFDO_DATA   fdoData;

    UNREFERENCED_PARAMETER(FileObject);
    UNREFERENCED_PARAMETER(Device);

    KdPrint( ("ToasterEvtDeviceFileCreate %p\n", Device));

    PAGED_CODE ();

    //
    // Get the device context given the device handle.
    //
    //fdoData = ToasterFdoGetData(Device);

    WdfRequestComplete(Request, STATUS_SUCCESS);

    return;
}


VOID
ToasterEvtFileClose (
    IN WDFFILEOBJECT    FileObject
    )

/*++

Routine Description:

   EvtFileClose is called when all the handles represented by the FileObject
   is closed and all the references to FileObject is removed. This callback
   may get called in an arbitrary thread context instead of the thread that
   called CloseHandle. If you want to delete any per FileObject context that
   must be done in the context of the user thread that made the Create call,
   you should do that in the EvtDeviceCleanp callback.

Arguments:

    FileObject - Pointer to fileobject that represents the open handle.

Return Value:

    None
    
--*/
{
    //PFDO_DATA    fdoData;
    UNREFERENCED_PARAMETER(FileObject);
    PAGED_CODE ();

    //fdoData = ToasterFdoGetData(WdfFileObjectGetDevice(FileObject));

    KdPrint( ("ToasterEvtFileClose\n"));

    return;
}



VOID
ToasterEvtIoRead (
    WDFQUEUE      Queue,
    WDFREQUEST    Request,
    size_t        Length
    )
/*++

Routine Description:

    Performs read to the toaster device. This event is called when the
    framework receives IRP_MJ_READ requests.

Arguments:

    Queue -  Handle to the framework queue object that is associated with the
            I/O request.
    Request - Handle to a framework request object.

    Lenght - Length of the data buffer associated with the request.
             The default property of the queue is to not dispatch
             zero lenght read & write requests to the driver and
             complete is with status success. So we will never get
             a zero length request.

Return Value:

    None

--*/
{
    NTSTATUS    status;
    ULONG_PTR bytesCopied =0;
    WDFMEMORY memory;

    UNREFERENCED_PARAMETER(Length);
    UNREFERENCED_PARAMETER(Queue);

    PAGED_CODE();

    KdPrint(("ToasterEvtIoRead: Request: 0x%p, Queue: 0x%p\n",
             Request, Queue));

    //
    // Get the request memory and perform read operation here
    //
    status = WdfRequestRetrieveOutputMemory(Request, &memory);
    if(NT_SUCCESS(status) ) {
        //
        // Copy data into the memory buffer using WdfMemoryCopyFromBuffer
        //
    }

    WdfRequestCompleteWithInformation(Request, status, bytesCopied);

}

VOID
ToasterEvtIoWrite (
    WDFQUEUE      Queue,
    WDFREQUEST    Request,
    size_t        Length
    )
/*++

Routine Description:

    Performs write to the toaster device. This event is called when the
    framework receives IRP_MJ_WRITE requests.

Arguments:

    Queue -  Handle to the framework queue object that is associated with the
            I/O request.
    Request - Handle to a framework request object.

    Lenght - Length of the data buffer associated with the request.
                 The default property of the queue is to not dispatch
                 zero lenght read & write requests to the driver and
                 complete is with status success. So we will never get
                 a zero length request.

Return Value:

    None
   
--*/

{
    NTSTATUS    status;
    WDFMEMORY memory;

    UNREFERENCED_PARAMETER(Queue);

    KdPrint(("ToasterEvtIoWrite. Request: 0x%p, Queue: 0x%p\n",
                                Request, Queue));
    PAGED_CODE();

    //
    // Get the request buffer and perform write operation here
    //
    status = WdfRequestRetrieveInputMemory(Request, &memory);
    if(NT_SUCCESS(status) ) {
        //
        // 1) Use WdfMemoryCopyToBuffer to copy data from the request
        // to driver buffer.
        // 2) Or get the buffer pointer from the request by calling
        // WdfRequestRetrieveInputBuffer to  transfer data to the hw
        // 3) Or you can get the buffer pointer from the memory handle
        // by calling WdfMemoryGetBuffer to  transfer data to the hw.
        //
    }

    WdfRequestCompleteWithInformation(Request, status, Length);

}


VOID
ToasterEvtIoDeviceControl(
    IN WDFQUEUE     Queue,
    IN WDFREQUEST   Request,
    IN size_t       OutputBufferLength,
    IN size_t       InputBufferLength,
    IN ULONG        IoControlCode
    )
/*++
Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework request object.

    OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.

Return Value:

    None

--*/
{
    NTSTATUS             status= STATUS_SUCCESS;
    WDF_DEVICE_STATE     deviceState;
    WDFDEVICE            hDevice = WdfIoQueueGetDevice(Queue);


    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

    KdPrint(("ToasterEvtIoDeviceControl called\n"));

    PAGED_CODE();

    switch (IoControlCode) {

    case IOCTL_TOASTER_DONT_DISPLAY_IN_UI_DEVICE:
        //
        // This is just an example on how to hide your device in the
        // device manager. Please remove this code when you adapt
        // this sample for your hardware.
        //
        WDF_DEVICE_STATE_INIT(&deviceState);
        deviceState.DontDisplayInUI = WdfTrue;
        WdfDeviceSetDeviceState(
            hDevice,
            &deviceState
            );
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
    }

    //
    // Complete the Request.
    //
    WdfRequestCompleteWithInformation(Request, status, (ULONG_PTR) 0);

}




<|start_filename|>audio/sysvad/TabletAudioSample/micarray2wavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:
    micarray2wavtable.h

Abstract:

    Declaration of wave miniport tables for the rear mic array.

--*/

#ifndef _SYSVAD_MICARRAY2WAVTABLE_H_
#define _SYSVAD_MICARRAY2WAVTABLE_H_

//
// Mic array range.
//
#define MICARRAY2_RAW_CHANNELS                   2       // Channels for raw mode
#define MICARRAY2_PROCESSED_CHANNELS             1       // Channels for default mode
#define MICARRAY2_DEVICE_MAX_CHANNELS            2       // Max channels overall
#define MICARRAY2_16_BITS_PER_SAMPLE_PCM         16      // 16 Bits Per Sample
#define MICARRAY2_32_BITS_PER_SAMPLE_PCM         32      // 32 Bits Per Sample
#define MICARRAY2_RAW_SAMPLE_RATE                48000   // Raw sample rate
#define MICARRAY2_PROCESSED_MIN_SAMPLE_RATE      8000    // Min Sample Rate
#define MICARRAY2_PROCESSED_MAX_SAMPLE_RATE      48000   // Max Sample Rate

//
// Max # of pin instances.
//
#define MICARRAY2_MAX_INPUT_STREAMS              4

//=============================================================================
static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArray2PinSupportedDeviceFormats[] =
{
    // 0 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this first element as the proposed DEFAULT format
    // 48 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                48000,
                96000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 1
    // 8 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                8000,
                16000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 2
    // 11.025 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                11025,
                22050,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 3 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed SPEECH format
    // 16 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,      // One channel
                16000,  // 16KHz
                32000,  // average bytes per second
                2,      // 2 bytes per frame
                16,     // 16 bits per sample container
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,         // valid bits per sample
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 4
    // 22.05 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                22050,
                44100,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 5 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed COMMUNICATIONS format
    // 24 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                24000,
                48000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 6
    // 32 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                32000,
                64000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 7
    // 44.1 KHz 16-bit mono
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                44100,
                88200,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    // 8 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this last element as the proposed RAW format
    // 48 KHz 32-bit 2 channels
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                384000,
                8,
                32,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            32,
            0,                                      // No channel configuration for unprocessed mic array
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
};

//
// Supported modes (only on streaming pins).
//
static
MODE_AND_DEFAULT_FORMAT MicArray2PinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        &MicArray2PinSupportedDeviceFormats[SIZEOF_ARRAY(MicArray2PinSupportedDeviceFormats)-1].DataFormat
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &MicArray2PinSupportedDeviceFormats[0].DataFormat
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH,
        &MicArray2PinSupportedDeviceFormats[3].DataFormat
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS,
        &MicArray2PinSupportedDeviceFormats[5].DataFormat
    }
};

// This structure goes along with the keyword supported device format,
// to indicate that the audio is interleaved microphone and loopback data.
INTERLEAVED_AUDIO_FORMAT_INFORMATION InterleavedFormatInformation = 
{
    sizeof(INTERLEAVED_AUDIO_FORMAT_INFORMATION),
    4, // Microphone channel count
    0, // Microphone channel start position
    0, // No channel configuration for unprocessed mic array
    2, // Channel count for interleaved data
    2,  // Interleaved audio start position
    KSAUDIO_SPEAKER_STEREO // Channel mask for interleaved loopback data
};

//=============================================================================
static
KSDATAFORMAT_WAVEFORMATEXTENSIBLE KeywordPin2SupportedDeviceFormats[] =
{
    {
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                6,       // six channels, make sure this matches InterleavedFormatInformation, if interleaving loopback audio
                16000,   // 16KHz
                192000,  // average bytes per second
                12,      // 8 bytes per frame
                16,      // 16 bits per sample container
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,         // valid bits per sample
            0, // No channel configuration for unprocessed mic array
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
};

static
MODE_AND_DEFAULT_FORMAT KeywordPin2SupportedDeviceModes[] =
{
    // NOTE: Because we're using the KWS apo as an EFX, this endpoint may only support a single mode.
    // You can not have an EFX when there are multiple modes supported by the driver, EFX must be implemented
    // in the driver in that case. Because we are exposing a single mode the audio engine can insert the KWSAPO
    // as an EFX. Further, in the INF associated to this driver the KWS EFX APO is registered as supporting default
    // mode. That registration is not coupled to the mode enumerated in this list.
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH,
        &KeywordPin2SupportedDeviceFormats[SIZEOF_ARRAY(KeywordPin2SupportedDeviceFormats) - 1].DataFormat
    },
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
//
static 
PIN_DEVICE_FORMATS_AND_MODES MicArray2PinDeviceFormatsAndModes[] = 
{
    {
        BridgePin,
        NULL,
        0,
        NULL,
        0
    },
    {
        SystemCapturePin,
        MicArray2PinSupportedDeviceFormats,
        SIZEOF_ARRAY(MicArray2PinSupportedDeviceFormats),
        MicArray2PinSupportedDeviceModes,
        SIZEOF_ARRAY(MicArray2PinSupportedDeviceModes)
    },
    {
        KeywordCapturePin,
        KeywordPin2SupportedDeviceFormats,
        SIZEOF_ARRAY(KeywordPin2SupportedDeviceFormats),
        KeywordPin2SupportedDeviceModes,
        SIZEOF_ARRAY(KeywordPin2SupportedDeviceModes)
    }
};

//=============================================================================
// Data ranges
//
// See CMiniportWaveRT::DataRangeIntersection.
//
// Both mono and two-channel formats are supported for the mic arrays. The
// design of this sample driver's data range intersection handler requires a
// separate data for each supported channel count.
//
static
KSDATARANGE_AUDIO MicArray2PinDataRangesRawStream[] =
{
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_RAW_CHANNELS,           
        MICARRAY2_32_BITS_PER_SAMPLE_PCM,    
        MICARRAY2_32_BITS_PER_SAMPLE_PCM,    
        MICARRAY2_RAW_SAMPLE_RATE,            
        MICARRAY2_RAW_SAMPLE_RATE             
    },
};

static
KSDATARANGE_AUDIO MicArray2PinDataRangesProcessedStream[] =
{
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_PROCESSED_MIN_SAMPLE_RATE,
        MICARRAY2_PROCESSED_MIN_SAMPLE_RATE
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        11025,
        11025
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        16000,
        16000
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        22050,
        22050
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        24000,
        24000
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        32000,
        32000
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        44100,
        44100
    },
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        MICARRAY2_PROCESSED_CHANNELS,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_16_BITS_PER_SAMPLE_PCM,
        MICARRAY2_PROCESSED_MAX_SAMPLE_RATE,
        MICARRAY2_PROCESSED_MAX_SAMPLE_RATE
    },
};

// if MicArray2PinDataRangesProcessedStream is changed, we MUST update MicArray2PinDataRangePointersStream too!
C_ASSERT(SIZEOF_ARRAY(MicArray2PinDataRangesProcessedStream) == 8);

static
PKSDATARANGE MicArray2PinDataRangePointersStream[] =
{
    // All supported device formats should be listed in the DataRange.
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[1]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[2]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[3]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[4]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[5]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[6]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesProcessedStream[7]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&MicArray2PinDataRangesRawStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
};

//=============================================================================
static
KSDATARANGE MicArray2PinDataRangesBridge[] =
{
    {
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE MicArray2PinDataRangePointersBridge[] =
{
    &MicArray2PinDataRangesBridge[0]
};

static
KSDATARANGE_AUDIO KeywordPin2DataRangesStream[] =
{
    {
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        6,      // max channels
        16,     // min bits per sample
        16,     // max bits per sample
        16000,  // min sample rate
        16000   // max sample rate
    },
};

static
PKSDATARANGE KeywordPin2DataRangePointersStream[] =
{
    PKSDATARANGE(&KeywordPin2DataRangesStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
};

//=============================================================================
static
PCPIN_DESCRIPTOR MicArray2WaveMiniportPins[] =
{
    // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicArray2PinDataRangePointersBridge),
            MicArray2PinDataRangePointersBridge,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST
    {
        MICARRAY2_MAX_INPUT_STREAMS,
        MICARRAY2_MAX_INPUT_STREAMS,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(MicArray2PinDataRangePointersStream),
            MicArray2PinDataRangePointersStream,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            &KSAUDFNAME_RECORDING_CONTROL,  
            0
        }
    },
    // Keyword Detector Streaming Pin (Capture) KSPIN_WAVEIN_KEYWORD
    {
        1,
        1,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(KeywordPin2DataRangePointersStream),
            KeywordPin2DataRangePointersStream,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_SINK,
            &KSNODETYPE_AUDIO_KEYWORDDETECTOR,
            NULL,
            0
        }
    }
};

//=============================================================================
static
PCNODE_DESCRIPTOR MicArray2WaveMiniportNodes[] =
{
    // KSNODE_WAVE_ADC
    {
        0,                      // Flags
        NULL,                   // AutomationTable
        &KSNODETYPE_ADC,        // Type
        NULL                    // Name
    }
};

//=============================================================================
static
PCCONNECTION_DESCRIPTOR MicArray2WaveMiniportConnections[] =
{
    { PCFILTER_NODE,        KSPIN_WAVE_BRIDGE,      KSNODE_WAVE_ADC,     1 },    
    { KSNODE_WAVE_ADC,      0,                      PCFILTER_NODE,       KSPIN_WAVEIN_HOST },
    { KSNODE_WAVE_ADC,      0,                      PCFILTER_NODE,       KSPIN_WAVEIN_KEYWORD },
};

//=============================================================================
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorMatchResult);

DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorReset2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorStreamingSupport2);
DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_InterleavedFormatInformation);


static
SYSVADPROPERTY_ITEM PropertiesMicArray2WaveFilter[] =
{
    {
        {
            &KSPROPSETID_General,
            KSPROPERTY_GENERAL_COMPONENTID,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_Pin,
            KSPROPERTY_PIN_PROPOSEDATAFORMAT,
            KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_Pin,
            KSPROPERTY_PIN_PROPOSEDATAFORMAT2,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter,
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_AudioEffectsDiscovery,
            KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            PropertyHandler_WaveFilter
        },
        0,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector,
            KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        0,
        sizeof(CONTOSO_SUPPORTEDPATTERNSVALUE),
        CMiniportWaveRT_Get_SoundDetectorSupportedPatterns,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector,
            KSPROPERTY_SOUNDDETECTOR_PATTERNS,
            KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        0,
        (sizeof(KSMULTIPLE_ITEM) + sizeof(CONTOSO_KEYWORDCONFIGURATION)),
        NULL,
        CMiniportWaveRT_Set_SoundDetectorPatterns,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector,
            KSPROPERTY_SOUNDDETECTOR_ARMED,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        0,
        sizeof(BOOL),
        CMiniportWaveRT_Get_SoundDetectorArmed,
        CMiniportWaveRT_Set_SoundDetectorArmed,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector,
            KSPROPERTY_SOUNDDETECTOR_MATCHRESULT,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        0,
        sizeof(CONTOSO_KEYWORDDETECTIONRESULT),
        CMiniportWaveRT_Get_SoundDetectorMatchResult,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector2,
            KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSSOUNDDETECTORPROPERTY) - sizeof(KSPROPERTY),
        sizeof(CONTOSO_SUPPORTEDPATTERNSVALUE),
        CMiniportWaveRT_Get_SoundDetectorSupportedPatterns2,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector2,
            KSPROPERTY_SOUNDDETECTOR_PATTERNS,
            KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSSOUNDDETECTORPROPERTY) - sizeof(KSPROPERTY),
        (sizeof(KSMULTIPLE_ITEM) + sizeof(CONTOSO_KEYWORDCONFIGURATION)),
        NULL,
        CMiniportWaveRT_Set_SoundDetectorPatterns2,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector2,
            KSPROPERTY_SOUNDDETECTOR_ARMED,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSSOUNDDETECTORPROPERTY) - sizeof(KSPROPERTY),
        sizeof(BOOL),
        CMiniportWaveRT_Get_SoundDetectorArmed2,
        CMiniportWaveRT_Set_SoundDetectorArmed2,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector2,
            KSPROPERTY_SOUNDDETECTOR_RESET,
            KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSSOUNDDETECTORPROPERTY) - sizeof(KSPROPERTY),
        sizeof(BOOL),
        NULL,
        CMiniportWaveRT_Set_SoundDetectorReset2,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_SoundDetector2,
            KSPROPERTY_SOUNDDETECTOR_STREAMINGSUPPORT,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSSOUNDDETECTORPROPERTY) - sizeof(KSPROPERTY),
        sizeof(BOOL),
        CMiniportWaveRT_Get_SoundDetectorStreamingSupport2,
        NULL,
        NULL,
        NULL,
        0
    },
    {
        {
            &KSPROPSETID_InterleavedAudio,
            KSPROPERTY_INTERLEAVEDAUDIO_FORMATINFORMATION,
            KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
            SysvadPropertyDispatch,
        },
        sizeof(KSP_PIN) - sizeof(KSPROPERTY),
        sizeof(INTERLEAVED_AUDIO_FORMAT_INFORMATION),
        CMiniportWaveRT_Get_InterleavedFormatInformation,
        NULL,
        NULL,
        &InterleavedFormatInformation, // format interleaving information for this endpoint
        sizeof(InterleavedFormatInformation)
    },
};

NTSTATUS CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected(
    _In_  PPCEVENT_REQUEST EventRequest
    );

static
PCEVENT_ITEM EventsMicArray2WaveFilter[] =
{
    {
        &KSEVENTSETID_SoundDetector,
        KSEVENT_SOUNDDETECTOR_MATCHDETECTED,
        KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
        CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected,
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationMicArray2WaveFilter, PropertiesMicArray2WaveFilter, EventsMicArray2WaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR MicArray2WaveMiniportFilterDescriptor =
{
    0,                                              // Version
    &AutomationMicArray2WaveFilter,                  // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                       // PinSize
    SIZEOF_ARRAY(MicArray2WaveMiniportPins),         // PinCount
    MicArray2WaveMiniportPins,                       // Pins
    sizeof(PCNODE_DESCRIPTOR),                      // NodeSize
    SIZEOF_ARRAY(MicArray2WaveMiniportNodes),        // NodeCount
    MicArray2WaveMiniportNodes,                      // Nodes
    SIZEOF_ARRAY(MicArray2WaveMiniportConnections),  // ConnectionCount
    MicArray2WaveMiniportConnections,                // Connections
    0,                                              // CategoryCount
    NULL                                            // Categories  - use defaults (audio, render, capture)
};


#endif // _SYSVAD_MICARRAY2WAVTABLE_H_



<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXHelper.h<|end_filename|>
#pragma once

#include <stdafx.h>

#include <wil\resultmacros.h>
#include <wil\com.h>
#include <audioclient.h>
#include <audioclientp.h>
#include <audiosessiontypesp.h>
#include <mmdeviceapi.h>
#include <mmdeviceapip.h>
#include <endpointvolume.h>
#include "etwlistener.h"
#include <functiondiscoverykeys.h>
#include <slpublic.h>

#include <DevPKey.h>
#include <AudioEngineBaseAPOP.h>
#include "TestMediaType.h"
#include <audioenginebaseapo.h>

#include <list>
#include <memory>

using namespace std;
using namespace wil;
using namespace WEX::Logging;
using namespace WEX::Common;

bool IsCapXAPO(GUID clsid);
void GetClsidsFromVar(const PROPVARIANT& var, std::vector<GUID>& guids, _Outptr_ PWSTR* ppszFormattedClsids, std::vector<GUID>& capxGuids, _Outptr_ PWSTR* ppszCapXFormattedClsids);
bool EndpointContainsCapx(IMMDevice* pDevice);
void EndpointsWithCapx(std::vector<wil::com_ptr<IMMDevice>>& capxEndpoints);

<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/Wrappers.h<|end_filename|>
//
// Wrappers.h -- Copyright (c) Microsoft Corporation
//
// Author: zaneh
//
// Description:
//
//   Wrapper methods for APIs which can throw
//

// Collects exception data and sends it to Watson without crashing Audio DG.
// This method can only be called from inside the __except condition statement
// as a filter function.
inline DWORD CollectExceptionDataAndContinue(LPEXCEPTION_POINTERS exceptionInfo)
{
    LONG lResult;
    DWORD dwType, dwVal, dwSize=sizeof(DWORD);

    // Check to see if we should allow the process create a crash dump
    lResult = RegGetValue(HKEY_LOCAL_MACHINE, REGKEY_AUDIO_SOFTWARE, PREVENT_APOTEST_CRASH_OR_REPORT_ON_APO_EXCEPTION, RRF_RT_DWORD, &dwType, &dwVal, &dwSize);

    // If we are allowing dumps, report an exception to Watson before continuing
    if ((lResult != ERROR_SUCCESS) || (dwVal == 0))
    {
        RtlReportException(exceptionInfo->ExceptionRecord, exceptionInfo->ContextRecord, RTL_WER_NO_DEBUG | RTL_WER_NON_FATAL);
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

HRESULT QIInternal(IAudioProcessingObject* pIAPO, _In_ REFIID riid, _COM_Outptr_ void** ppvObject)
{
    __try
    {
        return pIAPO->QueryInterface(riid, ppvObject);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

HRESULT UnknownQIInternal(IUnknown* pIAPO, _In_ REFIID riid, _COM_Outptr_ void** ppvObject)
{
    __try
    {
        return pIAPO->QueryInterface(riid, ppvObject);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

HRESULT IsOutputFormatSupported(IAudioProcessingObject* pIAPO, IAudioMediaType* pInputFormat, IAudioMediaType* pRequestedOutputFormat, IAudioMediaType** ppSupportedOutputFormat)
{
    __try
    {
        return pIAPO->IsOutputFormatSupported(pInputFormat, pRequestedOutputFormat, ppSupportedOutputFormat);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

HRESULT LockForProcess(IAudioProcessingObjectConfiguration* pIAPOConfig,
        _In_ UINT32 u32NumInputConnections, 
        _In_reads_(u32NumInputConnections) APO_CONNECTION_DESCRIPTOR** ppInputConnections,
        _In_ UINT32 u32NumOutputConnections, 
        _In_reads_(u32NumOutputConnections) APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
{
    __try
    {
        return pIAPOConfig->LockForProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

HRESULT UnlockForProcess(IAudioProcessingObjectConfiguration* pIAPOConfig)
{
    __try
    {
        return pIAPOConfig->UnlockForProcess();
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

void APOProcess(IAudioProcessingObjectRT* pIAPORT,
        _In_ UINT32 u32NumInputConnections,
        _In_reads_(u32NumInputConnections) APO_CONNECTION_PROPERTY** ppInputConnections,
        _In_ UINT32 u32NumOutputConnections,
        _Inout_updates_(u32NumOutputConnections) APO_CONNECTION_PROPERTY** ppOutputConnections)
{
    __try
    {
        pIAPORT->APOProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return;
    }
}

HRESULT GetRegistrationProperties(IAudioProcessingObject* pIAPO, APO_REG_PROPERTIES** ppRegProps)
{
    __try
    {
        return pIAPO->GetRegistrationProperties(ppRegProps);
    }
    __except (CollectExceptionDataAndContinue(GetExceptionInformation()))
    {
        return __HRESULT_FROM_WIN32(GetExceptionCode());
    }
}

<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APODeviceTest.cpp<|end_filename|>
#include <stdafx.h>

#include <initguid.h>
#include "propkey.h"
#include <AudioClient.h>
#include <functiondiscoverykeys.h>
#include <DevPKey.h>
#include <AudioEngineBaseAPOP.h>
#include "TestMediaType.h"
#include "audioconnectionutil.h"
#include <audioenginebaseapo.h>

#include <WexTestClass.h>

#include <list>
#include <memory>

#include "APODeviceTest.h"
#include "Wrappers.h"
#include "FormatHelpers.h"


using namespace std;
using namespace wil;
using namespace WEX::Logging;
using namespace WEX::Common;

#define REGKEY_AUDIOSERVICE L"Software\\Microsoft\\Windows\\CurrentVersion\\Audio"
#define REGVALUE_SKIPRTHEAP L"SkipRTHeap"

// Forward Declarations
bool SetupSkipRTHeap();
bool CleanupSkipRTHeap();
HRESULT VerifyCustomFormatSupport(CAPODevice * deviceUnderTest);
HRESULT VerifyValidFrameCount(CAPODevice * deviceUnderTest);
HRESULT VerifyAPODataStreaming(CAPODevice * deviceUnderTest);
HRESULT VerifyActivateDeactivate(CAPODevice * deviceUnderTest);
HRESULT FillConnection
(
    UNCOMPRESSEDAUDIOFORMAT* Format,
    UINT_PTR pBuffer,
    UINT32 u32ExtraFrameCount,
    UINT32 u32MaxFrameCount,
    APO_CONNECTION_DESCRIPTOR* pConnection
);

bool CApoDeviceTests::setUpMethod()
{
    // Populate a vector of each endpoint on the system and wrap it with
    // an APO test wrapper which will save necessary APO information for 
    // test cases
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
    com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    com_ptr_nothrow<IMMDeviceCollection> spDevices;

    UINT32 cDevices;
    PROPVARIANT var;

    PropVariantInit(&var);

    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));
    VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spDevices));
    VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

    // Initialize an APO device list for each device on the system
    for(UINT32 i = 0; i < cDevices; i++)
    {
        com_ptr_nothrow<IMMDevice> spEndpoint;
        com_ptr_nothrow<IPropertyStore> spPropertyStore;

        VERIFY_SUCCEEDED(spDevices->Item(i, &spEndpoint));
        VERIFY_SUCCEEDED(spEndpoint->OpenPropertyStore(STGM_READ, &spPropertyStore));
        VERIFY_SUCCEEDED(spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var));
        VERIFY_ARE_EQUAL(VT_LPWSTR, var.vt);
        LOG_OUTPUT(L"Setting Up Endpoint: %s", var.pwszVal);

        unique_ptr<CAPODeviceList> pTestModule(new CAPODeviceList());
        pTestModule->Initialize(wistd::move(spEndpoint), var.pwszVal);
        m_testDeviceWrapperList.push_back(move(pTestModule));

        PropVariantClear(&var);
    }

    return SetupSkipRTHeap();
}

bool CApoDeviceTests::tearDownMethod()
{
    m_testDeviceWrapperList.clear();
    return CleanupSkipRTHeap();
}

void CApoDeviceTests::TestAPOInitialize()
{
    // In the event that one initialization fails fully, we do not want to fail
    // the whole test. Instead, mark the test as failed and complete it on all
    // devices.
    bool testSucceeded = true;

    m_testDeviceWrapperList.remove_if([&](auto & deviceWrapper) {

        // Print Device Friendly name
        LOG_OUTPUT(L"Endpoint Under Test: %ls", deviceWrapper->m_deviceName);

        HRESULT hr = deviceWrapper->AddSysFxDevices();

        if (hr == S_FALSE)
        {
            // No cause for failure here. Just remove the device from our test list
            // as it appears to be inactive or has no third party APOs
            return true;
        }
        
        if (FAILED(hr))
        {
            LOG_ERROR(L"Device initialization failed with error: %x", hr);
            testSucceeded = false;
        }
        return false;

    });

    VERIFY_IS_TRUE(testSucceeded);
}

void CApoDeviceTests::TestCustomFormatSupport()
{
    bool testSucceeded = true;

    TestAPOInitialize();

    for(auto const& deviceWrapper : m_testDeviceWrapperList)
    {
        // Print Device Friendly name
        LOG_OUTPUT(L"Endpoint Under Test: %ls", deviceWrapper->m_deviceName);

        // Test over each SysFX device on the device
        for (auto const& apo : deviceWrapper->m_DeviceList)
        {
            if (apo->IsProxyApo())
            {
                GUID proxyGuid;
                unique_cotaskmem_string proxyString;

                apo->GetClsID(&proxyGuid);
                StringFromCLSID(proxyGuid, &proxyString);

                LOG_OUTPUT(L"Proxy APO found. Skipping: %ls", proxyString.get());
                continue;
            }

            HRESULT hr = VerifyCustomFormatSupport(apo.get());

            if (hr == S_FALSE)
            {
                LOG_OUTPUT(L"\tAPO does not support custom formats. This is not an error");
            }

            if (FAILED(hr))
            {
                LOG_ERROR(L"\tTest Custom Format Support failed with error: (0x%08lx)", hr);
                testSucceeded = false;
            }
        }
    }

    VERIFY_IS_TRUE(testSucceeded);
}

void CApoDeviceTests::TestValidFrameCount()
{
    bool testSucceeded = true;

    TestAPOInitialize();

    for(auto const& deviceWrapper : m_testDeviceWrapperList)
    {
        // Print Device Friendly name
        LOG_OUTPUT(L"Endpoint Under Test: %ls", deviceWrapper->m_deviceName);

        // Test over each SysFX device on the device
        for (auto const& apo : deviceWrapper->m_DeviceList)
        {
            if (apo->IsProxyApo())
            {
                GUID proxyGuid;
                unique_cotaskmem_string proxyString;

                apo->GetClsID(&proxyGuid);
                StringFromCLSID(proxyGuid, &proxyString);

                LOG_OUTPUT(L"\tProxy APO found. Skipping: %ls", proxyString.get());
                continue;
            }

            HRESULT hr = VerifyValidFrameCount(apo.get());

            if (FAILED(hr))
            {
                LOG_ERROR(L"\tTest Valid Frame Count failed with error: (0x%08lx)", hr);
                testSucceeded = false;
            }
        }
    }

    VERIFY_IS_TRUE(testSucceeded);
}

void CApoDeviceTests::TestAPODataStreaming()
{
    bool testSucceeded = true;

    TestAPOInitialize();

    for(auto const& deviceWrapper : m_testDeviceWrapperList)
    {
        // Print Device Friendly name
        LOG_OUTPUT(L"Endpoint Under Test: %ls", deviceWrapper->m_deviceName);

        // Test over each SysFX device on the device
        for (auto const& apo : deviceWrapper->m_DeviceList)
        {
            if (apo->IsProxyApo())
            {
                GUID proxyGuid;
                unique_cotaskmem_string proxyString;

                apo->GetClsID(&proxyGuid);
                StringFromCLSID(proxyGuid, &proxyString);

                LOG_OUTPUT(L"\tProxy APO found. Skipping: %ls", proxyString.get());
                continue;
            }

            HRESULT hr = VerifyAPODataStreaming(apo.get());

            if (FAILED(hr))
            {
                LOG_ERROR(L"\tTest APO Data Streaming failed with error: (0x%08lx)", hr);
                testSucceeded = false;
            }
        }
    }

    VERIFY_IS_TRUE(testSucceeded);
}

void CApoDeviceTests::TestActivateDeactivate()
{
    bool testSucceeded = true;

    TestAPOInitialize();

    for(auto const& deviceWrapper : m_testDeviceWrapperList)
    {
        // Print Device Friendly name
        LOG_OUTPUT(L"Endpoint Under Test: %ls", deviceWrapper->m_deviceName);

        // Test over each SysFX device on the device
        for (auto const& apo : deviceWrapper->m_DeviceList)
        {
            if (apo->IsProxyApo())
            {
                GUID proxyGuid;
                unique_cotaskmem_string proxyString;

                apo->GetClsID(&proxyGuid);
                StringFromCLSID(proxyGuid, &proxyString);

                LOG_OUTPUT(L"\tProxy APO found. Skipping: %ls", proxyString.get());
                continue;
            }

            HRESULT hr = VerifyActivateDeactivate(apo.get());

            if (FAILED(hr))
            {
                LOG_ERROR(L"\tTest Activate and Deactivate failed with error: (0x%08lx)", hr);
                testSucceeded = false;
            }
        }
    }

    VERIFY_IS_TRUE(testSucceeded);
}

HRESULT VerifyValidFrameCount(CAPODevice*  deviceUnderTest)
{
    wil::com_ptr<IAudioProcessingObject>                 pIAPO;
    wil::com_ptr<IAudioProcessingObjectRT>               pIAPORT;
    wil::com_ptr<IAudioProcessingObjectConfiguration>    pIAPOConfig;

    if (deviceUnderTest == nullptr)
    {
        LOG_ERROR(L"\tCould not instantiate SysFx object.\n"); 
        return E_NOINTERFACE;
    }

    LOG_RETURN_IF_FAILED(deviceUnderTest->GetAPOInterfaces(&pIAPO, &pIAPORT, &pIAPOConfig), L"\tCould not instantiate IAudioProcessingObject interface.");

    APO_CONNECTION_PROPERTY inputProperty, outputProperty;
    APO_CONNECTION_DESCRIPTOR inputDescriptor, outputDescriptor;
    APO_CONNECTION_PROPERTY *pInputConnProp = &inputProperty, *pOutputConnProp = &outputProperty;
    APO_CONNECTION_DESCRIPTOR *pInputConnDesc = &inputDescriptor, *pOutputConnDesc = &outputDescriptor;
    UNCOMPRESSEDAUDIOFORMAT defaultFormat {};

    FillFormat(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 2, sizeof(float), sizeof(float) * 8, static_cast<float>(cFrameRate), KSAUDIO_SPEAKER_STEREO, &defaultFormat);    

    LOG_RETURN_IF_FAILED(SetupConnection(pInputConnDesc, pOutputConnDesc, pInputConnProp, pOutputConnProp, &defaultFormat),
                        L"\tSetupConnection failed");

    LOG_RETURN_IF_FAILED(LockForProcess(pIAPOConfig.get(), 1, &pInputConnDesc, 1, &pOutputConnDesc),
                        L"\tLockForProcess failed");

    // setup vectors
    FLOAT32     *pf32Input, *pf32Output;
    pf32Input = (FLOAT32*)pInputConnProp->pBuffer;
    pf32Output = (FLOAT32*)pOutputConnProp->pBuffer;
    for (UINT32 index = 0; index < cFrameCountToProcess; index++)
    {
        pf32Input[index] = 0.5f;
        pf32Output[index] = 0.0f;
    }

    APOProcess(pIAPORT.get(), 1, &pInputConnProp, 1, &pOutputConnProp);

    LOG_RETURN_HR_IF(pOutputConnProp->u32ValidFrameCount != cFrameCountToProcess,
        E_FAIL,
        L"\tFAIL: APOProcess output(%d) and input(%d) buffer valid frame counts does not match.\n", pOutputConnProp->u32ValidFrameCount, cFrameCountToProcess);

    LOG_RETURN_IF_FAILED(UnlockForProcess(pIAPOConfig.get()),
                        L"\tUnlock for process failed");

    LOG_RETURN_IF_FAILED(DestroyConnection(pInputConnDesc), L"\tFailed to DestroyConnection");
    LOG_RETURN_IF_FAILED(DestroyConnection(pOutputConnDesc), L"\tFailed to DestroyConnection");

    return S_OK;
}

HRESULT VerifyCustomFormatSupport(CAPODevice * deviceUnderTest)
{
    com_ptr_nothrow<IAudioProcessingObject>                 pIAPO = nullptr;
    com_ptr_nothrow<IAudioSystemEffectsCustomFormats>       pICustomFormats = nullptr;
    com_ptr_nothrow<IEndpointUtility>                       pIEPUtility = nullptr;
    IMMDevice *                                             pIMMDevice = nullptr;
    UINT                                                    iFormat;

    if (deviceUnderTest == nullptr)
    {
        LOG_ERROR(L"\tCould not instantiate SysFx object.");
        return E_NOINTERFACE;
    }
    
    if (nullptr == (pIMMDevice = deviceUnderTest->GetEndpoint()))
    {
        LOG_ERROR(L"\tUnable to get endpoint for device.");
        return E_NOINTERFACE;
    }
    
    LOG_RETURN_IF_FAILED(pIMMDevice->Activate(__uuidof(IEndpointUtility), CLSCTX_INPROC_SERVER, NULL, (void**)&pIEPUtility),
                        L"\tUnable to activate utility interface");
    
    LOG_RETURN_IF_FAILED(deviceUnderTest->GetAPOInterfaces(&pIAPO, nullptr, nullptr), L"\tCould not instantiate IAudioProcessingObject interface.");
    
    if (pIAPO == nullptr)
    {
        LOG_ERROR(L"\tFAIL: Could not instantiate IAudioProcessingObject interface.");
        return E_NOINTERFACE;
    }

    HRESULT hrQi = QIInternal(pIAPO.get(), __uuidof(IAudioSystemEffectsCustomFormats), (void**)&pICustomFormats);

    if (E_NOINTERFACE == hrQi)
    {
        //  This GFX does not support custom formats...
        LOG_OUTPUT(L"\tSysFx does not support custom formats (QueryInterface for "
                    "IAudioSystemEffectsCustomFormats returns E_NOINTERFACE).");
        return S_FALSE;
    }

    LOG_RETURN_IF_FAILED(hrQi, L"\tQueryInterface for IAudioSystemEffectsCustomFormats returned error.");


    //  Custom formats are available and supported
    LOG_OUTPUT(L"\tIAudioSystemEffectsCustomFormats available on SysFx.");

    LOG_RETURN_IF_FAILED(pICustomFormats->GetFormatCount(&iFormat),
                        L"\tIAudioSystemEffectsCustomFormats::GetFormatCount failed");
    
    // No matter what we want to test all formats. However if one fails, make sure we fail
    // the test as well
    bool allFormatsSucceeded = true;

    for (; iFormat; --iFormat)
    {
        com_ptr_nothrow<IAudioMediaType>        pIFormat = nullptr;
        com_ptr_nothrow<IAudioMediaType>        pIFormatSuggested = nullptr;
        LPWSTR                                  strFormatName = NULL;        
        com_ptr_nothrow<IPartsList>             pIParts = nullptr;
        KSDATAFORMAT_WAVEFORMATEX *             pKsFormat = NULL;
        HRESULT                                 hrFormatSupport = S_OK;
        
        if (FAILED(hrFormatSupport = pICustomFormats->GetFormat(iFormat - 1, &pIFormat)))
        {
            LOG_ERROR(L"\tFAIL: IAudioSystemEffectsCustomFormats::GetFormat returned "
                    "error (0x%08lx).",
                hrFormatSupport);
            allFormatsSucceeded = false;

            continue;
        }
        
        if (FAILED(hrFormatSupport = pICustomFormats->GetFormatRepresentation(iFormat - 1, &strFormatName)))
        {
            LOG_ERROR(L"\tFAIL: IAudioSystemEffectsCustomFormats::"
                    "GetFormatRepresentation returned error (0x%08lx)",
                hrFormatSupport);
            allFormatsSucceeded = false;

            continue;
        }
        
        LOG_OUTPUT(L"\tVerifying format support for: [%ls]...", strFormatName);
        
        //  Custom format, must be supported on the output side of SysFx...
        hrFormatSupport = IsOutputFormatSupported(pIAPO.get(), NULL, pIFormat.get(), &pIFormatSuggested);
        const WAVEFORMATEX* original = pIFormat->GetAudioFormat();
        const WAVEFORMATEX* suggested = pIFormatSuggested->GetAudioFormat();

        LOG_OUTPUT(L"\tWaveFormatEx pIFormat - tag: %u, channels: %u, samplespersec: %u, avgbytespersec: %u, blockalign: %u, bitspersample: %u, cbsize: %u", 
                    original->wFormatTag, original->nChannels, original->nSamplesPerSec, original->nAvgBytesPerSec, original->nBlockAlign, 
                    original->wBitsPerSample, original->cbSize);

        LOG_OUTPUT(L"\tWaveFormatEx pIFormatSuggested - tag: %u, channels: %u, samplespersec: %u, avgbytespersec: %u, blockalign: %u, bitspersample: %u, cbsize: %u", 
                    suggested->wFormatTag, suggested->nChannels, suggested->nSamplesPerSec, suggested->nAvgBytesPerSec, suggested->nBlockAlign, 
                    suggested->wBitsPerSample, suggested->cbSize);
        
        if (S_FALSE == hrFormatSupport)
        {
            LOG_ERROR(L"\tFAIL: IsOutputFormatSupported returns S_FALSE with a suggested format");
            allFormatsSucceeded = false;            
        }
        else if (S_OK != hrFormatSupport)
        {
            LOG_ERROR(L"\tFAIL: IsOutputFormatSupported returns error (0x%08lx)",
                hrFormatSupport);
            allFormatsSucceeded = false;
        }
        else
        {
            if (pIFormatSuggested == nullptr)
            {
                LOG_ERROR(L"\tFAIL: IsOutputFormatSupported returns S_OK, but does not "
                        "suggest format.");
                allFormatsSucceeded = false;
            }
            else
            {
                if (!IsEqualFormat(pIFormat.get(), pIFormatSuggested.get()))
                {
                    LOG_ERROR(L"\tFAIL: IsOutputFormatSupported returns S_OK, but "
                            "returns a different format.");
                    allFormatsSucceeded = false;
                }
                else
                {
                    LOG_OUTPUT(L"\tIsOutputFormatSupported supports custom format.");
                }
            }
        }
        
        pKsFormat = CreateKSDataFromWFX((WAVEFORMATEX*)pIFormat->GetAudioFormat());
        
        if (pKsFormat == NULL)
        {
            LOG_ERROR(L"\tFAIL: Unable to create KSDATAFORMAT from PWAVEFORMATEX.");
            allFormatsSucceeded = false;

            continue;
        }
        
        {
            unique_cotaskmem_ptr<KSDATAFORMAT_WAVEFORMATEX> formatBuffer(pKsFormat);
            
            //  Custom format, must be supported by endpoint...
            hrFormatSupport = pIEPUtility->FindHostConnector(
                    (KSDATAFORMAT*)formatBuffer.get(),
                    formatBuffer->DataFormat.FormatSize,
                    FALSE,
                    &pIParts);
            
            if (S_OK != hrFormatSupport)
            {
                LOG_ERROR(L"\tFAIL: Endpoint does not support custom format with error: (0x%08lx)", hrFormatSupport);
                allFormatsSucceeded = false;
            }
            else
            {
                LOG_OUTPUT(L"\tEndpoint supports custom format.");
            }
        }
    }
    
    return allFormatsSucceeded;
}

HRESULT VerifyAPODataStreaming(CAPODevice* deviceUnderTest)
{
    wil::com_ptr<IAudioProcessingObject>                 pIAPO;
    wil::com_ptr<IAudioProcessingObjectRT>               pIAPORT;
    wil::com_ptr<IAudioProcessingObjectConfiguration>    pIAPOConfig;
    APO_REG_PROPERTIES *pRegProperties = NULL;    

    if (deviceUnderTest == nullptr)
    {
        LOG_ERROR(L"\tCould not instantiate SysFx object.\n"); 
        return E_NOINTERFACE;
    }

    LOG_RETURN_IF_FAILED(deviceUnderTest->GetAPOInterfaces(&pIAPO, &pIAPORT, &pIAPOConfig), L"\tCould not instantiate IAudioProcessingObject interface.");

    APO_CONNECTION_PROPERTY inputProperty, outputProperty;
    APO_CONNECTION_DESCRIPTOR inputDescriptor, outputDescriptor;
    APO_CONNECTION_PROPERTY *pInputConnProp = &inputProperty, *pOutputConnProp = &outputProperty;
    APO_CONNECTION_DESCRIPTOR *pInputConnDesc = &inputDescriptor, *pOutputConnDesc = &outputDescriptor;
    UNCOMPRESSEDAUDIOFORMAT defaultFormat {};

    FillFormat(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 2, sizeof(float), sizeof(float) * 8, static_cast<float>(cFrameRate), KSAUDIO_SPEAKER_STEREO, &defaultFormat);    

    LOG_RETURN_IF_FAILED(SetupConnection(pInputConnDesc, pOutputConnDesc, pInputConnProp, pOutputConnProp, &defaultFormat),
                        L"\tSetupConnection failed");

    LOG_RETURN_IF_FAILED(LockForProcess(pIAPOConfig.get(), 1, &pInputConnDesc, 1, &pOutputConnDesc),
                        L"\tLockForProcess failed");

    // setup vectors
    FLOAT32     *pf32Input, *pf32Output;
    pf32Input = (FLOAT32*)pInputConnProp->pBuffer;
    pf32Output = (FLOAT32*)pOutputConnProp->pBuffer;
    for (UINT32 index = 0; index < cFrameCountToProcess; index++)
    {
        pf32Input[index] = 0.5f;
        pf32Output[index] = INFINITY;
    }

    APOProcess(pIAPORT.get(), 1, &pInputConnProp, 1, &pOutputConnProp);

    GetRegistrationProperties(pIAPO.get(), &pRegProperties);

    // Tests to make sure that in place APO have the correct data.
    for (UINT32 index = 0; index < cFrameCountToProcess; index++)
    {
        // This will also check NaN along with INF
        LOG_RETURN_HR_IF(!isfinite(pf32Output[index]), E_FAIL,
            L"\tFAIL: APOProcess output(%f) is not a number or infinite.\n", pf32Output[index]);
    }

    LOG_RETURN_IF_FAILED(UnlockForProcess(pIAPOConfig.get()),
                        L"\tUnlock for process failed");

    if (APO_FLAG_INPLACE == (pRegProperties->Flags & APO_FLAG_INPLACE))
    {
        LOG_RETURN_IF_FAILED(LockForProcess(pIAPOConfig.get(), 1, &pInputConnDesc, 1, &pInputConnDesc),
                             L"\tLockForProcess failed");

        // setup vector
        pf32Input = (FLOAT32 *)pInputConnProp->pBuffer;
        for (UINT32 index = 0; index < cFrameCountToProcess; index++)
        {
            pf32Input[index] = 0.5f;
        }

        APOProcess(pIAPORT.get(), 1, &pInputConnProp, 1, &pInputConnProp);

        LOG_RETURN_IF_FAILED(UnlockForProcess(pIAPOConfig.get()),
                        L"\tUnlock for process failed");
    }

    LOG_RETURN_IF_FAILED(DestroyConnection(pInputConnDesc), L"\tFailed to DestroyConnection");
    LOG_RETURN_IF_FAILED(DestroyConnection(pOutputConnDesc), L"\tFailed to DestroyConnection");

    return S_OK;
}

HRESULT VerifyActivateDeactivate(CAPODevice* deviceUnderTest)
{
    GUID clsid;
    WCHAR ComRegistrationPath[1024];
    WCHAR file[1024];
    PWSTR end;
    size_t cchRemaining;
    DWORD dwSize = sizeof(WCHAR) * 1024;

    if (deviceUnderTest == nullptr)
    {
        LOG_ERROR(L"\tCould not instantiate SysFx object.\n"); 
        return E_NOINTERFACE;
    }

    deviceUnderTest->GetClsID(&clsid);

    LOG_RETURN_IF_FAILED(StringCchPrintfEx(ComRegistrationPath, ARRAYSIZE(ComRegistrationPath), &end, &cchRemaining, 0, L"CLSID\\{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\\InprocServer32",
        clsid.Data1, clsid.Data2, clsid.Data3, clsid.Data4[0], clsid.Data4[1], clsid.Data4[2], clsid.Data4[3], clsid.Data4[4], clsid.Data4[5], clsid.Data4[6], clsid.Data4[7]), 
        L"\tFailed to create string to find file name for DLL.");

    LONG error = RegGetValue(HKEY_CLASSES_ROOT, ComRegistrationPath, nullptr, RRF_RT_REG_SZ, nullptr, file, &dwSize);

    if (error != ERROR_SUCCESS)
    {
        LOG_RETURN_IF_FAILED(HRESULT_FROM_WIN32(error), L"\tFAILED to get the registry value.");
    }

    auto loadAPO = LoadLibrary(file);

    if (loadAPO == NULL)
    {
        DWORD err = GetLastError();
        HRESULT hr = HRESULT_FROM_WIN32(err);
        if (SUCCEEDED(hr))
        {
            hr = E_FAIL;
        }
        LOG_RETURN_IF_FAILED(hr, L"\tFAILED to load the APO.");
    }

    BOOL bFreed = FreeLibrary(loadAPO);

    if (!bFreed)
    {
        DWORD err = GetLastError();
        HRESULT hr = HRESULT_FROM_WIN32(err);
        if (SUCCEEDED(hr))
        {
            hr = E_FAIL;
        }
        LOG_RETURN_IF_FAILED(hr, L"\tFAILED to free the loaded APO.");
    }

    return S_OK;
}

struct APO_PROPERTIES { 
    PROPERTYKEY key; 
    LPARAM apoType;
    LPCWSTR apoTypeName;
    DWORD dwMajorVersion; 
    DWORD dwMinorVersion;
    } PKEY_FX_EFFECTS[] = {
        {PKEY_FX_PostMixEffectClsid,  DT_GFX, L"GFX " ,6, 0},
        {PKEY_FX_PreMixEffectClsid,   DT_LFX, L"LFX ", 6, 0},
        {PKEY_FX_StreamEffectClsid,   DT_SFX, L"SFX ", 6, 3},
        {PKEY_FX_ModeEffectClsid,     DT_MFX, L"MFX ", 6, 3},
        {PKEY_FX_EndpointEffectClsid, DT_EFX, L"EFX ", 6, 3},
        // Composite effects
        {PKEY_CompositeFX_StreamEffectClsid,    DT_SFX, L"Composite SFX", 10, 0},
        {PKEY_CompositeFX_ModeEffectClsid,      DT_MFX, L"Composite MFX", 10, 0},
        {PKEY_CompositeFX_EndpointEffectClsid,  DT_EFX, L"Composite EFX", 10, 0},
        // Offload effects
        {PKEY_FX_Offload_StreamEffectClsid,    DT_SFX, L"Offload SFX", 10, 0},
        {PKEY_FX_Offload_ModeEffectClsid,      DT_MFX, L"Offload MFX", 10, 0},
        {PKEY_CompositeFX_Offload_StreamEffectClsid,    DT_SFX, L"Composite Offload SFX", 10, 0},
        {PKEY_CompositeFX_Offload_ModeEffectClsid,      DT_MFX, L"Composite Offload MFX", 10, 0},
        // Keyword Detector effects
        {PKEY_FX_KeywordDetector_StreamEffectClsid,    DT_SFX, L"KeywordDetector SFX", 10, 0},
        {PKEY_FX_KeywordDetector_ModeEffectClsid,      DT_MFX, L"KeywordDetector MFX", 10, 0},
        {PKEY_FX_KeywordDetector_EndpointEffectClsid,  DT_EFX, L"KeywordDetector EFX", 10, 0},
        {PKEY_CompositeFX_KeywordDetector_StreamEffectClsid,    DT_SFX, L"Composite KeywordDetector SFX", 10, 0},
        {PKEY_CompositeFX_KeywordDetector_ModeEffectClsid,      DT_MFX, L"Composite KeywordDetector MFX", 10, 0},
        {PKEY_CompositeFX_KeywordDetector_EndpointEffectClsid,  DT_EFX, L"Composite KeywordDetector EFX", 10, 0},
    };

CAPODevice::CAPODevice
(
    IMMDevice          *pIEndpoint,
    LPCWSTR             pszClassId,
    LPCWSTR             pszEndpoint,
    IPropertyStore     *pIStoreDevice,
    IPropertyStore     *pIStoreFx,
    LPARAM              apoType,
    LPCWSTR             apoTypeName,
    LPCWSTR             pszAttachedDevice,
    BOOL                bProxyAPO
)
: m_pRegProps(NULL), m_fValid(FALSE)
{
    {
        //  Getting PnpId from property store...
        PROPVARIANT                     pv;
        com_ptr_nothrow<IMMDeviceEnumerator>    pIDeviceEnumerator;
        com_ptr_nothrow<IMMDevice>              pIMMDevNode;
        com_ptr_nothrow<IPropertyStore>         pIDNPropStore;
    
        LOG_RETURN_VOID_IF_FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pIDeviceEnumerator)), 
                                  L"\tCoCreateInstance for MMDeviceEnumerator returned error");
           
        PropVariantInit(&pv);
        auto clearPropVariantOnScopeExit = scope_exit([&]
        {
            PropVariantClear(&pv);
        });
    
        LOG_RETURN_VOID_IF_FAILED(pIStoreDevice->GetValue(PKEY_Endpoint_Devnode, &pv), L"\tFailed to get endpoint device");
    
        if (VT_LPWSTR != pv.vt)
        {
            LOG_ERROR(L"\tEnpoint Devnode property key is of the incorrect type");
            return;
        }
        
        LOG_RETURN_VOID_IF_FAILED(pIDeviceEnumerator->GetDevice(pv.pwszVal, &pIMMDevNode), L"\tGetting device (%s) from the enumerator failed", pv.pwszVal);
        
        PropVariantClear(&pv);
        
        LOG_RETURN_VOID_IF_FAILED(pIMMDevNode->OpenPropertyStore(STGM_READ, &pIDNPropStore), L"\tFailed to open device property store");
        
        PropVariantInit(&pv);
        
        LOG_RETURN_VOID_IF_FAILED(pIDNPropStore->GetValue((PROPERTYKEY&)DEVPKEY_Device_InstanceId, &pv), L"\tFailed to get device instance id from device prop store");
        
        if (VT_LPWSTR != pv.vt)
        {
            LOG_ERROR(L"\tDevice instance ID is of the incorrect type");
            return;
        }
        
        m_szPnPId = wil::make_cotaskmem_string_nothrow(pv.pwszVal, sizeof(m_szPnPId));
    }
    
    LOG_RETURN_VOID_IF_FAILED(CLSIDFromString((LPOLESTR)pszClassId, &m_gClsID), L"\tCLSIDFromString failed");
    
    m_szAttachedDevice = wil::make_cotaskmem_string_nothrow(pszAttachedDevice, wcslen(pszAttachedDevice));
    m_szEndpoint = wil::make_cotaskmem_string_nothrow(pszEndpoint, wcslen(pszEndpoint));

    PROPVARIANT value;
    PropVariantInit(&value);
    if (SUCCEEDED(pIStoreFx->GetValue(PKEY_ItemNameDisplay, &value)))
    {
        if (value.vt == VT_LPWSTR)
        {
            m_sApoName = wil::make_cotaskmem_string_nothrow(value.pwszVal, wcslen(value.pwszVal));
        }
        PropVariantClear(&value);
    }
    
        
    LOG_RETURN_VOID_IF_FAILED(CoCreateInstance(m_gClsID, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_pIUnknown)), L"\tCoCreateInstance for IUnknown returned error");


    LOG_RETURN_VOID_IF_FAILED(m_pIUnknown->QueryInterface(__uuidof(IAudioProcessingObject), (void**)&m_pIAPO), 
                                L"\tCoCreateInstance for IAudioProcessingObject returned error");


    //  Getting DeviceCollection
    {
        PROPVARIANT                 pvFormat;
        
        // the standard KSDATAFORMAT_WAVEFORMATEX only contains a 
        // WAVEFORMATEX, but we need the extensible format so
        // we define this type here:
        typedef struct {
            KSDATAFORMAT            DataFormat;
            WAVEFORMATEXTENSIBLE    WFXtensible;
        } KS_WFExtensible;
        
        KS_WFExtensible             ksWfx;
        
        // initialize the structure
        PropVariantInit(&pvFormat);

        // Clear the prop variant on scope exit
        auto clearFormatOnExit = scope_exit([&] {
            PropVariantClear(&pvFormat);
        });
        
        LOG_RETURN_VOID_IF_FAILED(pIStoreDevice->GetValue(PKEY_AudioEngine_DeviceFormat, &pvFormat), L"\tGet PKEY_AudioEngine_Device format returned error");
        
        if (pvFormat.vt == VT_BLOB)
        {
            if (pvFormat.blob.cbSize != sizeof(WAVEFORMATEXTENSIBLE))
            {
                LOG_ERROR(L"\tpvFormat.blob.cbSize (%zu) != sizeof(WAVEFORMATEXTENSIBLE) (%zu)\n", pvFormat.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE));
                return;
            }
        }
        else
        {
            LOG_ERROR(L"\tFormat is not VT_BLOB, Q != %c\n", VARTYPE(pvFormat.vt));
            return;
        }


        LPWAVEFORMATEX              pbWfxDevice = NULL;
        ULONG                       cbWfxDevice = 0;
        
        pbWfxDevice = (WAVEFORMATEX*)pvFormat.blob.pBlobData;
        cbWfxDevice = pvFormat.blob.cbSize;

        ksWfx.DataFormat.FormatSize  = sizeof(KSDATAFORMAT) + cbWfxDevice;
        ksWfx.DataFormat.Flags       = 0;
        ksWfx.DataFormat.SampleSize  = 0;
        ksWfx.DataFormat.Reserved    = 0;
        ksWfx.DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
        ksWfx.DataFormat.SubFormat   = KSDATAFORMAT_SUBTYPE_PCM;
        ksWfx.DataFormat.Specifier   = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
        CopyMemory( &(ksWfx.WFXtensible), pbWfxDevice, cbWfxDevice );
        
        com_ptr_nothrow<IEndpointUtility2>  pIEPUtil;
        LOG_RETURN_VOID_IF_FAILED(pIEndpoint->Activate(__uuidof(IEndpointUtility2), CLSCTX_ALL, NULL, (void**)&pIEPUtil), L"\tpIMMDevice->Activate returned error");
        
        LOG_RETURN_VOID_IF_FAILED(pIEPUtil->GetHostConnectorDeviceCollection2((PKSDATAFORMAT)&ksWfx, sizeof(ksWfx), TRUE, 
                                  eHostProcessConnector, &m_pIDevCollection, &m_nSoftwareIoDeviceInCollection, &m_nSoftwareIoConnectorIndex),
                                  L"\tGetting device collection returned error");
    }
    
    //  Saved off for InitializeAPO()
    m_pIEndpoint = pIEndpoint;
    m_pIEPStore  = pIStoreDevice;
    m_pIFXStore  = pIStoreFx;
    m_lpType     = apoType;
    m_sApoTypeName =  wil::make_cotaskmem_string_nothrow(apoTypeName, wcslen(apoTypeName));;
    m_bProxyAPO  = bProxyAPO;
    
    //  Initialize SysFx!!
    LOG_RETURN_VOID_IF_FAILED(InitializeAPO(m_pIAPO.get()), L"\tpIAPO->Initialize returned error");

    m_fValid = TRUE;
}

CAPODevice::~CAPODevice(void)
{
    if (NULL != m_pRegProps)
    {
        CoTaskMemFree(m_pRegProps);
        m_pRegProps = NULL;
    }
}


HRESULT CAPODevice::InitializeAPO
(
    IAudioProcessingObject     *pIAPO
)
{
    LOG_RETURN_HR_IF(pIAPO == nullptr, E_POINTER, L"IAudioProcessingObject is null");

    if(DT_SMEFX & GetDeviceType())
    {
        APOInitSystemEffects2                        sysfxInitParams2 = {0};
        // Setup initialization struct
        sysfxInitParams2.APOInit.cbSize                = sizeof(APOInitSystemEffects2);
        sysfxInitParams2.APOInit.clsid                 = m_gClsID;
        sysfxInitParams2.pReserved                     = NULL;
        sysfxInitParams2.pDeviceCollection             = m_pIDevCollection.get();
        sysfxInitParams2.pAPOEndpointProperties        = m_pIEPStore.get();
        sysfxInitParams2.pAPOSystemEffectsProperties   = m_pIFXStore.get();
        sysfxInitParams2.InitializeForDiscoveryOnly    = TRUE;
        sysfxInitParams2.AudioProcessingMode           = AUDIO_SIGNALPROCESSINGMODE_DEFAULT;
        sysfxInitParams2.nSoftwareIoDeviceInCollection = m_nSoftwareIoDeviceInCollection;
        sysfxInitParams2.nSoftwareIoConnectorIndex     = m_nSoftwareIoConnectorIndex;
        
        return pIAPO->Initialize(sysfxInitParams2.APOInit.cbSize, (BYTE*)(&sysfxInitParams2));
    }
    else if (DT_LGFX & GetDeviceType())
    {
        APOInitSystemEffects                        sysfxInitParams = {0};
        // Setup initialization struct
        sysfxInitParams.APOInit.cbSize              = sizeof(sysfxInitParams);
        sysfxInitParams.APOInit.clsid               = m_gClsID;
        sysfxInitParams.pAPOEndpointProperties      = m_pIEPStore.get();
        sysfxInitParams.pAPOSystemEffectsProperties = m_pIFXStore.get();
        sysfxInitParams.pDeviceCollection           = m_pIDevCollection.get();
        sysfxInitParams.pReserved                   = NULL;
        
        return (pIAPO->Initialize(sizeof(sysfxInitParams), (BYTE*)&sysfxInitParams));
    }

    //  We should never get here unless we are initializing a first party APO (which we shouldn't be)
    LOG_ERROR(L"SFX is of unknown type. Type is: %d", GetDeviceType());
    return E_FAIL;
}

HRESULT CAPODevice::GetAPOInterfaces
(
    IAudioProcessingObject                **ppIAPO,
    IAudioProcessingObjectRT              **ppIAPORT,
    IAudioProcessingObjectConfiguration   **ppIAPOConfig
)
{
    if (ppIAPO != nullptr)
    {
        LOG_RETURN_IF_FAILED(UnknownQIInternal(m_pIUnknown.get(), __uuidof(IAudioProcessingObject), (void**)ppIAPO),
                            L"\tCoCreateInstance for IAudioProcessingObject failed");
    }

    if (ppIAPORT != nullptr)
    {
        LOG_RETURN_IF_FAILED(UnknownQIInternal(m_pIUnknown.get(), __uuidof(IAudioProcessingObjectRT), (void**)ppIAPORT),
                            L"\tCoCreateInstance for IAudioProcessingObjectRT failed");
    }
    
    if (ppIAPOConfig != nullptr)
    {
        LOG_RETURN_IF_FAILED(UnknownQIInternal(m_pIUnknown.get(), __uuidof(IAudioProcessingObjectConfiguration), (void**)ppIAPOConfig),
                            L"\tCoCreateInstance for IAudioProcessingObjectConfiguration failed");
    }
    
    return S_OK;
}

//--------------------------------------------------------------------------
//  CAPODeviceList constructor
CAPODeviceList::CAPODeviceList ()
{
}

//--------------------------------------------------------------------------
//  ~CAPODeviceList destructor

CAPODeviceList::~CAPODeviceList(VOID)
{
    m_DeviceList.clear();

    RegDeleteKeyValue(HKEY_LOCAL_MACHINE,
    REGKEY_AUDIO_SOFTWARE,
    PREVENT_APOTEST_CRASH_OR_REPORT_ON_APO_EXCEPTION);
}

HRESULT CAPODeviceList::AddSysFxDevices()
{
    HRESULT                                 hr = S_OK;
    bool                                    allApoDevicesValid = true; 
    com_ptr_nothrow<IMMEndpoint>            pIMMEndpoint;
    com_ptr_nothrow<IMMEndpointInternal>    pIMMEndpointInt;
    com_ptr_nothrow<IPropertyStore>         pIPropertyStoreDevice;
    com_ptr_nothrow<IPropertyStore>         pIPropertyStoreFX;
    com_ptr_nothrow<IDeviceTopology>        pIDeviceTopology;
    com_ptr_nothrow<IConnector>             pIConnector;
    LPCWSTR                                 strConnectedDevice = NULL;
    PROPVARIANT                             pvName;
    PROPVARIANT                             pv;
    PROPVARIANT                             pvProxy;
    BOOL                                    bAPOProxy;

    
    if (FAILED(hr = m_spDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_INPROC_SERVER, NULL, (void**)&pIDeviceTopology)))
    {
        if (E_NOINTERFACE == hr)
        {
            LOG_OUTPUT(L"\tDevice is not active. Removing from test cases");
            //  There's the possibility that this legit if device is not active.
            return S_FALSE;
        }
            
        return hr;
    }
    
    LOG_RETURN_IF_FAILED(pIDeviceTopology->GetConnector(0, &pIConnector), 
                        L"\tFailed to get device connector");
    
    LOG_RETURN_IF_FAILED(pIConnector->GetDeviceIdConnectedTo((LPWSTR *)&strConnectedDevice),
                        L"\tFailed to get connected device ID");
    
    LOG_RETURN_IF_FAILED(m_spDevice->QueryInterface(__uuidof(IMMEndpoint), (void**)&pIMMEndpoint),
                        L"\tQueryInterfacce for IMMEndpoint failed");
    
    LOG_RETURN_IF_FAILED(pIMMEndpoint->QueryInterface(__uuidof(IMMEndpointInternal), (void**)&pIMMEndpointInt),
                        L"\tQueryInterface for IMMEndpointInternal failed");
    
    LOG_RETURN_IF_FAILED(m_spDevice->OpenPropertyStore(STGM_READ, &pIPropertyStoreDevice),
                        L"\tFailed to open property store for device");
    
    LOG_RETURN_IF_FAILED(pIMMEndpointInt->TryOpenFXPropertyStore(STGM_READ, &pIPropertyStoreFX),
                        L"\tFailed to Open SysFX property store for device");

    if (!pIPropertyStoreFX)
    {
        // This device doesn't appear to have any SysFX
        LOG_OUTPUT(L"\tThis device probably doesn't have any SysFx. Skipping and removing from test");    
        return S_FALSE;
    }
    
    PropVariantInit(&pvName);
    if (SUCCEEDED(pIPropertyStoreDevice->GetValue(PKEY_Device_FriendlyName, &pvName)))
    {
        if (VT_LPWSTR != V_VT(&pvName))
        {
            LOG_OUTPUT(L"\tFriendlyName value is not a string.");
        }
    }
    else
    {
        LOG_OUTPUT(L"\tIPropertyStore::GetValue returned error.");
    }
    

    OSVERSIONINFO ver = {0};
    ver.dwOSVersionInfoSize = sizeof(ver);

    GetVersionEx(&ver);

    for(int i = 0; i < ARRAYSIZE(PKEY_FX_EFFECTS); i++)
    {
        if (ver.dwMajorVersion < PKEY_FX_EFFECTS[i].dwMajorVersion || 
           (ver.dwMajorVersion == PKEY_FX_EFFECTS[i].dwMajorVersion &&
            ver.dwMinorVersion < PKEY_FX_EFFECTS[i].dwMinorVersion))
        {
            continue;
        }

        PropVariantInit(&pv);
        hr = pIPropertyStoreFX->GetValue(PKEY_FX_EFFECTS[i].key, &pv);
        LOG_OUTPUT(L"\tPKEY_FX_EFFECTS[%i] - GetValue PKEY_FX_EFFECTS[%i].key - 0x%x", i, i, hr);
        if (S_OK == hr)
        {
            LOG_OUTPUT(L"\tPKEY_FX_EFFECTS[%i] - V_VT(&pv) - %i", i, V_VT(&pv));
            if (VT_LPWSTR == V_VT(&pv))
            {
                bAPOProxy = FALSE;
                PropVariantInit(&pvProxy);
                
                // Check if mode effect is proxy
                if (PKEY_FX_EFFECTS[i].apoType & DT_MFX)
                {
                    hr = pIPropertyStoreFX->GetValue(PKEY_MFX_ProcessingModes_Supported_For_Streaming, &pvProxy);
                    LOG_OUTPUT(L"\tPKEY_FX_EFFECTS[%i] - GetValue MFX - 0x%x", i, hr);
                    if (S_OK == hr)
                    {
                        bAPOProxy = (pvProxy.vt != (VT_VECTOR | VT_LPWSTR)) ? TRUE : FALSE;
                    }
                }
                
                // Check if endpoint effect is proxy
                if (PKEY_FX_EFFECTS[i].apoType & DT_EFX)
                {
                    hr = pIPropertyStoreFX->GetValue(PKEY_EFX_ProcessingModes_Supported_For_Streaming, &pvProxy);
                    LOG_OUTPUT(L"\tPKEY_FX_EFFECTS[%i] - GetValue EFX - 0x%x", i, hr);
                    if (S_OK == hr)
                    {
                        bAPOProxy = (pvProxy.vt != (VT_VECTOR | VT_LPWSTR)) ? TRUE : FALSE;
                    }
                }

                LOG_OUTPUT(L"\tLooking at Audio Processing Object %ls\n", pv.pwszVal);

                unique_ptr<CAPODevice> pSysFxDevice(new CAPODevice(m_spDevice.get(), pv.pwszVal, pvName.pwszVal, pIPropertyStoreDevice.get(), 
                                                        pIPropertyStoreFX.get(), PKEY_FX_EFFECTS[i].apoType, PKEY_FX_EFFECTS[i].apoTypeName, 
                                                        strConnectedDevice, bAPOProxy));
    
                if (NULL != pSysFxDevice)
                {
                    if (pSysFxDevice->IsValid())
                    {
                        pSysFxDevice->m_fSelected = TRUE;
                        m_DeviceList.push_back(move(pSysFxDevice));
                    }
                    else
                    {
                        LOG_ERROR(L"\tInvalid sysFX device encountered");
                    }
                }

                PropVariantClear(&pvProxy);
            }
            else if ((VT_LPWSTR | VT_VECTOR) == V_VT(&pv) && pv.calpwstr.cElems > 0)
            {
                UINT32 cClsids = pv.calpwstr.cElems;
                bAPOProxy = FALSE;

                for (UINT32 j = 0; j < cClsids; j++)
                {
                    LOG_OUTPUT(L"\tLooking at Audio Processing Object %ls\n", pv.calpwstr.pElems[j]);

                    unique_ptr<CAPODevice> pSysFxDevice(new CAPODevice(m_spDevice.get(), pv.calpwstr.pElems[j], pvName.pwszVal, pIPropertyStoreDevice.get(),
                                                                       pIPropertyStoreFX.get(), PKEY_FX_EFFECTS[i].apoType, PKEY_FX_EFFECTS[i].apoTypeName,
                                                                       strConnectedDevice, bAPOProxy));

                    if (NULL != pSysFxDevice)
                    {
                        if (pSysFxDevice->IsValid())
                        {
                            pSysFxDevice->m_fSelected = TRUE;
                            m_DeviceList.push_back(move(pSysFxDevice));
                        }
                        else
                        {
                            LOG_ERROR(L"\tInvalid sysFX device encountered");
                        }
                    }
                }
            }
        }
        PropVariantClear(&pv);
    }

    return allApoDevicesValid ? S_OK : E_FAIL;
}

HRESULT CAPODeviceList::Initialize(wil::com_ptr_nothrow<IMMDevice> pDevice, LPWSTR deviceName)
{
    if (pDevice == nullptr || deviceName == nullptr)
    {
        LOG_ERROR(L"\tIMMDevice is null");
        return E_INVALIDARG;
    }

    m_spDevice = pDevice;

    wcscat(m_deviceName, deviceName);

    DWORD dwValue = 1;
    // Set registry entry so AudioDG won't crash during IAudioProcessingObjectRT function calls
    RETURN_IF_WIN32_ERROR(RegSetKeyValue(HKEY_LOCAL_MACHINE,
        REGKEY_AUDIO_SOFTWARE,
        PREVENT_APOTEST_CRASH_OR_REPORT_ON_APO_EXCEPTION,
        REG_DWORD,
        &dwValue,
        sizeof(dwValue)));

    return S_OK;
}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APOStressTest.cpp<|end_filename|>
#include <stdafx.h>

#include <initguid.h>
#include "propkey.h"
#include <AudioClient.h>
#include <SpatialAudioClient.h>
#include <functiondiscoverykeys.h>
#include <DevPKey.h>
#include <AudioEngineBaseAPOP.h>
#include "TestMediaType.h"
#include <audioenginebaseapo.h>
#include <MMDeviceAPI.h>
#include <MMDeviceAPIP.h>

#include <WexTestClass.h>

#include <list>
#include <memory>
#include <ctime>
#include <thread>
#include <future>
#include <mutex>
#include <functional>
#include <atlbase.h>

#include "APOStressTest.h"
#include "sinewave.h"
#include "util.h"

#include <psapi.h>
#include <processthreadsapi.h>

using namespace std;
using namespace wil;
using namespace WEX::Logging;
using namespace WEX::Common;

#define NUMBER_OF_TESTS 8
#define THIRTY_MIN_IN_SEC 1800
#define SIXTY_SECONDS 60
#define ONE_SEC_IN_MS 1000
#define THIRTY_MIN_IN_MS 1800000
#define MAX_ALLOWED_PAGEFILE_USAGE_IN_BYTES 300000000

#define IF_FAILED_RETURN(hr) { HRESULT hrCode = hr; if(FAILED(hrCode)) { return hrCode; } }

#define REGKEY_AUDIOSERVICE L"Software\\Microsoft\\Windows\\CurrentVersion\\Audio"
#define REGVALUE_SKIPRTHEAP L"SkipRTHeap"

const DWORD WAIT_TIME_MIN_FOR_THREAD_OPERATION = 3u * 1000u;        //  3 seconds
const DWORD WAIT_TIME_MAX_FOR_THREAD_OPERATION = 10u * 1000u;       // 10 seconds

size_t maxAudioDGPagefileUsage = 0;
std::mutex maxAudioDGPagefileUsage_mutex; // protects maxAudioDGPagefileUsage_mutex 

template <typename T>
inline T RandInRange(T min, T max)
{
    // Treat the value span as a double right away.  Otherwise if min = 0, and max
    // is truly the max for the type, the value span could loop to zero.
    double valueSpan = 1.0f + (double) (max - min);

    // The following had a problem where doubles larger than 0x8000000000000000 were always truncating
    // to 0x8000000000000000 when cast to a DWORD64.  Multiplying by 0.5f and then (T) 2 seemed to
    // get around this problem.
    //return min + (T) ((double) (max - min + 1) * (double) rand() / (double) (RAND_MAX + 1));

    // Then the following had a problem where RandInRange(0, 1) could never return 1.
    //return min + (T) 2 * (T) (0.5f * valueSpan * ((double) rand() / (double) (RAND_MAX + 1)));

    double offset = valueSpan * ((double) rand() / (double) (RAND_MAX + 1));

    if (offset > (double) ((DWORD64) 0x8000000000000000))
    {
        return min + (T) 2 * (T) (0.5f * offset);
    }

    return min + (T) offset;
}

void WriteToAudioObjectBuffer(FLOAT* buffer, UINT frameCount, FLOAT frequency, UINT samplingRate)
{
    const double PI = 4 * atan2(1.0, 1.0);
    static double _radPhase = 0.0;
    double step = 2 * PI * frequency / samplingRate;

    for (UINT i = 0; i < frameCount; i++)
    {
        double sample = sin(_radPhase);
        buffer[i] = FLOAT(sample);
        _radPhase += step; // next frame phase

        if (_radPhase >= 2 * PI)
        {
            _radPhase -= 2 * PI;
        }
    }
}

bool SetupSkipRTHeap()
{
    DWORD dwValue = 1;
    DWORD cbData = sizeof(dwValue);

    DWORD result = RegSetKeyValueW(HKEY_LOCAL_MACHINE, REGKEY_AUDIOSERVICE, REGVALUE_SKIPRTHEAP, REG_DWORD, &dwValue, cbData);

    RestartAudioService();

    return (result == ERROR_SUCCESS);
}

bool CleanupSkipRTHeap()
{
    DWORD result = RegDeleteKeyValueW(HKEY_LOCAL_MACHINE, REGKEY_AUDIOSERVICE, REGVALUE_SKIPRTHEAP);

    RestartAudioService();

    return (result == ERROR_SUCCESS);
}

bool CAPOStressTest::setUpMethod()
{
    return SetupSkipRTHeap();
}

bool CAPOStressTest::tearDownMethod()
{
    return CleanupSkipRTHeap();
}

void CAPOStressTest::AudioAPOStressTest()
{

    UINT cDevices = 0;
    com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    com_ptr_nothrow<IMMDeviceCollection> spDevices;
    vector<com_ptr<IMMDevice>> sEndpoints;

    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));
    VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spDevices));
    VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

    for (UINT i = 0; i < cDevices; i++)
    {
        wil::com_ptr<IMMDevice> pEndpoint = nullptr;

        VERIFY_SUCCEEDED(spDevices->Item(i, &pEndpoint));

        sEndpoints.push_back(pEndpoint);
    }

    RunStressTest(THIRTY_MIN_IN_SEC, sEndpoints);
}

std::wstring QueryProcessName(const DWORD pid)
{
    wil::unique_handle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
    if(NULL == hProcess)
    {
        return L"err " + std::to_wstring(GetLastError());
    }
    else
    {
        WCHAR szProcessName[1024];
        DWORD cchSize = ARRAYSIZE(szProcessName);
        if (0 == QueryFullProcessImageNameW(hProcess.get(), 0, szProcessName, &cchSize))
        {
            return L"err " + std::to_wstring(GetLastError());
        }

        PWSTR fileName = szProcessName + cchSize;
        while (fileName != szProcessName && *fileName != L'\\') fileName--;
        
        return (*fileName == L'\\') ? fileName + 1 : fileName;
    }
}

DWORD GetAudioDGProcessID()
{
    // Get the list of process identifiers.

    DWORD aProcesses[1024], cbNeeded, cProcesses;
    unsigned int i;

    if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
    {
        return 0;
    }

    // Calculate how many process identifiers were returned.

    cProcesses = cbNeeded / sizeof(DWORD);

    // Look for AudioDG name and process identifier on each process.

    for ( i = 0; i < cProcesses; i++ )
    {
        if( aProcesses[i] != 0 && 0 == _wcsicmp(QueryProcessName(aProcesses[i]).c_str(), L"AUDIODG.EXE"))
        {
            return aProcesses[i];
        }
    }

    return 0;
}

void GetAudioDGMemoryUsageSample()
{
    // Get the AudioDG process ID
    DWORD audioDGProcessID = GetAudioDGProcessID();

    if (audioDGProcessID != 0)
    {
        std::lock_guard<std::mutex> autoLock(maxAudioDGPagefileUsage_mutex);

        // Memory PagefileUsage snapshot 
        PROCESS_MEMORY_COUNTERS pmc;

        HANDLE hProcess = OpenProcess(  PROCESS_QUERY_INFORMATION |
                                        PROCESS_VM_READ,
                                        FALSE, audioDGProcessID );

        if (hProcess != NULL && GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) )
        {
            maxAudioDGPagefileUsage = max(maxAudioDGPagefileUsage, pmc.PagefileUsage);
        }

        VERIFY_IS_TRUE(maxAudioDGPagefileUsage < MAX_ALLOWED_PAGEFILE_USAGE_IN_BYTES);

        CloseHandle( hProcess );
    }
    else
    {
        Log::Warning(L"Unable to find AudioDG PID");
    }
}

HRESULT BasicAudioStreaming(IMMDevice* pEndpoint)
{
    com_ptr_nothrow<IAudioClient2> pAudioClient;
    wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    IF_FAILED_RETURN(pEndpoint->Activate(__uuidof(IAudioClient2), CLSCTX_ALL, NULL, (void**)&pAudioClient));

    AudioClientProperties clientProperties = { 0 };
    clientProperties.cbSize = sizeof(AudioClientProperties);
    clientProperties.bIsOffload = false;
    clientProperties.eCategory = (AUDIO_STREAM_CATEGORY) RandInRange(0, 11);

    IF_FAILED_RETURN(pAudioClient->SetClientProperties(&clientProperties));

    IF_FAILED_RETURN(pAudioClient->GetMixFormat(wil::out_param_ptr<WAVEFORMATEX **>(mixFormat)));

    IF_FAILED_RETURN(pAudioClient->Initialize(
        AUDCLNT_SHAREMODE_SHARED,
        AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
        0,
        0,
        mixFormat.get(),
        nullptr));

    com_ptr_nothrow<IAudioRenderClient> pRenderClient;
    IF_FAILED_RETURN(pAudioClient->GetService(_uuidof(IAudioRenderClient), wil::out_param_ptr<void **>(pRenderClient)));

    wil::unique_event bufferCompleteEvent;
    bufferCompleteEvent.create();
    IF_FAILED_RETURN(pAudioClient->SetEventHandle(bufferCompleteEvent.get()));

    UINT32 bufferFrameCount;
    REFERENCE_TIME duration = RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION);
    IF_FAILED_RETURN(pAudioClient->GetBufferSize(&bufferFrameCount));

    SineWave sw(mixFormat->nChannels, mixFormat->nSamplesPerSec);
    for (unsigned i = 0; i < mixFormat->nChannels; ++i)
    {
        sw.SetChannel(i, 400, 0.25f);
    }

    BYTE* data{};
    IF_FAILED_RETURN(pRenderClient->GetBuffer(bufferFrameCount, &data));

    sw.FillFrames(data, bufferFrameCount);
    IF_FAILED_RETURN(pRenderClient->ReleaseBuffer(bufferFrameCount, 0));

    IF_FAILED_RETURN(pAudioClient->Start());

    unsigned c = 0;
    while (c++ < duration)
    {
        if (WaitForSingleObject(bufferCompleteEvent.get(), ONE_SEC_IN_MS) != WAIT_OBJECT_0)
        {
            break;
        }

        unsigned numFramesPadding{};
        IF_FAILED_RETURN(pAudioClient->GetCurrentPadding(&numFramesPadding));
        auto numFramesAvailable = bufferFrameCount - numFramesPadding;

        IF_FAILED_RETURN(pRenderClient->GetBuffer(numFramesAvailable, &data));
        sw.FillFrames(data, numFramesAvailable);
        IF_FAILED_RETURN(pRenderClient->ReleaseBuffer(numFramesAvailable, 0));
    }

    // Get a sample of the current AudioDG memory usage
    GetAudioDGMemoryUsageSample();

    IF_FAILED_RETURN(pAudioClient->Stop());

    return S_OK;
}

HRESULT DisableEnableEndpoint(IMMDevice* pEndpoint)
{
    com_ptr_nothrow<IMMEndpointManager> pManager = nullptr;

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    IF_FAILED_RETURN(CoCreateInstance(__uuidof(MMEndpointManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pManager)));

    IF_FAILED_RETURN(pManager->SetEndpointState(pEndpoint, DEVICE_STATE_DISABLED));

    Sleep(1000);

    IF_FAILED_RETURN(pManager->SetEndpointState(pEndpoint, DEVICE_STATE_ACTIVE));

    return S_OK;
}

HRESULT BasicAudioCapture(IMMDevice* pEndpoint)
{
    com_ptr_nothrow<IAudioClient> pAudioClient;
    wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    IF_FAILED_RETURN(pEndpoint->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient));

    IF_FAILED_RETURN(pAudioClient->GetMixFormat(wil::out_param_ptr<WAVEFORMATEX **>(mixFormat)));

    IF_FAILED_RETURN(pAudioClient->Initialize(
        AUDCLNT_SHAREMODE_SHARED,
        AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
        0,
        0,
        mixFormat.get(),
        nullptr));

    com_ptr_nothrow<IAudioCaptureClient> pCaptureClient;
    IF_FAILED_RETURN(pAudioClient->GetService(_uuidof(IAudioCaptureClient), wil::out_param_ptr<void **>(pCaptureClient)));

    wil::unique_event bufferCompleteEvent;
    bufferCompleteEvent.create();
    IF_FAILED_RETURN(pAudioClient->SetEventHandle(bufferCompleteEvent.get()));

    UINT32 bufferFrameCount;
    REFERENCE_TIME duration = RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION);
    IF_FAILED_RETURN(pAudioClient->GetBufferSize(&bufferFrameCount));

    BYTE* data{};
    DWORD dwFlags = 0;
    UINT32 numFramesInNextPacket = 0;

    IF_FAILED_RETURN(pCaptureClient->GetNextPacketSize(&numFramesInNextPacket));
    IF_FAILED_RETURN(pCaptureClient->GetBuffer(&data, &numFramesInNextPacket, &dwFlags, NULL, NULL)); 

    IF_FAILED_RETURN(pAudioClient->Start());

    unsigned c = 0;
    while (c++ < duration)
    {
       if (WaitForSingleObject(bufferCompleteEvent.get(), ONE_SEC_IN_MS) != WAIT_OBJECT_0)
        {
            break;
        }

        IF_FAILED_RETURN(pCaptureClient->ReleaseBuffer(numFramesInNextPacket));

        IF_FAILED_RETURN(pCaptureClient->GetBuffer(&data, &numFramesInNextPacket, &dwFlags, NULL, NULL));
    }

    // Get a sample of the current AudioDG memory usage
    GetAudioDGMemoryUsageSample();
    
    IF_FAILED_RETURN(pAudioClient->Stop());

    return S_OK;
}

HRESULT BasicAudioLoopback(IMMDevice* pEndpoint)
{
    com_ptr_nothrow<IAudioClient> pAudioClient;
    wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    IF_FAILED_RETURN(pEndpoint->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient));

    IF_FAILED_RETURN(pAudioClient->GetMixFormat(wil::out_param_ptr<WAVEFORMATEX **>(mixFormat)));

    IF_FAILED_RETURN(pAudioClient->Initialize(
        AUDCLNT_SHAREMODE_SHARED,
        AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM,
        0,
        0,
        mixFormat.get(),
        nullptr));

    com_ptr_nothrow<IAudioCaptureClient> pCaptureClient;
    IF_FAILED_RETURN(pAudioClient->GetService(__uuidof(IAudioCaptureClient), wil::out_param_ptr<void **>(pCaptureClient)));

    wil::unique_event bufferCompleteEvent;
    bufferCompleteEvent.create();
    IF_FAILED_RETURN(pAudioClient->SetEventHandle(bufferCompleteEvent.get()));

    UINT32 bufferFrameCount;
    IF_FAILED_RETURN(pAudioClient->GetBufferSize(&bufferFrameCount));

    BYTE* data{};
    DWORD dwFlags = 0;
    UINT32 numFramesInNextPacket = 0;

    IF_FAILED_RETURN(pCaptureClient->GetNextPacketSize(&numFramesInNextPacket));

    IF_FAILED_RETURN(pAudioClient->Start());

    while (numFramesInNextPacket != 0)
    {
        IF_FAILED_RETURN(pCaptureClient->GetBuffer(&data, &numFramesInNextPacket, &dwFlags, NULL, NULL));

        IF_FAILED_RETURN(pCaptureClient->ReleaseBuffer(numFramesInNextPacket));

        IF_FAILED_RETURN(pCaptureClient->GetNextPacketSize(&numFramesInNextPacket));
    }

    // Get a sample of the current AudioDG memory usage
    GetAudioDGMemoryUsageSample();

    IF_FAILED_RETURN(pAudioClient->Stop());

    return S_OK;
}

HRESULT BasicSpatialAudio(IMMDevice* pEndpoint)
{
#ifdef LNM
    EnableSpatialAudio();
#endif

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    com_ptr_nothrow<ISpatialAudioClient> client;
    IF_FAILED_RETURN(pEndpoint->Activate(__uuidof(ISpatialAudioClient), CLSCTX_ALL, nullptr, wil::out_param_ptr<void **>(client)));

    com_ptr_nothrow<IAudioFormatEnumerator> enumer;
    IF_FAILED_RETURN(client->GetSupportedAudioObjectFormatEnumerator(&enumer));

    wil::unique_cotaskmem_ptr<WAVEFORMATEX> fmt;
    IF_FAILED_RETURN(enumer->GetFormat(0, wil::out_param_ptr<WAVEFORMATEX **>(fmt)));

    unique_event event;
    event.create();
    if (event == nullptr)
    {
        HRESULT_FROM_WIN32(GetLastError());
    }

    unique_prop_variant pv;
    auto params = reinterpret_cast<SpatialAudioObjectRenderStreamActivationParams *>(CoTaskMemAlloc(sizeof(SpatialAudioObjectRenderStreamActivationParams)));
    *params = SpatialAudioObjectRenderStreamActivationParams{fmt.get(), (AudioObjectType)AudioObjectType_FrontLeft, 0, 0, AudioCategory_Other, event.get(), nullptr};
    pv.vt = VT_BLOB;
    pv.blob.cbSize = sizeof(*params);
    pv.blob.pBlobData = reinterpret_cast<BYTE *>(params);

    com_ptr_nothrow<ISpatialAudioObjectRenderStream> stream;
    IF_FAILED_RETURN(client->ActivateSpatialAudioStream(&pv, __uuidof(ISpatialAudioObjectRenderStream), wil::out_param_ptr<void **>(stream)));

    com_ptr_nothrow<ISpatialAudioObject> audioObjectFrontLeft;
    IF_FAILED_RETURN(stream->ActivateSpatialAudioObject(AudioObjectType_FrontLeft, &audioObjectFrontLeft));

    IF_FAILED_RETURN(stream->Start());

    UINT totalFrameCount = fmt->nSamplesPerSec * 2;

    bool isRendering = true;
    while (isRendering)
    {
        // Wait for a signal from the audio-engine to start the next processing pass
        if (WaitForSingleObject(event.get(), 100) != WAIT_OBJECT_0)
        {
            IF_FAILED_RETURN(stream->Reset());
        }
    
        UINT32 availableDynamicObjectCount = 0;
        UINT32 frameCount = 0;
    
        // Begin the process of sending object data and metadata
        // Get the number of dynamic objects that can be used to send object-data
        // Get the frame count that each buffer will be filled with 
        IF_FAILED_RETURN(stream->BeginUpdatingAudioObjects(&availableDynamicObjectCount, &frameCount));
    
        BYTE* buffer = nullptr;
        UINT32 bufferLength = 0;
    
        if (audioObjectFrontLeft == nullptr)
        {
            IF_FAILED_RETURN(stream->ActivateSpatialAudioObject(AudioObjectType::AudioObjectType_FrontLeft, &audioObjectFrontLeft));
        }
    
        // Get the buffer to write audio data
        IF_FAILED_RETURN(audioObjectFrontLeft->GetBuffer(&buffer, &bufferLength));
    
        if (totalFrameCount >= frameCount)
        {
            // Write audio data to the buffer
            WriteToAudioObjectBuffer(reinterpret_cast<float*>(buffer), frameCount, 200.0f, fmt->nSamplesPerSec);
    
            totalFrameCount -= frameCount;
        }
        else
        {
            // Write audio data to the buffer
            WriteToAudioObjectBuffer(reinterpret_cast<float*>(buffer), totalFrameCount, 750.0f, fmt->nSamplesPerSec);
    
            // Get a sample of the current AudioDG memory usage
            GetAudioDGMemoryUsageSample();

            // Set end of stream for the last buffer 
            IF_FAILED_RETURN(audioObjectFrontLeft->SetEndOfStream(totalFrameCount));
    
            audioObjectFrontLeft = nullptr; // Release the object
    
            isRendering = false;
        }
    
        // Let the audio engine know that the object data are available for processing now
        IF_FAILED_RETURN(stream->EndUpdatingAudioObjects());
    }

    return S_OK;
}

HRESULT BasicOffloadStreaming(IMMDevice* pEndpoint)
{
    com_ptr_nothrow<IAudioClient2> pAudioClient;
    wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;
    BOOL bOffloadCapable = FALSE;

    if (pEndpoint == nullptr)
    {
        return E_INVALIDARG;
    }

    IF_FAILED_RETURN(pEndpoint->Activate(__uuidof(IAudioClient2), CLSCTX_ALL, NULL, (void**)&pAudioClient));
    IF_FAILED_RETURN(pAudioClient->IsOffloadCapable(AudioCategory_Media, &bOffloadCapable));

    AudioClientProperties clientProperties = { 0 };
    clientProperties.cbSize = sizeof(AudioClientProperties);
    clientProperties.bIsOffload = bOffloadCapable ? true : false;
    clientProperties.eCategory = AudioCategory_Media;

    if (bOffloadCapable)
    {
        clientProperties.bIsOffload = true;
    }

    IF_FAILED_RETURN(pAudioClient->SetClientProperties(&clientProperties));

    IF_FAILED_RETURN(pAudioClient->GetMixFormat(wil::out_param_ptr<WAVEFORMATEX **>(mixFormat)));

    IF_FAILED_RETURN(pAudioClient->Initialize(
        AUDCLNT_SHAREMODE_SHARED,
        AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
        0,
        0,
        mixFormat.get(),
        nullptr));

    com_ptr_nothrow<IAudioRenderClient> pRenderClient;
    IF_FAILED_RETURN(pAudioClient->GetService(_uuidof(IAudioRenderClient), wil::out_param_ptr<void **>(pRenderClient)));

    wil::unique_event bufferCompleteEvent;
    bufferCompleteEvent.create();
    IF_FAILED_RETURN(pAudioClient->SetEventHandle(bufferCompleteEvent.get()));

    UINT32 bufferFrameCount;
    REFERENCE_TIME duration = RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION);
    IF_FAILED_RETURN(pAudioClient->GetBufferSize(&bufferFrameCount));

    SineWave sw(mixFormat->nChannels, mixFormat->nSamplesPerSec);
    for (unsigned i = 0; i < mixFormat->nChannels; ++i)
    {
        sw.SetChannel(i, 400, 0.25f);
    }

    BYTE* data{};
    IF_FAILED_RETURN(pRenderClient->GetBuffer(bufferFrameCount, &data));

    sw.FillFrames(data, bufferFrameCount);
    IF_FAILED_RETURN(pRenderClient->ReleaseBuffer(bufferFrameCount, 0));

    IF_FAILED_RETURN(pAudioClient->Start());

    unsigned c = 0;
    while (c++ < duration)
    {
        if (WaitForSingleObject(bufferCompleteEvent.get(), ONE_SEC_IN_MS) != WAIT_OBJECT_0)
        {
            break;
        }

        unsigned numFramesPadding{};
        IF_FAILED_RETURN(pAudioClient->GetCurrentPadding(&numFramesPadding));
        auto numFramesAvailable = bufferFrameCount - numFramesPadding;

        IF_FAILED_RETURN(pRenderClient->GetBuffer(numFramesAvailable, &data));
        sw.FillFrames(data, numFramesAvailable);
        IF_FAILED_RETURN(pRenderClient->ReleaseBuffer(numFramesAvailable, 0));
    }

    // Get a sample of the current AudioDG memory usage
    GetAudioDGMemoryUsageSample();

    IF_FAILED_RETURN(pAudioClient->Stop());

    return S_OK;
}

void RunStressTest(int timeInSeconds, std::vector<wil::com_ptr<IMMDevice>>& pEndpoints)
{
    const UINT num = static_cast<UINT>(pEndpoints.size()) * NUMBER_OF_TESTS;

    std::vector<std::thread> threads(num);

    for (UINT i = 0; i < pEndpoints.size(); i++)
    {
        threads[i * NUMBER_OF_TESTS + 0] = std::thread([=](){
            BasicAudioStreaming(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 1] = std::thread([=](){
            BasicAudioStreaming(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 2] = std::thread([=](){
            BasicAudioStreaming(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 3] = std::thread([=](){
            BasicAudioCapture(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 4] = std::thread([=](){
            BasicAudioLoopback(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 5] = std::thread([=](){
            DisableEnableEndpoint(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 6] = std::thread([=](){
            BasicSpatialAudio(pEndpoints[i].get());
        });

        threads[i * NUMBER_OF_TESTS + 7] = std::thread([=](){
            BasicOffloadStreaming(pEndpoints[i].get());
        });
    }

    time_t timer;
    time(&timer);

    time_t endTime = timer + timeInSeconds;

    while (timer < endTime) 
    {
        for (UINT i = 0; i < num; i++)
        {
            if (threads[i].joinable())
            {
                UINT testNumber = i % NUMBER_OF_TESTS;
                UINT k = i / NUMBER_OF_TESTS;

                threads[i].join();

                switch (testNumber)
                {
                case 0:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicAudioStreaming(pEndpoints[k].get());
                    });
                    break;
                case 1:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicAudioStreaming(pEndpoints[k].get());
                    });
                    break;
                case 2:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicAudioStreaming(pEndpoints[k].get());
                    });
                    break;
                case 3:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicAudioCapture(pEndpoints[k].get());
                    });
                    break;
                case 4:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicAudioLoopback(pEndpoints[k].get());
                    });
                    break;
                case 5:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        DisableEnableEndpoint(pEndpoints[k].get());
                    });
                    break;
                case 6:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicSpatialAudio(pEndpoints[k].get());
                    });
                    break;
                case 7:
                    threads[i] = std::thread([=]() {
                        Sleep(RandInRange(WAIT_TIME_MIN_FOR_THREAD_OPERATION, WAIT_TIME_MAX_FOR_THREAD_OPERATION));
                        BasicOffloadStreaming(pEndpoints[k].get());
                    });
                    break;
                }
            }
        }

        time(&timer);
    }

    for (UINT j = 0; j < num; j++)
    {
        if (threads[j].joinable())
        {
            threads[j].join();
        }
    }

    std::lock_guard<std::mutex> autoLock(maxAudioDGPagefileUsage_mutex);

    // If we were not able to open the AudioDG handle process we should have 0 as memory usage
    VERIFY_IS_TRUE(maxAudioDGPagefileUsage > 0);

    // Adding the maxPagefileUsageValue to the VERIFY as a string
    wchar_t maxAudioDGPagefileUsageString[32];
    swprintf_s(maxAudioDGPagefileUsageString, L"%u", maxAudioDGPagefileUsage);

    VERIFY_IS_TRUE(maxAudioDGPagefileUsage < MAX_ALLOWED_PAGEFILE_USAGE_IN_BYTES, maxAudioDGPagefileUsageString);
}

<|start_filename|>audio/sysvad/APO/DelayAPO/DelayAPO.h<|end_filename|>
//
// DelayAPO.h -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description:
//
//   Declaration of the CDelayAPO class.
//

#pragma once

#include <audioenginebaseapo.h>
#include <BaseAudioProcessingObject.h>
#include <DelayAPOInterface.h>
#include <DelayAPODll.h>

#include <commonmacros.h>
#include <devicetopology.h>

_Analysis_mode_(_Analysis_code_type_user_driver_)

#define PK_EQUAL(x, y)  ((x.fmtid == y.fmtid) && (x.pid == y.pid))

//
// Define a GUID identifying the type of this APO's custom effect.
//
// APOs generally should not define new GUIDs for types of effects and instead
// should use predefined effect types. Only define a new GUID if the effect is
// truly very different from all predefined types of effects.
//
// {29FBFBB5-9002-4ABC-DCBC-DD45462478C8}
DEFINE_GUID(DelayEffectId,      0x29FBFBB5, 0x9002, 0x4ABC, 0xDC, 0xBC, 0xDD, 0x45, 0x46, 0x24, 0x78, 0xC8);

// 1000 ms of delay
#define HNS_DELAY HNS_PER_SECOND

#define FRAMES_FROM_HNS(hns) (ULONG)(1.0 * hns / HNS_PER_SECOND * GetFramesPerSecond() + 0.5)

LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode);

#pragma AVRT_VTABLES_BEGIN
// Delay APO class - MFX
class CDelayAPOMFX :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CDelayAPOMFX, &CLSID_DelayAPOMFX>,
    public CBaseAudioProcessingObject,
    public IMMNotificationClient,
    public IAudioSystemEffects2,
    // IAudioSystemEffectsCustomFormats may be optionally supported
    // by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    public IAudioSystemEffectsCustomFormats, 
    public IDelayAPOMFX
{
public:
    // constructor
    CDelayAPOMFX()
    :   CBaseAudioProcessingObject(sm_RegProperties)
    ,   m_hEffectsChangedEvent(NULL)
    ,   m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT)
    ,   m_fEnableDelayMFX(FALSE)
    ,   m_nDelayFrames(0)
    ,   m_iDelayIndex(0)
    {
        m_pf32Coefficients = NULL;
    }

    virtual ~CDelayAPOMFX();    // destructor

DECLARE_REGISTRY_RESOURCEID(IDR_DELAYAPOMFX)

BEGIN_COM_MAP(CDelayAPOMFX)
    COM_INTERFACE_ENTRY(IDelayAPOMFX)
    COM_INTERFACE_ENTRY(IAudioSystemEffects)
    COM_INTERFACE_ENTRY(IAudioSystemEffects2)
    // IAudioSystemEffectsCustomFormats may be optionally supported
    // by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    COM_INTERFACE_ENTRY(IAudioSystemEffectsCustomFormats)
    COM_INTERFACE_ENTRY(IMMNotificationClient)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectRT)
    COM_INTERFACE_ENTRY(IAudioProcessingObject)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

public:
    STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
        APO_CONNECTION_PROPERTY** ppOutputConnections);

    STDMETHOD(GetLatency)(HNSTIME* pTime);

    STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
        UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioSystemEffects2
    STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects)  LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event);

    virtual HRESULT ValidateAndCacheConnectionInfo(
                                    UINT32 u32NumInputConnections, 
                                    APO_CONNECTION_DESCRIPTOR** ppInputConnections, 
                                    UINT32 u32NumOutputConnections, 
                                    APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    // IMMNotificationClient
    STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) 
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        UNREFERENCED_PARAMETER(dwNewState); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
    { 
        UNREFERENCED_PARAMETER(flow); 
        UNREFERENCED_PARAMETER(role); 
        UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);

    // IAudioSystemEffectsCustomFormats
    // This interface may be optionally supported by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    STDMETHODIMP GetFormatCount(UINT* pcFormats);
    STDMETHODIMP GetFormat(UINT nFormat, IAudioMediaType** ppFormat);
    STDMETHODIMP GetFormatRepresentation(UINT nFormat, _Outptr_ LPWSTR* ppwstrFormatRep);

    // IAudioProcessingObject
    STDMETHODIMP IsOutputFormatSupported(IAudioMediaType *pOppositeFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat);

    STDMETHODIMP CheckCustomFormats(IAudioMediaType *pRequestedFormat);

public:
    LONG                                    m_fEnableDelayMFX;
    GUID                                    m_AudioProcessingMode;
    CComPtr<IPropertyStore>                 m_spAPOSystemEffectsProperties;
    CComPtr<IMMDeviceEnumerator>            m_spEnumerator;
    static const CRegAPOProperties<1>       sm_RegProperties;   // registration properties

    // Locked memory
    FLOAT32                                 *m_pf32Coefficients;

    CComHeapPtr<FLOAT32>                    m_pf32DelayBuffer;
    UINT32                                  m_nDelayFrames;
    UINT32                                  m_iDelayIndex;

private:
    CCriticalSection                        m_EffectsLock;
    HANDLE                                  m_hEffectsChangedEvent;

    HRESULT ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2);

};
#pragma AVRT_VTABLES_END


#pragma AVRT_VTABLES_BEGIN
// Delay APO class - SFX
class CDelayAPOSFX :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CDelayAPOSFX, &CLSID_DelayAPOSFX>,
    public CBaseAudioProcessingObject,
    public IMMNotificationClient,
    public IAudioSystemEffects2,
    public IDelayAPOSFX
{
public:
    // constructor
    CDelayAPOSFX()
    :   CBaseAudioProcessingObject(sm_RegProperties)
    ,   m_hEffectsChangedEvent(NULL)
    ,   m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT)
    ,   m_fEnableDelaySFX(FALSE)
    ,   m_nDelayFrames(0)
    ,   m_iDelayIndex(0)
    {
    }

    virtual ~CDelayAPOSFX();    // destructor

DECLARE_REGISTRY_RESOURCEID(IDR_DELAYAPOSFX)

BEGIN_COM_MAP(CDelayAPOSFX)
    COM_INTERFACE_ENTRY(IDelayAPOSFX)
    COM_INTERFACE_ENTRY(IAudioSystemEffects)
    COM_INTERFACE_ENTRY(IAudioSystemEffects2)
    COM_INTERFACE_ENTRY(IMMNotificationClient)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectRT)
    COM_INTERFACE_ENTRY(IAudioProcessingObject)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

public:
    STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
        APO_CONNECTION_PROPERTY** ppOutputConnections);

    STDMETHOD(GetLatency)(HNSTIME* pTime);

    STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
        UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioSystemEffects2
    STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects)  LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event);

    // IMMNotificationClient
    STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) 
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        UNREFERENCED_PARAMETER(dwNewState); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
    { 
        UNREFERENCED_PARAMETER(flow); 
        UNREFERENCED_PARAMETER(role); 
        UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);

public:
    LONG                                    m_fEnableDelaySFX;
    GUID                                    m_AudioProcessingMode;
    CComPtr<IPropertyStore>                 m_spAPOSystemEffectsProperties;
    CComPtr<IMMDeviceEnumerator>            m_spEnumerator;
    static const CRegAPOProperties<1>       sm_RegProperties;   // registration properties

    CCriticalSection                        m_EffectsLock;
    HANDLE                                  m_hEffectsChangedEvent;

    CComHeapPtr<FLOAT32>                    m_pf32DelayBuffer;
    UINT32                                  m_nDelayFrames;
    UINT32                                  m_iDelayIndex;
};
#pragma AVRT_VTABLES_END

OBJECT_ENTRY_AUTO(__uuidof(DelayAPOMFX), CDelayAPOMFX)
OBJECT_ENTRY_AUTO(__uuidof(DelayAPOSFX), CDelayAPOSFX)

//
//   Declaration of the ProcessDelay routine.
//
void ProcessDelay(
    _Out_writes_(u32ValidFrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32OutputFrames,
    _In_reads_(u32ValidFrameCount * u32SamplesPerFrame)
        const FLOAT32 *pf32InputFrames,
    UINT32       u32ValidFrameCount,
    UINT32       u32SamplesPerFrame,
    _Inout_updates_(u32DelayFrames * u32SamplesPerFrame)
        FLOAT32 *pf32DelayBuffer,
    UINT32       u32DelayFrames,
    _Inout_
        UINT32  *pu32DelayIndex );

//
//   Convenience methods
//

void WriteSilence(
    _Out_writes_(u32FrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32Frames,
    UINT32 u32FrameCount,
    UINT32 u32SamplesPerFrame );

void CopyFrames(
    _Out_writes_(u32FrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32OutFrames,
    _In_reads_(u32FrameCount * u32SamplesPerFrame)
        const FLOAT32 *pf32InFrames,
    UINT32 u32FrameCount,
    UINT32 u32SamplesPerFrame );


<|start_filename|>usb/UcmUcsiAcpiSample/UcmUcsiAcpiSample/MemoryKm.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:

    MemoryKm.cpp

Abstract:

    Memory allocation and delete definitions

Environment:

    Kernel-mode.

--*/

#include "Pch.h"

#ifdef _KERNEL_MODE

void
__cdecl
operator delete(
    void* memory
    ) noexcept
{
    ExFreePoolWithTag(memory, TAG_UCSI);
}

void
__cdecl
operator delete[](
    void* memory
    ) noexcept
{
	ExFreePoolWithTag(memory, TAG_UCSI);
}

void*
__cdecl
operator new (
    size_t size
    )
{
    return ExAllocatePool2(POOL_FLAG_NON_PAGED, size, TAG_UCSI);
}

void*
__cdecl
operator new[] (
    size_t size
    )
{
    return ExAllocatePool2(POOL_FLAG_NON_PAGED, size, TAG_UCSI);
}

#endif


<|start_filename|>general/registry/regfltr/sys/util.c<|end_filename|>
/*++
Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    util.c

Abstract: 

    Utility routines for the sample driver.

Environment:

    Kernel mode only

--*/


#include "regfltr.h"


FAST_MUTEX g_CallbackCtxListLock;
LIST_ENTRY g_CallbackCtxListHead;
USHORT g_NumCallbackCtxListEntries;


ULONG 
ExceptionFilter (
    _In_ PEXCEPTION_POINTERS ExceptionPointers
    )
/*++

Routine Description:

    ExceptionFilter breaks into the debugger if an exception happens
    inside the callback.

Arguments:

    ExceptionPointers - unused

Return Value:

    Always returns EXCEPTION_CONTINUE_SEARCH

--*/
{

    ErrorPrint("Exception %lx, ExceptionPointers = %p", 
               ExceptionPointers->ExceptionRecord->ExceptionCode,
               ExceptionPointers);

    DbgBreakPoint();
    
    return EXCEPTION_EXECUTE_HANDLER;

}


PVOID
CreateCallbackContext(
    _In_ CALLBACK_MODE CallbackMode,
    _In_ PCWSTR AltitudeString
    ) 
/*++

Routine Description:

    Utility method to create a callback context. Callback context 
    should be freed using DeleteCallbackContext.
    
Arguments:

    CallbackMode - the callback mode value

    AltitudeString - a string with the altitude the callback will be 
        registered at

Return Value:

    Pointer to the allocated and initialized callback context 

--*/
{

    PCALLBACK_CONTEXT CallbackCtx = NULL;
    NTSTATUS Status;
    BOOLEAN Success = FALSE;

    CallbackCtx = (PCALLBACK_CONTEXT) ExAllocatePoolZero (
                        PagedPool,
                        sizeof(CALLBACK_CONTEXT),
                        REGFLTR_CONTEXT_POOL_TAG);

    if (CallbackCtx == NULL) {
        ErrorPrint("CreateCallbackContext failed due to insufficient resources.");
        goto Exit;
    }

    CallbackCtx->CallbackMode = CallbackMode;
    CallbackCtx->ProcessId = PsGetCurrentProcessId();

    Status = RtlStringCbPrintfW(CallbackCtx->AltitudeBuffer,
                                 MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR),
                                 L"%s",
                                 AltitudeString);
    
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("RtlStringCbPrintfW in CreateCallbackContext failed. Status 0x%x", Status);
        goto Exit;
    }
    
    RtlInitUnicodeString (&CallbackCtx->Altitude, CallbackCtx->AltitudeBuffer);

    Success = TRUE;

  Exit:

    if (Success == FALSE) {
        if (CallbackCtx != NULL) {
            ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG);
            CallbackCtx = NULL;
        }
    }

    return CallbackCtx;
    
}


BOOLEAN
InsertCallbackContext(
    _In_ PCALLBACK_CONTEXT CallbackCtx
    )
/*++

Routine Description:

    Utility method to insert the callback context into a list. 
    
Arguments:

    CallbackCtx - the callback context to insert

Return Value:

    TRUE if successful, FALSE otherwise

--*/
{

    BOOLEAN Success = FALSE;
    
    ExAcquireFastMutex(&g_CallbackCtxListLock);

    if (g_NumCallbackCtxListEntries < MAX_CALLBACK_CTX_ENTRIES) {
        g_NumCallbackCtxListEntries++;
        InsertHeadList(&g_CallbackCtxListHead, &CallbackCtx->CallbackCtxList);
        Success = TRUE;
    } else {
        ErrorPrint("Insert Callback Ctx failed: Max CallbackCtx entries reached.");
    }

    ExReleaseFastMutex(&g_CallbackCtxListLock);

    return Success;

}


PCALLBACK_CONTEXT
FindCallbackContext(
    _In_ LARGE_INTEGER Cookie
    )
/*++

Routine Description:

    Utility method to find a callback context using the cookie value.
    
Arguments:

    Cookie - the cookie value associated with the callback context. The
             cookie is returned when CmRegisterCallbackEx is called.

Return Value:

    Pointer to the found callback context

--*/
{
   
    PCALLBACK_CONTEXT CallbackCtx = NULL;
    PLIST_ENTRY Entry;
    
    ExAcquireFastMutex(&g_CallbackCtxListLock);

    Entry = g_CallbackCtxListHead.Flink;
    while (Entry != &g_CallbackCtxListHead) {

        CallbackCtx = CONTAINING_RECORD(Entry,
                                        CALLBACK_CONTEXT,
                                        CallbackCtxList);
        if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) {
            break;
        }

        Entry = Entry->Flink;
    }

    ExReleaseFastMutex(&g_CallbackCtxListLock);

    if (CallbackCtx == NULL) {
        ErrorPrint("FindCallbackContext failed: No context with specified cookied was found.");
    }

    return CallbackCtx;
    
}

PCALLBACK_CONTEXT
FindAndRemoveCallbackContext(
    _In_ LARGE_INTEGER Cookie
    )
/*++

Routine Description:

    Utility method to find a callback context using the cookie value and then
    remove it.
    
Arguments:

    Cookie - the cookie value associated with the callback context. The
             cookie is returned when CmRegisterCallbackEx is called.

Return Value:

    Pointer to the found callback context

--*/
{
   
    PCALLBACK_CONTEXT CallbackCtx = NULL;
    PLIST_ENTRY Entry;
    
    ExAcquireFastMutex(&g_CallbackCtxListLock);

    Entry = g_CallbackCtxListHead.Flink;
    while (Entry != &g_CallbackCtxListHead) {

        CallbackCtx = CONTAINING_RECORD(Entry,
                                        CALLBACK_CONTEXT,
                                        CallbackCtxList);
        if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) {
            RemoveEntryList(&CallbackCtx->CallbackCtxList);
            g_NumCallbackCtxListEntries--;
            break;
        }
    }

    ExReleaseFastMutex(&g_CallbackCtxListLock);

    if (CallbackCtx == NULL) {
        ErrorPrint("FindAndRemoveCallbackContext failed: No context with specified cookied was found.");
    }

    return CallbackCtx;    
}


VOID
DeleteCallbackContext(
    _In_ PCALLBACK_CONTEXT CallbackCtx
    )
/*++

Routine Description:

    Utility method to delete a callback context. 
    
Arguments:

    CallbackCtx - the callback context to insert

Return Value:

    None

--*/
{

    if (CallbackCtx != NULL) {
        ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG);
    }

}


<|start_filename|>audio/sysvad/BthhfpDevice.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    common.cpp

Abstract:

    Implementation of the BthHfpDevice class.

--*/

#pragma warning (disable : 4127)

#include <initguid.h>
#include <sysvad.h>
#include "hw.h"
#include "savedata.h"
#include "IHVPrivatePropertySet.h"
#include "simple.h"

#ifdef SYSVAD_BTH_BYPASS
#include <limits.h>
#include <bthhfpddi.h>
#include <wdmguid.h>    // guild-arrival/removal
#include <devpkey.h>
#include "bthhfpminipairs.h"
#include "BthhfpDevice.h"

//
// BthHfpDevice implementation.
//

// # of sec before sync request is cancelled.
#define BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC         60 
#define BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT    5

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP
BthHfpDevice::NonDelegatingQueryInterface
(
    _In_ REFIID                 Interface,
    _COM_Outptr_ PVOID *        Object
)
/*++

Routine Description:

QueryInterface routine for BthHfpDevice

Arguments:

Interface -

Object -

Return Value:

NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(Object);

    if (IsEqualGUIDAligned(Interface, IID_IUnknown))
    {
        *Object = PVOID(PUNKNOWN(PSIDEBANDDEVICECOMMON(this)));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IBthHfpDeviceCommon))
    {
        *Object = PVOID(PSIDEBANDDEVICECOMMON(this));
    }
    else
    {
        *Object = NULL;
    }

    if (*Object)
    {
        PUNKNOWN(*Object)->AddRef();
        return STATUS_SUCCESS;
    }

    return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface

  //=============================================================================
  // Dummy stubs to override the default WDF behavior of closing the target 
  // on query remove. This driver closes and deletes the supporting objects
  // when the target removes the BTH HFP SCO Bypass interface.
  //

#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::EvtBthHfpTargetQueryRemove
(
    _In_    WDFIOTARGET     IoTarget
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetQueryRemove]"));

    UNREFERENCED_PARAMETER(IoTarget);
    return STATUS_SUCCESS;
}

#pragma code_seg("PAGE")
VOID
BthHfpDevice::EvtBthHfpTargetRemoveCanceled
(
    _In_    WDFIOTARGET     IoTarget
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveCanceled]"));

    UNREFERENCED_PARAMETER(IoTarget);
}
#pragma code_seg("PAGE")
VOID
BthHfpDevice::EvtBthHfpTargetRemoveComplete
(
    _In_    WDFIOTARGET     IoTarget
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveComplete]"));

    UNREFERENCED_PARAMETER(IoTarget);
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::Init
(
    _In_ IAdapterCommon     * Adapter,
    _In_ PUNICODE_STRING      SymbolicLinkName
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::Init]"));

    NTSTATUS                                ntStatus        = STATUS_SUCCESS;
    BthHfpDeviceNotificationWorkItemContext *wiCtx          = NULL;
    BthHfpDeviceNotificationReqContext      *reqCtx         = NULL;
    WDF_OBJECT_ATTRIBUTES                   attributes;
    WDF_IO_TARGET_OPEN_PARAMS               openParams;
    WDF_WORKITEM_CONFIG                     wiConfig;

    AddRef(); // first ref.

     //
     // Basic init of all the class' members.
     //
    m_State = eBthHfpStateInitializing;
    m_Adapter = Adapter;

    // Static config.
    m_WdfIoTarget               = NULL;
    m_SpeakerMiniports          = NULL;
    m_MicMiniports              = NULL;
    m_UnknownSpeakerTopology    = NULL;
    m_UnknownSpeakerWave        = NULL;
    m_UnknownMicTopology        = NULL;
    m_UnknownMicWave            = NULL;
    m_Descriptor                = NULL;
    m_VolumePropValues          = NULL;

    // Notification updates. 
    m_SpeakerVolumeLevel        = 0;
    m_MicVolumeLevel            = 0;
    m_ConnectionStatusLong      = FALSE;
    m_StreamStatusLong          = STATUS_INVALID_DEVICE_STATE; // Sco stream is not open.
    m_NRECDisableStatusLong     = FALSE;
        
    m_StreamReq                 = NULL;
    m_SpeakerVolumeReq          = NULL;
    m_MicVolumeReq              = NULL;
    m_ConnectionReq             = NULL;
    m_NRECDisableStatusReq      = NULL;

    m_WorkItem                  = NULL;
    m_ReqCollection             = NULL;
    m_CodecId                   = 0;
    
    m_nStreams                  = 0;

    KeInitializeEvent(&m_StreamStatusEvent, NotificationEvent, TRUE);

    InitializeListHead(&m_ListEntry);
    KeInitializeSpinLock(&m_Lock);

    RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));

    RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback));
    RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback));
    RtlZeroMemory(&m_MicVolumeCallback, sizeof(m_MicVolumeCallback));
    RtlZeroMemory(&m_MicConnectionStatusCallback, sizeof(m_MicConnectionStatusCallback));
    RtlZeroMemory(&m_SpeakerFormatChangeCallback, sizeof(m_SpeakerFormatChangeCallback));
    RtlZeroMemory(&m_MicFormatChangeCallback, sizeof(m_MicFormatChangeCallback));

    //
    // Allocate a notification WDF work-item.
    //
    WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpDeviceNotificationStatusWorkItem);
    wiConfig.AutomaticSerialization = FALSE;

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationWorkItemContext);
    attributes.ParentObject = Adapter->GetWdfDevice();
    ntStatus = WdfWorkItemCreate(&wiConfig,
                                 &attributes,
                                 &m_WorkItem);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfWorkItemCreate failed: 0x%x", ntStatus)),
        Done);

    wiCtx = GetBthHfpDeviceNotificationWorkItemContext(m_WorkItem);
    wiCtx->BthHfpDevice = this; // weak ref.

     //
     // Allocate a collection to hold notification requests for the notification work-item.
     //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfCollectionCreate(
        &attributes,
        &m_ReqCollection);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfCollectionCreate failed: 0x%x", ntStatus)),
        Done);

    //
    // Open the target interface.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(),
                                 &attributes,
                                 &m_WdfIoTarget);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfIoTargetCreate failed: 0x%x", ntStatus)),
        Done);

    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
        &openParams,
        SymbolicLinkName,
        STANDARD_RIGHTS_ALL);

    openParams.EvtIoTargetQueryRemove = EvtBthHfpTargetQueryRemove;
    openParams.EvtIoTargetRemoveCanceled = EvtBthHfpTargetRemoveCanceled;
    openParams.EvtIoTargetRemoveComplete = EvtBthHfpTargetRemoveComplete;

    ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)),
        Done);

    //
    // Make a copy of the symbolic link name.
    //
    m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength;
    m_SymbolicLinkName.Length = SymbolicLinkName->Length;
    m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePool2(POOL_FLAG_NON_PAGED,
                                                        SymbolicLinkName->MaximumLength,
                                                        MINADAPTER_POOLTAG);
    if (m_SymbolicLinkName.Buffer == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: ExAllocatePool2 failed, out of memory")),
        Done);

    RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName);

    //
    // Allocate the WDF requests for status notifications.
    //

    //
    // IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE 
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_NRECDisableStatusReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Nrec-disable status) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq);
    reqCtx->BthHfpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.bImmediate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.BoolStatus),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE 
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_SpeakerVolumeReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq);
    reqCtx->BthHfpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.bImmediate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Volume),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_MicVolumeReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Mic-Volume) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq);
    reqCtx->BthHfpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.bImmediate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.Volume),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_ConnectionReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Connection-Status) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq);
    reqCtx->BthHfpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.bImmediate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.BoolStatus),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &m_StreamReq);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)),
        Done);

    // Init context.
    reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq);
    reqCtx->BthHfpDevice = this; // weak ref.

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.bImmediate),
        &reqCtx->MemIn);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    ntStatus = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        &reqCtx->Buffer,
        sizeof(reqCtx->Buffer.NtStatus),
        &reqCtx->MemOut);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)),
        Done);

    //
    // Create wave and filter names unique to this HFP device
    //
    ntStatus = CreateFilterNames(SymbolicLinkName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Init: Creating unqie filter names for the hfp device failed, 0x%x", ntStatus)),
        Done);

    //
    // This remote device is now in running state. No need to use interlock operations
    // b/c at this time this is the only thread accessing this info.
    //
    m_State = eBthHfpStateRunning;

    //
    // Init successful.
    //
    ntStatus = STATUS_SUCCESS;

Done:
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
BthHfpDevice::~BthHfpDevice
(
    void
)
/*++

Routine Description:

Destructor for BthHfpDevice.

Arguments:

Return Value:

void

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::~BthHfpDevice]"));

    ASSERT(m_State != eBthHfpStateRunning);
    ASSERT(IsListEmpty(&m_ListEntry));

    //
    // Release ref to remote stack.
    //
    if (m_WdfIoTarget != NULL)
    {
        WdfObjectDelete(m_WdfIoTarget);
        m_WdfIoTarget = NULL;
    }

    //
    // Free symbolic links.
    //
    if (m_SymbolicLinkName.Buffer != NULL)
    {
        ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG);
        RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));
    }

    DeleteCustomEndpointMinipair(m_SpeakerMiniports);
    m_SpeakerMiniports = NULL;

    DeleteCustomEndpointMinipair(m_MicMiniports);
    m_MicMiniports = NULL;

    if (m_Descriptor != NULL)
    {
        ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG);
        m_Descriptor = NULL;
    }

    if (m_VolumePropValues != NULL)
    {
        ExFreePoolWithTag(m_VolumePropValues, MINADAPTER_POOLTAG);
        m_VolumePropValues = NULL;
    }

    //
    // Free Irps.
    //
    if (m_SpeakerVolumeReq != NULL)
    {
        BthHfpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_SpeakerVolumeReq);
        m_SpeakerVolumeReq = NULL;
    }

    if (m_MicVolumeReq != NULL)
    {
        BthHfpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_MicVolumeReq);
        m_MicVolumeReq = NULL;
    }

    if (m_ConnectionReq != NULL)
    {
        BthHfpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_ConnectionReq);
        m_ConnectionReq = NULL;
    }

    if (m_StreamReq != NULL)
    {
        BthHfpDeviceNotificationReqContext * ctx;

        // Delete the associated memory objects.
        ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq);
        if (ctx->MemIn != NULL)
        {
            WdfObjectDelete(ctx->MemIn);
            ctx->MemIn = NULL;
        }

        if (ctx->MemOut != NULL)
        {
            WdfObjectDelete(ctx->MemOut);
            ctx->MemOut = NULL;
        }

        // Delete the request.
        WdfObjectDelete(m_StreamReq);
        m_StreamReq = NULL;
    }

    //
    // Notification work-item.
    //
    if (m_WorkItem != NULL)
    {
        WdfObjectDelete(m_WorkItem);
        m_WorkItem = NULL;
    }

    //
    // Notification req. collection.
    //
    if (m_ReqCollection != NULL)
    {
        WdfObjectDelete(m_ReqCollection);
        m_ReqCollection = NULL;
    }

    ASSERT(m_UnknownSpeakerTopology == NULL);
    SAFE_RELEASE(m_UnknownSpeakerTopology);

    ASSERT(m_UnknownSpeakerWave == NULL);
    SAFE_RELEASE(m_UnknownSpeakerWave);

    ASSERT(m_UnknownMicTopology == NULL);
    SAFE_RELEASE(m_UnknownMicTopology);

    ASSERT(m_UnknownMicWave == NULL);
    SAFE_RELEASE(m_UnknownMicWave);

    ASSERT(m_nStreams == 0);

    ASSERT(m_SpeakerVolumeCallback.Handler == NULL);
    ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL);
    ASSERT(m_MicVolumeCallback.Handler == NULL);
    ASSERT(m_MicConnectionStatusCallback.Handler == NULL);
    ASSERT(m_SpeakerFormatChangeCallback.Handler == NULL);
    ASSERT(m_MicFormatChangeCallback.Handler == NULL);
} // ~BthHfpDevice

//
// ISidebandDeviceCommon implementation.
//

//=============================================================================
#pragma code_seg("PAGE")
BOOL
BthHfpDevice::IsVolumeSupported
(
    _In_ eDeviceType deviceType
)
{
    UNREFERENCED_PARAMETER(deviceType);
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::IsVolumeSupported]"));

    return m_Descriptor->SupportsVolume;
}

//=============================================================================
#pragma code_seg("PAGE")
PVOID
BthHfpDevice::GetVolumeSettings
(
    _In_ eDeviceType deviceType,
    _Out_ PULONG    Size
)
{
    UNREFERENCED_PARAMETER(deviceType);
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetVolumeSettings]"));

    ASSERT(Size != NULL);

    *Size = m_Descriptor->VolumePropertyValuesSize;

    return m_VolumePropValues;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetVolume
(
    _In_    eDeviceType     DeviceType,
    _In_    LONG            Channel,
    _Out_   LONG            *pVolume
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(Channel);
    DPF_ENTER(("[BthHfpDevice::GetSpeakerVolume]"));

    NTSTATUS status = STATUS_SUCCESS;

    ASSERT(pVolume);

    if (eBthHfpSpeakerDevice == DeviceType)
    {
        status = GetBthHfpSpeakerVolume(pVolume);
        IF_FAILED_ACTION_JUMP(
            status,
            DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", status)),
            Done);

        InterlockedExchange(&m_SpeakerVolumeLevel, *pVolume);
    }
    else if(eBthHfpMicDevice == DeviceType)
    {
        status = GetBthHfpMicVolume(pVolume);
        IF_FAILED_ACTION_JUMP(
            status,
            DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", status)),
            Done);

        InterlockedExchange(&m_MicVolumeLevel, *pVolume);
    }
    else
    {
        ASSERTMSG("Invalid device type", FALSE);
        status = STATUS_INVALID_PARAMETER;
    }

Done:
    return status;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetVolume
(
    _In_    eDeviceType     DeviceType,
    _In_    LONG            Channel,
    _In_    LONG            Volume
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(Channel);
    DPF_ENTER(("[BthHfpDevice::SetSpeakerVolume]"));

    if (eBthHfpSpeakerDevice == DeviceType)
    {
        return SetBthHfpSpeakerVolume(Volume);
    }
    else if(eBthHfpMicDevice == DeviceType)
    {
        return SetBthHfpMicVolume(Volume);
    }
    else
    {
        ASSERTMSG("Invalid device type", FALSE);
        return STATUS_INVALID_PARAMETER;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
BthHfpDevice::IsMuteSupported
(
    _In_    eDeviceType     DeviceType
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(DeviceType);
    DPF_ENTER(("[BthHfpDevice::IsMuteSupported]"));

    return FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
PVOID
BthHfpDevice::GetMuteSettings
(
    _In_    eDeviceType     DeviceType,
    _Out_   PULONG          Size
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(DeviceType);
    UNREFERENCED_PARAMETER(Size);
    DPF_ENTER(("[BthHfpDevice::GetMuteSettings]"));

    return NULL;
}

//=============================================================================
#pragma code_seg("PAGE")
LONG
BthHfpDevice::GetMute
(
    _In_    eDeviceType     DeviceType,
    _In_    LONG            Channel
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(DeviceType);
    UNREFERENCED_PARAMETER(Channel);
    DPF_ENTER(("[BthHfpDevice::GetMute]"));

    return 0;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetMute
(
    _In_    eDeviceType     DeviceType,
    _In_    LONG            Channel,
    _In_    LONG            Mute
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(DeviceType);
    UNREFERENCED_PARAMETER(Channel);
    UNREFERENCED_PARAMETER(Mute);
    DPF_ENTER(("[BthHfpDevice::SetMute]"));

    return STATUS_NOT_IMPLEMENTED;
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
BthHfpDevice::GetConnectionStatus()
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetConnectionStatus]"));

    return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0);
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpCodecId(_Out_ UCHAR * CodecId)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpCodecId]"));

    ASSERT(CodecId != NULL);

    NTSTATUS ntStatus = STATUS_SUCCESS;

    union {
        HFP_BYPASS_CODEC_ID_V1 CodecIdV1;
        HFP_BYPASS_CODEC_ID_VERSION Version;
    } value;

    *CodecId = 0;

    value.Version = REQ_HFP_BYPASS_CODEC_ID_V1;

    //
    // Get the Bth HFP SCO Codec ID.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_GET_CODEC_ID,
        sizeof(value.Version),
        sizeof(value.CodecIdV1),
        &value);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpCodecId: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CODEC_ID) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *CodecId = value.CodecIdV1.CodecId;
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::Connect()
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::Connect]"));

    return SetBthHfpConnect();
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::Disconnect()
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::Disconnect]"));

    return SetBthHfpDisconnect();
}

//=============================================================================
#pragma code_seg()
BOOL
BthHfpDevice::GetStreamStatus(_In_ eDeviceType deviceType)
{
    UNREFERENCED_PARAMETER(deviceType);
    DPF_ENTER(("[BthHfpDevice::GetStreamStatus]"));

    NTSTATUS ntStatus;

    ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_StreamStatusLong, 0, 0);

    return NT_SUCCESS(ntStatus) ? TRUE : FALSE;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::StreamOpen(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(deviceType);
    DPF_ENTER(("[BthHfpDevice::StreamOpen]"));

    // BTH HFP DDI has stream open and close.
    // No start and suspend commands
    // START = BTH HFP STREAM OPEN
    // SUSPEND = BTH HFP STREAM CLOSE
    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::StreamStart
(
    _In_ eDeviceType deviceType
)
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(deviceType);
    DPF_ENTER(("[BthHfpDevice::StreamStart]"));

    NTSTATUS    ntStatus = STATUS_SUCCESS;
    LONG        nStreams = 0;

    ASSERT(m_nStreams >= 0);

    nStreams = InterlockedIncrement(&m_nStreams);
    if (nStreams == 1)
    {
        BOOLEAN  streamStart = FALSE;

        ntStatus = SetBthHfpStreamOpen();
        if (NT_SUCCESS(ntStatus))
        {
            streamStart = TRUE;
            m_StreamStatus = STATUS_SUCCESS;
            ntStatus = EnableBthHfpStreamStatusNotification();
        }

        //
        // Cleanup if any error.
        //
        if (!NT_SUCCESS(ntStatus))
        {
            nStreams = InterlockedDecrement(&m_nStreams);
            ASSERT(nStreams == 0);
            UNREFERENCED_VAR(nStreams);

            if (streamStart)
            {
                SetBthHfpStreamClose();
            }

            m_StreamStatus = STATUS_INVALID_DEVICE_STATE;
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::StreamSuspend
(
    _In_ eDeviceType deviceType
)
{
    UNREFERENCED_PARAMETER(deviceType);
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::StreamSuspend]"));

    NTSTATUS    ntStatus = STATUS_SUCCESS;
    LONG        nStreams = 0;

    ASSERT(m_nStreams > 0);

    nStreams = InterlockedDecrement(&m_nStreams);
    if (nStreams == 0)
    {
        ntStatus = SetBthHfpStreamClose();

        StopBthHfpStreamStatusNotification();

        m_StreamStatus = STATUS_INVALID_DEVICE_STATE;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::StreamClose
(
    _In_ eDeviceType deviceType
)
{
    UNREFERENCED_PARAMETER(deviceType);
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::StreamClose]"));

    // BTH HFP DDI has stream open and close.
    // No start and suspend commands
    // START = BTH HFP STREAM OPEN
    // SUSPEND = BTH HFP STREAM CLOSE
    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
GUID
BthHfpDevice::GetContainerId(_In_ eDeviceType deviceType)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetContainerId]"));

    UNREFERENCED_PARAMETER(deviceType);

    return m_Descriptor->ContainerId;
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::SetVolumeHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetVolumeHandler]"));

    if (deviceType == eBthHfpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL);

        m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref.
        m_SpeakerVolumeCallback.Context = EventHandlerContext;
    }
    else if (deviceType == eBthHfpMicDevice)
    {
        ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL);

        m_MicVolumeCallback.Handler = EventHandler; // weak ref.
        m_MicVolumeCallback.Context = EventHandlerContext;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::SetMuteHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetMuteHandler]"));

    if (deviceType == eBthHfpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerMuteCallback.Handler == NULL);

        m_SpeakerMuteCallback.Handler = EventHandler; // weak ref.
        m_SpeakerMuteCallback.Context = EventHandlerContext;
    }
    else if (deviceType == eBthHfpMicDevice)
    {
        ASSERT(EventHandler == NULL || m_MicMuteCallback.Handler == NULL);

        m_MicMuteCallback.Handler = EventHandler; // weak ref.
        m_MicMuteCallback.Context = EventHandlerContext;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::SetConnectionStatusHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetConnectionStatusHandler]"));

    if (deviceType == eBthHfpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL);

        m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref.
        m_SpeakerConnectionStatusCallback.Context = EventHandlerContext;
    }
    else if (deviceType == eBthHfpMicDevice)
    {
        ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL);

        m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref.
        m_MicConnectionStatusCallback.Context = EventHandlerContext;
    }

}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::SetFormatChangeHandler
(
    _In_        eDeviceType             deviceType,
    _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
    _In_opt_    PVOID                   EventHandlerContext
)
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetFormatChangeHandler]"));

    if (deviceType == eBthHfpSpeakerDevice)
    {
        ASSERT(EventHandler == NULL || m_SpeakerFormatChangeCallback.Handler == NULL);

        m_SpeakerFormatChangeCallback.Handler = EventHandler; // weak ref.
        m_SpeakerFormatChangeCallback.Context = EventHandlerContext;
    }
    else if (deviceType == eBthHfpMicDevice)
    {
        ASSERT(EventHandler == NULL || m_MicFormatChangeCallback.Handler == NULL);

        m_MicFormatChangeCallback.Handler = EventHandler; // weak ref.
        m_MicFormatChangeCallback.Context = EventHandlerContext;
    }

}

//=============================================================================
#pragma code_seg("PAGE")
PPIN_DEVICE_FORMATS_AND_MODES
BthHfpDevice::GetFormatsAndModes
(
    _In_        eDeviceType             deviceType
)
{
    PAGED_CODE();

    PPIN_DEVICE_FORMATS_AND_MODES pFormatsAndModes = NULL;
    ULONG endpointIndex = 0;


    // CVSD is the narrow band codec for SCO. Wideband codec IDs are any number higher than 1.
    // mSBC is the only required wideband codec, though the controller+headset combination may
    // support other wideband codecs.

    enum
    {
        BTHHFP_CODEC_CVSD = 0,
        BTHHFP_CODEC_mSBC,

        BTHHFP_CODEC_INVALID
    };

    if (m_CodecId > BTHHFP_CODEC_CVSD)
    {
        // Use the miniport tables that support 16kHz
        endpointIndex = 1;
    }

    if (deviceType == eBthHfpSpeakerDevice)
    {
        pFormatsAndModes = g_BthHfpRenderEndpoints[endpointIndex]->PinDeviceFormatsAndModes;
    }
    else if (deviceType == eBthHfpMicDevice)
    {
        pFormatsAndModes = g_BthHfpCaptureEndpoints[endpointIndex]->PinDeviceFormatsAndModes;
    }

    return pFormatsAndModes;
}

//=============================================================================
#pragma code_seg()
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOL
BthHfpDevice::IsNRECSupported()
{
    DPF_ENTER(("[BthHfpDevice::IsNRECSupported]"));

    return m_Descriptor->SupportsNREC;
}

//=============================================================================
#pragma code_seg("PAGE")
BOOL
BthHfpDevice::GetNRECDisableStatus()
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetNRECDisableStatus]"));

    // Return TRUE if HF wants to disable the NREC on the AG.
    return (BOOL)InterlockedCompareExchange(&m_NRECDisableStatusLong, 0, 0);
}

//
// Helper functions.
//

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::SendIoCtrlAsynchronously
(
    _In_        WDFREQUEST      Request,
    _In_        ULONG           IoControlCode,
    _In_opt_    WDFMEMORY       MemIn,
    _In_opt_    WDFMEMORY       MemOut,
    _In_        PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
    _In_        WDFCONTEXT      Context
)
/*++

Routine Description:

This function aynchronously sends an I/O Ctrl request to the BTH HFP
SCO Bypass device.

--*/
{
    DPF_ENTER(("[BthHfpDevice::SendIoCtrlAsynchronously]"));

    NTSTATUS    ntStatus = STATUS_SUCCESS;
    BOOLEAN     fSent = FALSE;

    //
    // Format and send the request.
    //
    ntStatus = WdfIoTargetFormatRequestForIoctl(
        m_WdfIoTarget,
        Request,
        IoControlCode,
        MemIn,
        NULL,
        MemOut,
        NULL);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)),
        Done);

    WdfRequestSetCompletionRoutine(
        Request,
        CompletionRoutine,
        Context);

    fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL);  // no options.
    if (fSent == FALSE)
    {
        ntStatus = WdfRequestGetStatus(Request);
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }

        DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus));
        goto Done;
    }

    //
    // All Done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SendIoCtrlSynchronously
(
    _In_opt_    WDFREQUEST  Request,
    _In_        ULONG       IoControlCode,
    _In_        ULONG       InLength,
    _In_        ULONG       OutLength,
    _When_(InLength > 0 || OutLength > 0, _In_)
    _When_(InLength == 0 && OutLength == 0, _In_opt_)
    PVOID       Buffer
)
/*++

Routine Description:

This function inits and synchronously sends an I/O Ctrl request to the BTH HFP
SCO Bypass device.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SendIoCtrlSynchronously]"));

    NTSTATUS                    ntStatus    = STATUS_SUCCESS;
    PWDF_MEMORY_DESCRIPTOR      memInPtr    = NULL;
    PWDF_MEMORY_DESCRIPTOR      memOutPtr   = NULL;
    WDF_MEMORY_DESCRIPTOR       memIn;
    WDF_MEMORY_DESCRIPTOR       memOut;
    WDF_REQUEST_SEND_OPTIONS    reqOpts;

    //
    // Format and send the request.
    //
    if (InLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, Buffer, InLength);
        memInPtr = &memIn;
    }

    if (OutLength)
    {
        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength);
        memOutPtr = &memOut;
    }

    WDF_REQUEST_SEND_OPTIONS_INIT(
        &reqOpts,
        WDF_REQUEST_SEND_OPTION_TIMEOUT |
        WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);

    reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC);

    ntStatus = WdfIoTargetSendIoctlSynchronously(
        m_WdfIoTarget,
        Request,
        IoControlCode,
        memInPtr,
        memOutPtr,
        &reqOpts,
        NULL);      // bytes returned.

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_VERBOSE, ("SendIoCtrlSynchronously: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)),
        Done);

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
VOID
BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem
(
    _In_    WDFWORKITEM WorkItem
)
/*++

Routine Description:

The function processes status notification updates.

Arguments:

WorkItem    - WDF work-item object.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem]"));

    NTSTATUS            ntStatus = STATUS_SUCCESS;
    BthHfpDevice      * This;
    KIRQL               oldIrql;

    if (WorkItem == NULL)
    {
        return;
    }

    This = GetBthHfpDeviceNotificationWorkItemContext(WorkItem)->BthHfpDevice;
    ASSERT(This != NULL);

    for (;;)
    {
        BOOL                                    resend  = TRUE;
        WDFREQUEST                              req     = NULL;
        BthHfpDeviceNotificationReqContext    * reqCtx;
        WDF_REQUEST_COMPLETION_PARAMS           params;

        //
        // Retrieve a task.
        //
        KeAcquireSpinLock(&This->m_Lock, &oldIrql);
        req = (WDFREQUEST)WdfCollectionGetFirstItem(This->m_ReqCollection);
        if (req != NULL)
        {
            WdfCollectionRemove(This->m_ReqCollection, req);
        }
        KeReleaseSpinLock(&This->m_Lock, oldIrql);

        if (req == NULL)
        {
            break;
        }

        //
        // Get request parameters and context.
        //
        WDF_REQUEST_COMPLETION_PARAMS_INIT(&params);
        WdfRequestGetCompletionParams(req, &params);

        reqCtx = GetBthHfpDeviceNotificationReqContext(req);
        ASSERT(reqCtx != NULL);

        //
        // Handle this notification.
        //
        if (NT_SUCCESS(params.IoStatus.Status))
        {
            switch (params.Parameters.Ioctl.IoControlCode)
            {
            case IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE:
            {
                InterlockedExchange(&This->m_NRECDisableStatusLong, (LONG)reqCtx->Buffer.BoolStatus);
            }
            break;

            case IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE:
            {
                LONG oldVolume;

                oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume);
                if (reqCtx->Buffer.Volume != oldVolume)
                {
                    // Notify audio miniport about this change.
                    if (This->m_SpeakerVolumeCallback.Handler != NULL)
                    {
                        This->m_SpeakerVolumeCallback.Handler(
                            This->m_SpeakerVolumeCallback.Context);
                    }
                }
            }
            break;

            case IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE:
            {
                LONG oldVolume;

                oldVolume = InterlockedExchange(&This->m_MicVolumeLevel, reqCtx->Buffer.Volume);
                if (reqCtx->Buffer.Volume != oldVolume)
                {
                    // Notify audio miniport about this change.
                    if (This->m_MicVolumeCallback.Handler != NULL)
                    {
                        This->m_MicVolumeCallback.Handler(
                            This->m_MicVolumeCallback.Context);
                    }
                }
            }
            break;

            case IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE:
            {
                BOOL oldStatus;
                //

                //
                // Get codec id (if non-zero, connection supports Wideband Speech)
                //
                UCHAR codecId = 0;
                UCHAR oldCodecId = 0;
                if (reqCtx->Buffer.BoolStatus)
                {
                    ntStatus = This->GetBthHfpCodecId(&codecId);
                    if (ntStatus == STATUS_INVALID_DEVICE_REQUEST)
                    {
                        // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't
                        // support Wideband Speech (currently only Mobile supports this call)
                        ntStatus = STATUS_SUCCESS;
                    }

                    if (NT_SUCCESS(ntStatus))
                    {
                        oldCodecId = (UCHAR)InterlockedExchange(&This->m_CodecId, codecId);
                        if (codecId != oldCodecId)
                        {
                            // Notify audio miniport about this change.
                            if (This->m_SpeakerFormatChangeCallback.Handler != NULL)
                            {
                                This->m_SpeakerFormatChangeCallback.Handler(
                                    This->m_SpeakerFormatChangeCallback.Context);
                            }

                            if (This->m_MicFormatChangeCallback.Handler != NULL)
                            {
                                This->m_MicFormatChangeCallback.Handler(
                                    This->m_MicFormatChangeCallback.Context);
                            }
                        }
                    }
                }

                oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.BoolStatus);
                if (reqCtx->Buffer.BoolStatus != oldStatus)
                {
                    // Notify audio miniport about this change.
                    if (This->m_SpeakerConnectionStatusCallback.Handler != NULL)
                    {
                        This->m_SpeakerConnectionStatusCallback.Handler(
                            This->m_SpeakerConnectionStatusCallback.Context);
                    }

                    if (This->m_MicConnectionStatusCallback.Handler != NULL)
                    {
                        This->m_MicConnectionStatusCallback.Handler(
                            This->m_MicConnectionStatusCallback.Context);
                    }
                }
            }
            break;

            default:
                // This should never happen.
                resend = FALSE;
                DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: invalid request ctrl 0x%x",
                    params.Parameters.Ioctl.IoControlCode));
                break;
            }
        }

        if (resend)
        {
            WDF_REQUEST_REUSE_PARAMS    reuseParams;

            WDF_REQUEST_REUSE_PARAMS_INIT(
                &reuseParams,
                WDF_REQUEST_REUSE_NO_FLAGS,
                STATUS_SUCCESS);

            ntStatus = WdfRequestReuse(req, &reuseParams);
            if (!NT_SUCCESS(ntStatus))
            {
                DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: WdfRequestReuse failed, 0x%x", ntStatus));
                break;
            }

            // Resend status notification request.
            reqCtx->Buffer.bImmediate = FALSE;

            ntStatus = This->SendIoCtrlAsynchronously(
                req,
                params.Parameters.Ioctl.IoControlCode,
                reqCtx->MemIn,
                reqCtx->MemOut,
                EvtBthHfpDeviceNotificationStatusCompletion,
                This);

            if (!NT_SUCCESS(ntStatus))
            {
                DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: SendIoCtrlAsynchronously"
                    "(0x%x) failed, 0x%x",
                    params.Parameters.Ioctl.IoControlCode, ntStatus));
                break;
            }
        }
    }
}

//=============================================================================
#pragma code_seg()
void
BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion
(
    _In_  WDFREQUEST Request,
    _In_  WDFIOTARGET Target,
    _In_  PWDF_REQUEST_COMPLETION_PARAMS Params,
    _In_  WDFCONTEXT Context
)
{
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion]"));

    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext    * ctx         = NULL;
    BthHfpDevice                          * This        = NULL;
    KIRQL                                   oldIrql;

    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);

    ctx = GetBthHfpDeviceNotificationReqContext(Request);
    This = ctx->BthHfpDevice;
    ASSERT(This != NULL);

    ntStatus = Params->IoStatus.Status;
    if (ntStatus == STATUS_CANCELLED)
    {
        // BTH HFP device is shutting down. Do not re-send this request.
        goto Done;
    }

    //
    // If something is wrong with the HFP interface, do not loop forever.
    //
    if (!NT_SUCCESS(ntStatus))
    {
        if (++ctx->Errors > BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT)
        {
            // Too many errors. Do not re-send this request.
            goto Done;
        }
    }
    else
    {
        // reset the # of errors.
        ctx->Errors = 0;
    }

    // 
    // Let the work-item thread process this request.
    //
    KeAcquireSpinLock(&This->m_Lock, &oldIrql);

    ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request);
    if (NT_SUCCESS(ntStatus))
    {
        WdfWorkItemEnqueue(This->m_WorkItem);
    }

    KeReleaseSpinLock(&This->m_Lock, oldIrql);

Done:;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpDeviceCapabilities
(
    _In_    BOOL bSupports16kHzSampling

)
/*++

Routine Description:

This function synchronously informs the Audio Driver capabilities to BthHfp stack.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpDeviceCapabilities]"));

    NTSTATUS                            ntStatus = STATUS_SUCCESS;
    BTHHFP_AUDIO_DEVICE_CAPABILTIES     deviceCaps = { 0 };

    BTHHFP_AUDIO_DEVICE_CAPABILTIES_INIT(&deviceCaps);
    deviceCaps.Supports16kHzSampling = bSupports16kHzSampling;

    //
    // Get the Bth HFP SCO Bypass speaker volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_INDICATE_AUDIO_DEVICE_CAPABILITIES,
        sizeof(BTHHFP_AUDIO_DEVICE_CAPABILTIES),
        0,
        &deviceCaps);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpDeviceCapabilities: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_INDICATE_AUDIO_DEVICE_CAPABILITIES) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpDescriptor
(
    _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor
)
/*++

Routine Description:

This function synchronously gets the remote Bluetooth Hands-Free Profile SCO
Bypass descriptor.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpDescriptor]"));

    NTSTATUS                    ntStatus    = STATUS_SUCCESS;
    WDFREQUEST                  req         = NULL;
    PBTHHFP_DESCRIPTOR2         descriptor  = NULL;
    ULONG                       length      = 0;
    ULONG_PTR                   information = 0;
    WDF_REQUEST_REUSE_PARAMS    reuseParams;
    WDF_OBJECT_ATTRIBUTES       attributes;

    *Descriptor = NULL;

    //
    // Allocate and format a WDF request.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_Adapter->GetWdfDevice();
    ntStatus = WdfRequestCreate(
        &attributes,
        m_WdfIoTarget,
        &req);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Get the size of the buffer.
    //
    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2,
        NULL,
        NULL,
        NULL);

    if (ntStatus != STATUS_BUFFER_TOO_SMALL)
    {
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
        }

        DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2): failed, 0x%x", ntStatus));
        goto Done;
    }

    ntStatus = STATUS_SUCCESS;

    information = WdfRequestGetInformation(req);
    if (information == 0 || information > ULONG_MAX)
    {
        ntStatus = STATUS_INVALID_DEVICE_STATE;
        DPF(D_ERROR, ("GetBthHfpDescriptor: IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 buffer too big (%Id): 0x%x", information, ntStatus));
        goto Done;
    }

    length = (ULONG)information;

    //
    // Allocate memory needed to hold the info.
    //
    descriptor = (PBTHHFP_DESCRIPTOR2)ExAllocatePool2(POOL_FLAG_NON_PAGED, length, MINADAPTER_POOLTAG);
    if (descriptor == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Get the Bth HFP SCO Bypass descriptor.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);

    ntStatus = WdfRequestReuse(req, &reuseParams);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestReuse failed, 0x%x", ntStatus)),
        Done);

    ntStatus = SendIoCtrlSynchronously(
        req,
        IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2,
        NULL,
        length,
        descriptor);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Descriptor = descriptor;
    ntStatus = STATUS_SUCCESS;

Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (descriptor != NULL)
        {
            ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG);
        }
    }

    if (req != NULL)
    {
        WdfObjectDelete(req);
        req = NULL;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::EnableBthHfpNrecDisableStatusNotification()
/*++

Routine Description:

This function registers for Bluetooth Hands-Free Profile SCO Bypass NREC-Disable
status change notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EnableBthHfpNrecDisableStatusNotification]"));

    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext    * ctx         = NULL;

    ctx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.bImmediate = FALSE;

    //
    // Get the Bth HFP SCO Bypass NREC-Disable status (async).
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_NRECDisableStatusReq,
        IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtBthHfpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("EnableBthHfpNrecDisableStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpVolumePropertyValues
(
    _In_  ULONG                 Length,
    _Out_ PKSPROPERTY_VALUES  * PropValues
)
/*++

Routine Description:

This function synchronously gets the remote Bluetooth Hands-Free Profile SCO
Bypass volume values.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpVolumePropertyValues]"));

    NTSTATUS            ntStatus    = STATUS_SUCCESS;
    PKSPROPERTY_VALUES  propValues  = NULL;

    *PropValues = NULL;

    //
    // Allocate memory.
    //
    propValues = (PKSPROPERTY_VALUES)ExAllocatePool2(POOL_FLAG_NON_PAGED, Length, MINADAPTER_POOLTAG);
    if (propValues == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePool2 failed, out of memory")),
        Done);

    //
    // Get the Bth HFP SCO Bypass descriptor.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES,
        0,
        Length,
        propValues);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *PropValues = propValues;
    ntStatus = STATUS_SUCCESS;

Done:
    if (!NT_SUCCESS(ntStatus))
    {
        if (propValues != NULL)
        {
            ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG);
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpSpeakerVolume
(
    _In_ LONG  Volume
)
/*++

Routine Description:

This function synchronously sets the remote Bluetooth Hands-Free Profile SCO
Bypass speaker volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpSpeakerVolume]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    //
    // Get the Bth HFP SCO Bypass speaker volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_SPEAKER_SET_VOLUME,
        sizeof(Volume),
        0,
        &Volume);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_SET_VOLUME) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpSpeakerVolume
(
    _Out_ LONG  * Volume
)
/*++

Routine Description:

This function synchronously gets the remote Bluetooth Hands-Free Profile SCO
Bypass speaker volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpSpeakerVolume]"));

    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationBuffer  buffer      = { 0 };

    *Volume = 0;

    buffer.bImmediate = TRUE;

    //
    // Get the Bth HFP SCO Bypass speaker volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE,
        sizeof(buffer.bImmediate),
        sizeof(buffer.Volume),
        &buffer);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Volume = buffer.Volume;
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification()
/*++

Routine Description:

This function registers for Bluetooth Hands-Free Profile SCO Bypass speaker
volume change notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification]"));

    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext    * ctx         = NULL;

    ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.bImmediate = FALSE;

    //
    // Register for speaker volume updates.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_SpeakerVolumeReq,
        IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtBthHfpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("EnableBthHfpSpeakerVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpMicVolume
(
    _In_ LONG  Volume
)
/*++

Routine Description:

This function synchronously sets the remote Bluetooth Hands-Free Profile SCO
Bypass mic volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpMicVolume]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    //
    // Get the Bth HFP SCO Bypass mic volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_MIC_SET_VOLUME,
        sizeof(Volume),
        0,
        &Volume);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_SET_VOLUME) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpMicVolume
(
    _Out_ LONG  * Volume
)
/*++

Routine Description:

This function synchronously gets the remote Bluetooth Hands-Free Profile SCO
Bypass mic volume.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpMicVolume]"));

    NTSTATUS                        ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationBuffer  buffer      = { 0 };

    *Volume = 0;

    buffer.bImmediate = TRUE;

    //
    // Get the Bth HFP SCO Bypass mic volume.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE,
        sizeof(buffer.bImmediate),
        sizeof(buffer.Volume),
        &buffer);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *Volume = buffer.Volume;
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::EnableBthHfpMicVolumeStatusNotification()
/*++

Routine Description:

This function registers for Bluetooth Hands-Free Profile SCO Bypass mic
volume change notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EnableBthHfpMicVolumeStatusNotification]"));

    NTSTATUS                               ntStatus = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext   * ctx      = NULL;

    ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq);

    //
    // This is a notification request.
    //
    ctx->Buffer.bImmediate = FALSE;

    //
    // Register for mic volume updates.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_MicVolumeReq,
        IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtBthHfpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("EnableBthHfpMicVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::GetBthHfpConnectionStatus
(
    _Out_ BOOL * ConnectionStatus
)
/*++

Routine Description:

This function synchronously gets the remote Bluetooth Hands-Free Profile SCO
Bypass connection status.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::GetBthHfpConnectionStatus]"));

    NTSTATUS            ntStatus    = STATUS_SUCCESS;
    BOOL                bValue      = TRUE; // In: bImmediate, Out: value.

    *ConnectionStatus = 0;

    //
    // Get the Bth HFP SCO Bypass connection status.
    //
    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE,
        sizeof(bValue),
        sizeof(bValue),
        &bValue);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("GetBthHfpConnectionStatus: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    *ConnectionStatus = bValue;
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::EnableBthHfpConnectionStatusNotification()
/*++

Routine Description:

This function registers for Bluetooth Hands-Free Profile SCO Bypass
connection status notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EnableBthHfpConnectionStatusNotification]"));

    NTSTATUS                               ntStatus = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext   * ctx      = NULL;

    ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq);

    //
    // Make sure this obj is alive while the IRP is active.
    //
    ctx->Buffer.bImmediate = FALSE;

    //
    // Get the Bth HFP SCO Bypass connection status.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_ConnectionReq,
        IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtBthHfpDeviceNotificationStatusCompletion,
        this);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("EnableBthHfpConnectionStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpConnect()
/*++

Routine Description:

This function synchronously requests a Bluetooth Hands-Free Profile level
connection to the paired Bluetooth device.

This request initiates the Service Level Connection establishment procedure
and completes without waiting for the connection procedure to complete.
Connection status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpConnect]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT,
        0,
        0,
        NULL);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpConnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpDisconnect()
/*++

Routine Description:

This function synchronously requests a Bluetooth Hands-Free Profile level
connection to the paired Bluetooth device.

This request initiates disconnection of the Service Level Connection and
completes without waiting for the disconnection to complete. Connection
status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpDisconnect]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT,
        0,
        0,
        NULL);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpDisconnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
void
BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion
(
    _In_  WDFREQUEST Request,
    _In_  WDFIOTARGET Target,
    _In_  PWDF_REQUEST_COMPLETION_PARAMS Params,
    _In_  WDFCONTEXT Context
)
/*++

Routine Description:

Completion callback for the Bluetooth Hands-Free Profile SCO Bypass
stream status notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion]"));

    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext    * reqCtx      = NULL;
    BthHfpDevice                          * This        = NULL;
    NTSTATUS                                ntResult    = STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);

    //
    // Get the SCO stream status.
    //
    reqCtx = GetBthHfpDeviceNotificationReqContext(Request);
    This = reqCtx->BthHfpDevice;
    ASSERT(This != NULL);

    ntStatus = Params->IoStatus.Status;
    if (!NT_SUCCESS(ntStatus))
    {
        ntResult = STATUS_INVALID_DEVICE_STATE;
    }
    else
    {
        ntResult = reqCtx->Buffer.NtStatus;
    }

    InterlockedExchange(&This->m_StreamStatusLong, (LONG)ntResult);

    //
    // Let the stop routine know we are done. Stop routine will 
    // re-init the request.
    //
    KeSetEvent(&This->m_StreamStatusEvent, IO_NO_INCREMENT, FALSE);
}

//=============================================================================
#pragma code_seg()
NTSTATUS
BthHfpDevice::EnableBthHfpStreamStatusNotification()
/*++

Routine Description:

This function registers for Bluetooth Hands-Free Profile SCO Bypass
stream status notification.

--*/
{
    DPF_ENTER(("[BthHfpDevice::EnableBthHfpStreamStatusNotification]"));

    NTSTATUS                               ntStatus = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext   * ctx      = NULL;

    ASSERT(m_nStreams > 0);

    ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq);
    ctx->Buffer.bImmediate = FALSE;

    KeClearEvent(&m_StreamStatusEvent);

    //
    // Get the Bth HFP SCO Bypass connection status.
    //
    ntStatus = SendIoCtrlAsynchronously(
        m_StreamReq,
        IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE,
        ctx->MemIn,
        ctx->MemOut,
        EvtBthHfpDeviceStreamStatusCompletion,
        this);

    if (!NT_SUCCESS(ntStatus))
    {
        KeSetEvent(&m_StreamStatusEvent, IO_NO_INCREMENT, FALSE);
        DPF(D_ERROR, ("EnableBthHfpStreamStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE) failed, 0x%x", ntStatus));
        goto Done;
    }

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::StopBthHfpStreamStatusNotification()
/*++

Routine Description:

This function stops the Bluetooth Hands-Free Profile SCO Bypass
connection status notification.
The function waits for the request to be done before returning.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::StopBthHfpStreamStatusNotification]"));

    NTSTATUS                                ntStatus    = STATUS_SUCCESS;
    BthHfpDeviceNotificationReqContext    * reqCtx      = NULL;
    WDF_REQUEST_REUSE_PARAMS                reuseParams;

    WdfRequestCancelSentRequest(m_StreamReq);
    KeWaitForSingleObject(&m_StreamStatusEvent, Executive, KernelMode, FALSE, NULL);

    reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq);
    ASSERT(reqCtx != NULL);
    UNREFERENCED_VAR(reqCtx);

    // 
    // Re-init the request for later.
    //
    WDF_REQUEST_REUSE_PARAMS_INIT(
        &reuseParams,
        WDF_REQUEST_REUSE_NO_FLAGS,
        STATUS_SUCCESS);

    ntStatus = WdfRequestReuse(m_StreamReq, &reuseParams);
    if (!NT_SUCCESS(ntStatus))
    {
        DPF(D_ERROR, ("StopBthHfpStreamStatusNotification: WdfRequestReuse failed, 0x%x", ntStatus));
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpStreamOpen()
/*++

Routine Description:

This function synchronously requests an open SCO channel to transmit audio
data over the air.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamOpen]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_STREAM_OPEN,
        0,
        0,
        NULL);

    if (ntStatus == STATUS_DEVICE_BUSY)
    {
        // The stream channel is already open.
        DPF(D_VERBOSE, ("SetBthHfpStreamOpen: the stream channel is already open"));
        ntStatus = STATUS_SUCCESS;
    }

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpStreamOpen: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_OPEN) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::SetBthHfpStreamClose()
/*++

Routine Description:

This function synchronously requests to close the SCO channel.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamClose]"));

    NTSTATUS        ntStatus = STATUS_SUCCESS;

    ntStatus = SendIoCtrlSynchronously(
        NULL,
        IOCTL_BTHHFP_STREAM_CLOSE,
        0,
        0,
        NULL);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("SetBthHfpStreamClose: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_CLOSE) failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::Start()
/*++

Routine Description:

Asynchronously called to start the audio device.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::Start]"));

    NTSTATUS                            ntStatus            = STATUS_SUCCESS;
    BOOL                                connStatus          = FALSE;
    UINT                                bthMiniportsIndex   = 0;

    // Indicate audio driver capabilities indicating support for 16KHz sampling rate
    ntStatus = SetBthHfpDeviceCapabilities(TRUE);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: SetBthHfpDeviceCapabilities: failed to set Wideband Support, 0x%x", ntStatus)),
        Done);

    //
    // Get bth hfp descriptor
    //
    ntStatus = GetBthHfpDescriptor(&m_Descriptor);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: GetBthHfpDescriptor: failed to retrieve BTHHFP_DESCRIPTOR2, 0x%x", ntStatus)),
        Done);

    //
    // Get valume settings.
    //
    if (m_Descriptor->SupportsVolume)
    {
        PKSPROPERTY_VALUES  volumePropValues    = NULL;
        LONG                volume              = 0;

        // Volume settings.
        ntStatus = GetBthHfpVolumePropertyValues(
            m_Descriptor->VolumePropertyValuesSize,
            &volumePropValues);

        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("Start: GetBthHfpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)),
            Done);

        m_VolumePropValues = volumePropValues;

        // Speaker volume.
        ntStatus = GetBthHfpSpeakerVolume(&volume);
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", ntStatus)),
            Done);

        m_SpeakerVolumeLevel = volume;

        // Mic volume.
        ntStatus = GetBthHfpMicVolume(&volume);
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", ntStatus)),
            Done);

        m_MicVolumeLevel = volume;
    }

    //
    // Get connection status.
    //
    ntStatus = GetBthHfpConnectionStatus(&connStatus);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: GetBthHfpConnectionStatus: failed, 0x%x", ntStatus)),
        Done);

    m_ConnectionStatus = connStatus;

    //
    // Get codec id (if non-zero, connection supports Wideband Speech)
    //
    UCHAR codecId = 0;
    if (connStatus)
    {
        ntStatus = GetBthHfpCodecId(&codecId);
        if (ntStatus == STATUS_INVALID_DEVICE_REQUEST)
        {
            // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't
            // support Wideband Speech (currently only Mobile supports this call)
            ntStatus = STATUS_SUCCESS;
        }
        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("Start: GetBthHfpCodecId: failed, 0x%x", ntStatus)),
            Done);
    }

    m_CodecId = codecId;

    enum
    {
        BTHHFP_CODEC_CVSD = 0,
        BTHHFP_CODEC_mSBC,

        BTHHFP_CODEC_INVALID
    };

    if (codecId > BTHHFP_CODEC_CVSD)
    {
        // Use the miniport tables that support 16kHz
        bthMiniportsIndex = 1;
    }


    //
    // Customize the topology/wave descriptors for this instance
    //
    ntStatus = CreateCustomEndpointMinipair(
        g_BthHfpRenderEndpoints[bthMiniportsIndex],
        &m_Descriptor->FriendlyName,
        &m_Descriptor->OutputPinCategory,
        &m_SpeakerMiniports);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)),
        Done);
    m_SpeakerMiniports->TopoName = m_SpeakerTopologyNameBuffer;
    m_SpeakerMiniports->WaveName = m_SpeakerWaveNameBuffer;

    ntStatus = CreateCustomEndpointMinipair(
        g_BthHfpCaptureEndpoints[bthMiniportsIndex],
        &m_Descriptor->FriendlyName,
        &m_Descriptor->InputPinCategory,
        &m_MicMiniports);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Capture: failed, 0x%x", ntStatus)),
        Done);
    m_MicMiniports->TopoName = m_MicTopologyNameBuffer;
    m_MicMiniports->WaveName = m_MicWaveNameBuffer;


    ASSERT(m_SpeakerMiniports != NULL);
    ASSERT(m_MicMiniports != NULL);
    _Analysis_assume_(m_SpeakerMiniports != NULL);
    _Analysis_assume_(m_MicMiniports != NULL);

    //
    // Register topology and wave filters.
    //
    ntStatus = m_Adapter->InstallEndpointFilters(
        NULL,
        m_SpeakerMiniports,
        PSIDEBANDDEVICECOMMON(this),
        &m_UnknownSpeakerTopology,
        &m_UnknownSpeakerWave, NULL, NULL
    );

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: InstallEndpointRenderFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)),
        Done);

    ntStatus = m_Adapter->InstallEndpointFilters(
        NULL,
        m_MicMiniports,
        PSIDEBANDDEVICECOMMON(this),
        &m_UnknownMicTopology,
        &m_UnknownMicWave, NULL, NULL
    );

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: InstallEndpointCaptureFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)),
        Done);

    ntStatus = m_Adapter->NotifyEndpointPair(
        m_SpeakerTopologyNameBuffer,
        SIZEOF_ARRAY(m_SpeakerTopologyNameBuffer),
        KSPIN_TOPO_LINEOUT_DEST,
        m_MicTopologyNameBuffer,
        SIZEOF_ARRAY(m_MicTopologyNameBuffer),
        KSPIN_TOPO_MIC_ELEMENTS
    );

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: NotifyEndpointPair (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)),
        Done);

    //
    // Pend status notifications.
    //

    // NREC disable AudioGateway (AG) status.
    ntStatus = EnableBthHfpNrecDisableStatusNotification();
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: EnableBthHfpNrecDisableStatusNotification: failed, 0x%x", ntStatus)),
        Done);

    // Volume speaker status.
    ntStatus = EnableBthHfpSpeakerVolumeStatusNotification();
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: EnableBthHfpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)),
        Done);

    // Volume mic status.
    ntStatus = EnableBthHfpMicVolumeStatusNotification();
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: EnableBthHfpMicVolumeStatusNotification: failed, 0x%x", ntStatus)),
        Done);

    // Connection status.
    ntStatus = EnableBthHfpConnectionStatusNotification();
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Start: EnableBthHfpConnectionStatusNotification: failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:
    if (!NT_SUCCESS(ntStatus))
    {
        InterlockedExchange((PLONG)&m_State, eBthHfpStateFailed);
    }
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::CreateCustomEndpointMinipair
(
    _In_        PENDPOINT_MINIPAIR pBaseMinipair,
    _In_        PUNICODE_STRING FriendlyName,
    _In_        PGUID pCategory,
    _Outptr_    PENDPOINT_MINIPAIR *ppCustomMinipair
)
{
    NTSTATUS ntStatus;
    PENDPOINT_MINIPAIR  pNewMinipair = NULL;
    SYSVAD_DEVPROPERTY* pProperties = NULL;
    PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL;
    PPCPIN_DESCRIPTOR   pNewTopoPins = NULL;
    ULONG cProperties;
    ULONG cTopoPins;

    PAGED_CODE();

    //
    // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties
    // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the hfp device
    //
    cTopoPins = pBaseMinipair->TopoDescriptor->PinCount;
    cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1;
    pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG);
    pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG);
    pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG);
    pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePool2(POOL_FLAG_NON_PAGED, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG);

    if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL))
    {
        SYSVAD_DEVPROPERTY *pLastProperty;

        // Copy base minipair properties to new property list
        if (pBaseMinipair->TopoInterfacePropertyCount > 0)
        {
            RtlCopyMemory(pProperties, pBaseMinipair->TopoInterfaceProperties, (cProperties - 1) * sizeof(SYSVAD_DEVPROPERTY));
        }

        // Add friendly name property to the list
        NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength);  // Assuming NULL terminated string
        pLastProperty = &pProperties[cProperties - 1];
        pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName;
        pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT;
        pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL);
        pLastProperty->Buffer = FriendlyName->Buffer;

        // Copy base minipair structure
        RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR));

        RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR));
        RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR));

        pNewTopoFilterDesc->Pins = pNewTopoPins;
        pNewMinipair->TopoDescriptor = pNewTopoFilterDesc;

        // Update it to point to new property list
        pNewMinipair->TopoInterfacePropertyCount = cProperties;
        pNewMinipair->TopoInterfaceProperties = pProperties;

        ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory);
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_ERROR, ("UpdateCustomEndpointCategory: failed, 0x%x", ntStatus));
        }
        else
        {
            *ppCustomMinipair = pNewMinipair;

            pProperties = NULL;
            pNewMinipair = NULL;
            pNewTopoFilterDesc = NULL;
            pNewTopoPins = NULL;

            ntStatus = STATUS_SUCCESS;
        }
    }
    else
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (pProperties != NULL)
    {
        ExFreePoolWithTag(pProperties, SYSVAD_POOLTAG);
    }
    if (pNewMinipair != NULL)
    {
        ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG);
    }
    if (pNewTopoFilterDesc != NULL)
    {
        ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG);
    }
    if (pNewTopoPins != NULL)
    {
        ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG);
    }

    return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
BthHfpDevice::UpdateCustomEndpointCategory
(
    _In_        PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter,
    _In_        PPCPIN_DESCRIPTOR pCustomMinipairTopoPins,
    _In_        PGUID pCategory
)
{
    NTSTATUS ntStatus = STATUS_NOT_FOUND;
    ULONG cPinCount = 0;
    BOOL FoundCategoryAudio = FALSE;
    BOOL FoundNodeType = FALSE;

    PAGED_CODE();

    cPinCount = pCustomMinipairTopoFilter->PinCount;

    // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO,
    // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category.
    for (ULONG i = 0; i < cPinCount; ++i)
    {
        if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO))
        {
            ASSERT(FoundCategoryAudio == FALSE);
            if (FoundCategoryAudio)
            {
                ntStatus = STATUS_INVALID_DEVICE_STATE;
                DPF(D_ERROR, ("UpdateCustomEndpointCategory: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus));
                break;
            }

            FoundCategoryAudio = TRUE;
            continue;
        }

        ASSERT(FoundNodeType == FALSE);
        if (FoundNodeType)
        {
            ntStatus = STATUS_INVALID_DEVICE_STATE;
            DPF(D_ERROR, ("UpdateCustomEndpointCategory: Found more than one applicable Pin, 0x%x", ntStatus));
            break;
        }

        pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory;
        FoundNodeType = TRUE;
        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::DeleteCustomEndpointMinipair
(
    _In_        PENDPOINT_MINIPAIR pCustomMinipair
)
{
    PAGED_CODE();

    if (pCustomMinipair != NULL)
    {
        if (pCustomMinipair->TopoInterfaceProperties != NULL)
        {
            ExFreePoolWithTag(const_cast<SYSVAD_DEVPROPERTY*>(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG);
            pCustomMinipair->TopoInterfaceProperties = NULL;
        }
        if (pCustomMinipair->TopoDescriptor != NULL)
        {
            if (pCustomMinipair->TopoDescriptor->Pins != NULL)
            {
                ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG);
                pCustomMinipair->TopoDescriptor->Pins = NULL;
            }
            ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG);
            pCustomMinipair->TopoDescriptor = NULL;
        }
        ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG);
    }
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
BthHfpDevice::Stop()
/*++

Routine Description:

Asynchronously called to stop the audio device.
After returning from this function, there are no more async notifications
pending (volume, connection, etc.).

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::Stop]"));

    NTSTATUS        ntStatus    = STATUS_SUCCESS;
    eBthHfpState    state       = eBthHfpStateInvalid;

    state = (eBthHfpState)InterlockedExchange((PLONG)&m_State, eBthHfpStateStopping);
    ASSERT(state == eBthHfpStateRunning || state == eBthHfpStateFailed);
    UNREFERENCED_VAR(state);

    //
    // Stop async notifications.
    //
    WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait);

    //
    // Wait for work-item.
    //
    WdfWorkItemFlush(m_WorkItem);

    //
    // Remove the topology and wave render filters.
    //
    if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave)
    {
        ntStatus = m_Adapter->RemoveEndpointFilters(
            m_SpeakerMiniports,
            m_UnknownSpeakerTopology,
            m_UnknownSpeakerWave);

        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Speaker): failed, 0x%x", ntStatus));
        }
    }

    //
    // Remove the topology and wave capture filters.
    //
    if (m_UnknownMicTopology || m_UnknownMicWave)
    {
        ntStatus = m_Adapter->RemoveEndpointFilters(
            m_MicMiniports,
            m_UnknownMicTopology,
            m_UnknownMicWave);

        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Capture): failed, 0x%x", ntStatus));
        }
    }

    //
    // Release port/miniport pointers.
    //
    SAFE_RELEASE(m_UnknownSpeakerTopology);
    SAFE_RELEASE(m_UnknownSpeakerWave);
    SAFE_RELEASE(m_UnknownMicTopology);
    SAFE_RELEASE(m_UnknownMicWave);

    //
    // The device is in the stopped state.
    //
    InterlockedExchange((PLONG)&m_State, eBthHfpStateStopped);

}

#pragma code_seg("PAGE")
NTSTATUS BthHfpDevice::CreateFilterNames(
    PUNICODE_STRING HfpDeviceSymbolicLinkName
)
/*++

Routine Description:

Creates unique wave and topology filter reference names.

Since each subdevice representing a HFP sideband interface arrival needs to have a unique
reference string, this function generates a hash on 'HfpDeviceSymbolicLinkName' parameter
and appends to static interface strings.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[BthHfpDevice::CreateAudioInterfaces]"));

    NTSTATUS status = STATUS_SUCCESS;
    ULONG hashValue = 0;

    status = RtlHashUnicodeString(HfpDeviceSymbolicLinkName, TRUE, HASH_STRING_ALGORITHM_X65599, &hashValue);
    IF_FAILED_ACTION_JUMP(
        status,
        DPF(D_ERROR, ("Failed to create KSCATEGORY_REALTIME wave interface: 0x%x", status)),
        End);

    // create wave interfaces for speaker
    RtlStringCbPrintfW(m_SpeakerWaveNameBuffer, sizeof(m_SpeakerWaveNameBuffer), L"%s-%lu", BTHHFP_SPEAKER_WAVE_NAME, hashValue);
    RtlUnicodeStringInit(&m_SpeakerWaveRefString, m_SpeakerWaveNameBuffer);

    // create topology interfaces for speaker
    RtlStringCbPrintfW(m_SpeakerTopologyNameBuffer, sizeof(m_SpeakerTopologyNameBuffer), L"%s-%lu", BTHHFP_SPEAKER_TOPO_NAME, hashValue);
    RtlUnicodeStringInit(&m_SpeakerTopologyRefString, m_SpeakerTopologyNameBuffer);

    // create wave interfaces for mic
    RtlStringCbPrintfW(m_MicWaveNameBuffer, sizeof(m_MicWaveNameBuffer), L"%s-%lu", BTHHFP_MIC_WAVE_NAME, hashValue);
    RtlUnicodeStringInit(&m_MicWaveRefString, m_MicWaveNameBuffer);

    // create topology interfaces for mic
    RtlStringCbPrintfW(m_MicTopologyNameBuffer, sizeof(m_MicTopologyNameBuffer), L"%s-%lu", BTHHFP_MIC_TOPO_NAME, hashValue);
    RtlUnicodeStringInit(&m_MicTopologyRefString, m_MicTopologyNameBuffer);
End:
    return status;
}

#endif // SYSVAD_BTH_BYPASS


<|start_filename|>network/wlan/WDI/PLATFORM/NDIS6/N63C_Oids.c<|end_filename|>
#include "Mp_Precomp.h"
#include "P2P_Internal.h"

VOID
N63CIndicateReceivedProvisionDiscoveryResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
	);

VOID
N63CIndicateReceivedGONegotiationResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
	);

VOID
N63CIndicateReceivedGONegotiationConfirm(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
	);

VOID
N63CIndicateReceivedInvitationResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
	);

static u1Byte InfoBuf[1024];
static ULONG InfoBufLen;

//======================================================================================
// Local function for OffChnlTx
//======================================================================================

static
BOOLEAN
n63c_SendP2pActionFrameComplete(
	IN  PADAPTER				pAdapter,
	IN  const N63C_SEND_ACTION_CTX *ctx
	)
{
	P2P_INFO				*info = GET_P2P_INFO(pAdapter);
	OFF_CHNL_TX_STATUS_FLAG txStatusFlag;
	NDIS_STATUS				status = NDIS_STATUS_SUCCESS;
	FRAME_BUF				*buf = OffChnlTx_GetTxFrameBuf(ctx->req);
	
	FunctionIn(COMP_OID_SET);
	
	txStatusFlag = OffChnlTx_GetTxStatusFlag(ctx->req);

	status = (TEST_FLAG(txStatusFlag.perTxAttemptFlag, OFF_CHNL_TX_PER_TX_ATTEMPT_FLAG_FRAME_SENT) ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE);

	RT_ASSERT(buf, ("%s(): buf is NULL!!!n", __FUNCTION__));

	RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("indicate frame sent with token: %u\n", ctx->token));
		
	// Change dialog token to make sure the token is the same as the previous query.
	p2p_IndicateActionFrameSendCompleteWithToken(info, 
		ctx->compEvent, 
		(NDIS_STATUS_SUCCESS == status) ? RT_STATUS_SUCCESS : RT_STATUS_FAILURE, 
		FrameBuf_MHead(buf), 
		FrameBuf_Length(buf),
		ctx->token);

	FunctionOut(COMP_OID_SET);

	return TRUE;
}

static
BOOLEAN
n63c_Construct_FakeInvitationRsp(
	IN  ADAPTER						*pAdapter,
	IN  const N63C_SEND_ACTION_CTX 	*ctx,
	IN  const P2P_DEV_LIST_ENTRY 	*dev,
	OUT FRAME_BUF					*fbuf
	)
{
	P2P_INFO				*info = GET_P2P_INFO(pAdapter);
	OCTET_STRING			osWFDIE, osProbeRspPkt;

	FunctionIn(COMP_OID_SET);
	
	// MAC Header
	p2p_add_ActionFrameMacHdr(fbuf, info->DeviceAddress, dev->mac, info->DeviceAddress);

	// Action Header
	p2p_add_P2PPublicActionHdr(fbuf, P2P_INVITATION_RSP, ctx->token);

	// P2P IE
	p2p_build_FakeInvitationRspIe(fbuf, info, dev->mac);

	// WFD IE, from probe rsp
	if(dev->rxFrames[P2P_FID_PROBE_RSP] && dev->rxFrames[P2P_FID_PROBE_RSP]->frameLen)
	{
		FillOctetString(osProbeRspPkt, 
			dev->rxFrames[P2P_FID_PROBE_RSP]->frame, 
			dev->rxFrames[P2P_FID_PROBE_RSP]->frameLen
			);
		
		osWFDIE = PacketGetElement(osProbeRspPkt, EID_Vendor, OUI_SUB_WIFI_DISPLAY, OUI_SUB_DONT_CARE);

		if(osWFDIE.Length > 0)
		{
			PacketMakeElement(&fbuf->os, EID_Vendor, osWFDIE);
		}
	}

	RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("a fake invitation rsp constructed\n"));
	FrameBuf_Dump(fbuf, 0, DBG_LOUD, __FUNCTION__);

	N6CWriteEventLogEntry(pAdapter, NDIS_STATUS_SUCCESS, 
		FrameBuf_Length(fbuf), FrameBuf_MHead(fbuf), 
		L"A fake invitation rsp constructed"
		);

	FunctionOut(COMP_OID_SET);
	
	return TRUE;
}

static
BOOLEAN
n63c_Construct_FakePdRsp(
	IN  ADAPTER						*pAdapter,
	IN  const N63C_SEND_ACTION_CTX 	*ctx,
	IN  const P2P_DEV_LIST_ENTRY 	*dev,
	OUT FRAME_BUF					*fbuf
	)
{
	P2P_INFO				*info = GET_P2P_INFO(pAdapter);
	OCTET_STRING			osPdReq, osTmpIe;
	
	FunctionIn(COMP_OID_SET);

	if(dev->txFrames[P2P_FID_PD_REQ] && dev->txFrames[P2P_FID_PD_REQ]->frameLen)
	{
		FillOctetString(osPdReq, 
			dev->txFrames[P2P_FID_PD_REQ]->frame, 
			dev->txFrames[P2P_FID_PD_REQ]->frameLen
			);
	}
	else
	{
		RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("no corresp. pd req\n"));
		FillOctetString(osPdReq, NULL, 0);
	}
	
	// MAC Header
	p2p_add_ActionFrameMacHdr(fbuf, info->DeviceAddress, dev->mac, dev->mac);

	// Action Header
	p2p_add_P2PPublicActionHdr(fbuf, P2P_PROV_DISC_RSP, ctx->token);

	// WPS IE, getting from the original PD request
	if(osPdReq.Length)
	{	
		osTmpIe = PacketGetElement(osPdReq, EID_Vendor, OUI_SUB_SimpleConfig, OUI_SUB_DONT_CARE);
		if(osTmpIe.Length)
		{
			PacketMakeElement(&fbuf->os, EID_Vendor, osTmpIe);
		}
	}
	
	// WFD IE
	if(osPdReq.Length)
	{
		osTmpIe = PacketGetElement(osPdReq, EID_Vendor, OUI_SUB_WIFI_DISPLAY, OUI_SUB_DONT_CARE);
		if(osTmpIe.Length)
		{
			PacketMakeElement(&fbuf->os, EID_Vendor, osTmpIe);
		}			
	}
	
	RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("a fake pd rsp constructed\n"));
	FrameBuf_Dump(fbuf, 0, DBG_LOUD, __FUNCTION__);

	N6CWriteEventLogEntry(pAdapter, NDIS_STATUS_SUCCESS, 
		FrameBuf_Length(fbuf), FrameBuf_MHead(fbuf), 
		L"A fake pd rsp constructed"
		);


	FunctionOut(COMP_OID_SET);

	return TRUE;
}

static
BOOLEAN
n63c_IndicateRxP2pRspActionFrame(
	IN  ADAPTER					*pAdapter,
	IN  const N63C_SEND_ACTION_CTX *ctx
	)
{
	typedef VOID (*N63C_INDIC_RX_P2P_RSP_ACTION_FRAME_PFN)(P2P_INFO *, const N63C_SEND_ACTION_CTX *, u4Byte, u1Byte *);
	
	P2P_INFO					*info = GET_P2P_INFO(pAdapter);
	N63C_INDIC_RX_P2P_RSP_ACTION_FRAME_PFN pfn = NULL;					
	u4Byte						eid = P2P_EVENT_NONE;
	P2P_FRAME_TYPE				ft = P2P_FID_MAX;

	RT_GEN_TEMP_BUFFER			*pGenBuf = NULL;
	FRAME_BUF					fbuf;
	
	P2P_DEV_LIST_ENTRY			*dev = NULL;
	u4Byte						ieOffset = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;

	BOOLEAN						bIndicate = FALSE;

	p2p_DevList_Lock(&info->devList);

	do
	{
		if(NULL == (pGenBuf = GetGenTempBuffer(pAdapter, GEN_TEMP_BUFFER_SIZE)))
		{
			RT_TRACE_F(COMP_OID_SET, DBG_SERIOUS, ("[ERROR] Memory allocation failed!\n"));
			break;
		}

		FrameBuf_Init(GEN_TEMP_BUFFER_SIZE, 0, (u1Byte *)pGenBuf->Buffer.Ptr, &fbuf);
		
		// get corresp. types
		if(N63C_P2P_ACTION_FRAME_PROVISION_DISCOVERY_RESPONSE == ctx->rspType)
		{
			pfn = N63CIndicateReceivedProvisionDiscoveryResponse;
			eid = P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_RESPONSE;
			ft = P2P_FID_PD_RSP;
		}
		else if(N63C_P2P_ACTION_FRAME_GO_NEGOTIATION_RESPONSE == ctx->rspType)
		{
			pfn = N63CIndicateReceivedGONegotiationResponse;
			eid = P2P_EVENT_RECEIVED_GO_NEGOTIATION_RESPONSE;
			ft = P2P_FID_GO_NEG_RSP;
		}
		else if(N63C_P2P_ACTION_FRAME_GO_NEGOTIATION_CONFIRM == ctx->rspType)
		{
			pfn = N63CIndicateReceivedGONegotiationConfirm;
			eid = P2P_EVENT_RECEIVED_GO_NEGOTIATION_CONFIRM;
			ft = P2P_FID_GO_NEG_CONF;
		}
		else if(N63C_P2P_ACTION_FRAME_INVITATION_RESPONSE == ctx->rspType)
		{
			pfn = N63CIndicateReceivedInvitationResponse;
			eid = P2P_EVENT_RECEIVED_INVITATION_RESPONSE;
			ft = P2P_FID_INV_RSP;
		}
		else
		{
			RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("invalid response type: %u\n", ctx->rspType));
			break;
		}

		// get dev
		if(NULL == (dev = p2p_DevList_Get(&info->devList, ctx->da, ctx->bGo ? P2P_DEV_TYPE_GO : P2P_DEV_TYPE_DEV)))
		{
			RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("dev not found\n"));
			break;
		}

		if(dev->rxFrames[ft])
		{
			if(dev->rxFrames[ft]->frameLen < ieOffset + 1)
			{
				RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("invalid received rsp action frame len: %u\n", dev->rxFrames[ft]->frameLen));
				break;
			}

			FrameBuf_Add_Data(&fbuf, dev->rxFrames[ft]->frame, dev->rxFrames[ft]->frameLen);
		}
		else
		{
			RT_TRACE_F(COMP_OID_SET, DBG_WARNING, ("no coresp. received rsp action frame, rsp type: %u\n", ft));

			if(N63C_P2P_ACTION_FRAME_INVITATION_RESPONSE == ctx->rspType)
			{
				if(!n63c_Construct_FakeInvitationRsp(pAdapter, ctx, dev, &fbuf))
					break;
			}
			else if(N63C_P2P_ACTION_FRAME_PROVISION_DISCOVERY_RESPONSE == ctx->rspType)
			{
				if(!n63c_Construct_FakePdRsp(pAdapter, ctx, dev, &fbuf))
					break;
			}
			else
			{
				break;
			}		
		}

		bIndicate = TRUE;
	}while(FALSE);

	p2p_DevList_Unlock(&info->devList);

	if(bIndicate)
	{// to indicate w/o lock acquired
		RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("to indicate rx action frame with token: %u\n", ctx->token));
		(pfn)(info, ctx, FrameBuf_Length(&fbuf), FrameBuf_MHead(&fbuf));
	}

	if(pGenBuf)
	{
		ReturnGenTempBuffer(pAdapter, pGenBuf);
	}

	FunctionOut(COMP_OID_SET);

	return bIndicate;
}

static
BOOLEAN
n63c_SendP2pActionFrameStateCb(
	IN	VOID					*req,
	IN	int 					state,
	IN	VOID					*pCtx
	)
{
	BOOLEAN						bHandled = FALSE;

	N63C_SEND_ACTION_CTX		*ctx = (N63C_SEND_ACTION_CTX *)pCtx;
	ADAPTER						*pAdapter = ctx->pAdapter;
	P2P_INFO					*info = GET_P2P_INFO(pAdapter);

	do
	{
		if(OFF_CHNL_TX_STATE_CHNL_HIT == state)
		{
			RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("on chnl\n"));		
			bHandled = TRUE;
			break;
		}

		if(OFF_CHNL_TX_STATE_COMPLETE == state)
		{
			RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("complete\n"));	
			bHandled = TRUE;
			break;
		}
		
		if(OFF_CHNL_TX_STATE_FRAME_SENT == state)
		{
			RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("frame sent\n"));
			bHandled = TRUE;
			break;
		}
	}while(FALSE);

	return bHandled;
}

static
VOID
n63c_SendP2pRspActionFrameStateCb(
	IN  VOID					*req,
	IN  int						state,
	IN  VOID					*pCtx
	)
{
	N63C_SEND_ACTION_CTX		*ctx = (N63C_SEND_ACTION_CTX *)pCtx;
	ADAPTER						*pAdapter = ctx->pAdapter;
	P2P_INFO					*info = GET_P2P_INFO(pAdapter);
	
	n63c_SendP2pActionFrameStateCb(req, state, pCtx);
	
	if(OFF_CHNL_TX_STATE_COMPLETE == state)
	{
		n63c_SendP2pActionFrameComplete(pAdapter, ctx);

		// restore Dialog Token in P2PInfo since we are sending 
		// response action frame, self dialog token shall not be 
		// modified
		RT_TRACE_F(COMP_P2P, DBG_LOUD, ("restore token from %u to %u\n", info->DialogToken, ctx->oldToken));
		info->DialogToken = ctx->oldToken;
	}
	else if(OFF_CHNL_TX_STATE_PRE_DESTROY == state)
	{
		if(N63C_P2P_ACTION_FRAME_GO_NEGOTIATION_CONFIRM == ctx->rspType)
		{
			n63c_IndicateRxP2pRspActionFrame(pAdapter, ctx);
		}

	}
	else if(OFF_CHNL_TX_STATE_RETRY == state)
	{
		RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("retry with token: %u, reqType: %u\n", ctx->token, ctx->reqType));
	}
	
	return;
}

static
VOID
n63c_SendP2pReqActionFrameStateCb(
	IN  VOID					*req,
	IN  int						state,
	IN  VOID					*pCtx
	)
{
	N63C_SEND_ACTION_CTX		*ctx = (N63C_SEND_ACTION_CTX *)pCtx;
	ADAPTER						*pAdapter = ctx->pAdapter;
	
	n63c_SendP2pActionFrameStateCb(req, state, pCtx);
	
	if(OFF_CHNL_TX_STATE_COMPLETE == state)
	{
		P2P_INFO				*info = GET_P2P_INFO(pAdapter);
		
		n63c_SendP2pActionFrameComplete(pAdapter, ctx);

		RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("restore StateBeforeScan to initialized\n"));
		info->StateBeforeScan = P2P_STATE_INITIALIZED;
	}
	else if(OFF_CHNL_TX_STATE_PRE_DESTROY == state)
	{
		if(N63C_P2P_ACTION_FRAME_PROVISION_DISCOVERY_RESPONSE == ctx->rspType
			|| N63C_P2P_ACTION_FRAME_INVITATION_RESPONSE == ctx->rspType
			)
		{
			n63c_IndicateRxP2pRspActionFrame(pAdapter, ctx);
		}


		if(N63C_P2P_ACTION_FRAME_GO_NEGOTIATION_RESPONSE == ctx->rspType)
		{
			InfoBufLen = 0;
			n63c_IndicateRxP2pRspActionFrame(pAdapter, ctx);
			if(InfoBufLen)
			{
				if(NULL == N63C_SendAction_GoNegConfirm(pAdapter, (DOT11_SEND_GO_NEGOTIATION_CONFIRMATION_PARAMETERS *)InfoBuf, n63c_SendP2pRspActionFrameStateCb))
				{
					RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("N63C_SendAction_GoNegConfirm failed\n"));
					p2p_IndicateActionFrameSendCompleteWithToken(GET_P2P_INFO(pAdapter), P2P_EVENT_GO_NEGOTIATION_CONFIRM_SEND_COMPLETE, RT_STATUS_FAILURE, NULL, 0, 0);
				}
				InfoBufLen = 0;
			}
		}
	}
	else if(OFF_CHNL_TX_STATE_RETRY == state)
	{
		u1Byte					newToken = 0;
		static BOOLEAN			bSkip = TRUE;

		newToken = N63C_SendAction_UpdateTxFrameDialogToken(req, pAdapter, ctx->reqType);

		if(pAdapter->bInHctTest)
		{
			// Not to skip probing under HCT test, because it wastes time 
			// sending action frame to peer on its Listen Channel when peer
			// is not doing extended listen
			bSkip = FALSE;
		}
		
		if(bSkip)
		{
			N63C_SendAction_SkipProbeTemporarilly(req);
		}
		
		RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("retry with token: %u, reqType: %u, skip probe: %u\n", newToken, ctx->reqType, bSkip));

		bSkip = !bSkip;
	}
	
	return;
}

//======================================================================================
// Put some temp codes for Windows 8
//======================================================================================

VOID
N63CValidateRoleOfWifiDirectPorts(
	PADAPTER pAdapter
)
{
	PADAPTER pTargetAdapter = GetDefaultAdapter(pAdapter);
	u1Byte uNumberOfDevicePort = 0;
	u1Byte uNumberOfGoPort = 0;
	u1Byte uNumberOfClientPort = 0;

	while(pTargetAdapter)
	{
		if(P2P_ENABLED(GET_P2P_INFO(pTargetAdapter)))
		{
			switch(GET_P2P_INFO(pTargetAdapter)->Role)
			{

				case P2P_DEVICE: uNumberOfDevicePort++;	break;
				case P2P_CLIENT: uNumberOfClientPort++;	break;
				case P2P_GO: 	uNumberOfGoPort++;		break;
				default:
					break;
			}
		}


		pTargetAdapter = GetNextExtAdapter(pTargetAdapter);
	}

	if(uNumberOfDevicePort > 2)	
	{
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET | COMP_P2P), DBG_SERIOUS, 
				("Wifi-Direct Port Failure: %s: uNumberOfDevicePort > 2\n", __FUNCTION__)
			);
	}

	if(uNumberOfGoPort > 2)	
	{
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET | COMP_P2P), DBG_SERIOUS, 
				("Wifi-Direct Port Failure: %s: uNumberOfGoPort > 2)\n", __FUNCTION__)
			);
	}
	
	if(uNumberOfClientPort > 1)	
	{
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET | COMP_P2P), DBG_SERIOUS, 
				("Wifi-Direct Port Failure: %s: uNumberOfClientPort > 1\n", __FUNCTION__)
			);
	}
}

VOID
N63CResetNetworkListOffload(
	PADAPTER pAdapter
)
{
	PRT_NLO_INFO		pNLOInfo = &(pAdapter->MgntInfo.NLOInfo);

	RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("--- Reset Network List Offload --- \n"));

	pNLOInfo->NumDot11OffloadNetwork = 0;
	pNLOInfo->FastScanIterations = 0;
	pNLOInfo->FastScanPeriod = 0;
	pNLOInfo->SlowScanPeriod = 0;
}


NDIS_STATUS
N63CResetWifiDirectPorts(
	PADAPTER pAdapter,
	P2P_ROLE PortRole,
	RESET_LEVEL ResetLevel
)
{
	PRT_NDIS62_COMMON pNdis62Common = pAdapter->pNdis62Common;
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PP2P_INFO pP2PInfo = (PP2P_INFO)(pAdapter->MgntInfo.pP2PInfo);
	PP2P_INFO pP2PDevInfo = NULL;
	u1Byte uListenChannel = 0;
	u1Byte uOpChannel = 0;
	u4Byte	BoostInitGainValue = 0;
	u1Byte	uAutoChnl = P2P_DEFAULT_OPERATING_CHANNEL;
	
	PADAPTER pDevicePort = NULL;

	FunctionIn(COMP_P2P);

#if (MULTICHANNEL_SUPPORT == 1)
	MultichannelHandlePacketDuringScan(pAdapter,FALSE);
#endif

	// Set the default OpChannel and ListenChannel ----------------------------------------------------------------------------
	uListenChannel = P2P_DEFAULT_LISTEN_CHANNEL;
	uOpChannel = uListenChannel;


	if(pDefaultAdapter->MgntInfo.WFDOpChannel !=0)
		uOpChannel = pDefaultAdapter->MgntInfo.WFDOpChannel;
	else if(MgntLinkStatusQuery(pDefaultAdapter) == RT_MEDIA_CONNECT)
	{
		uOpChannel = RT_GetChannelNumber(pDefaultAdapter);
		RT_TRACE(COMP_P2P, DBG_LOUD, ("--- Use Default Port Channel: OpChannel %d ---\n", uOpChannel));
	}
	else if(GetFirstGOPort(pAdapter))
	{
		pP2PDevInfo = GET_P2P_INFO(GetFirstGOPort(pAdapter));
		uOpChannel = pP2PDevInfo->OperatingChannel;
	}
	else if(GetFirstClientPort(pAdapter))
	{
		pP2PDevInfo = GET_P2P_INFO(GetFirstClientPort(pAdapter));
		uOpChannel = pP2PDevInfo->OperatingChannel;
	}
	else if(GetFirstDevicePort(pAdapter))
	{
		pP2PDevInfo = GET_P2P_INFO(GetFirstDevicePort(pAdapter));
		uOpChannel = pP2PDevInfo->OperatingChannel;
	}
	else if(PortRole == P2P_DEVICE)
	{
#if (AUTO_CHNL_SEL_NHM ==1)		
		if(IS_AUTO_CHNL_SUPPORT(pAdapter))
		{
			pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_AUTO_CHNL_SEL, (pu1Byte)(&uAutoChnl));
			uOpChannel = P2PIsSocialChannel(uAutoChnl)? uAutoChnl : P2P_DEFAULT_OPERATING_CHANNEL;			
			pDefaultAdapter->MgntInfo.AutoChnlSel.AutoChnlNumberSelected= uAutoChnl;		
			RT_TRACE(COMP_P2P, DBG_LOUD, ("===> [ACS] N63CResetWifiDirectPorts(): uOpChannel(%d)\n", uOpChannel));
		}		
#endif		
	}	

	if(P2PIsSocialChannel(uOpChannel))
	{		
		uListenChannel = uOpChannel;
		if(GetFirstDevicePort(pAdapter))
		{
			GET_P2P_INFO(GetFirstDevicePort(pAdapter))->ListenChannel = uListenChannel;
		}
	}

	RT_TRACE_F(COMP_P2P, DBG_LOUD, ("Set from Role=%d to Role=%d, listenChnl = %d, OpChnl = %d\n", pP2PInfo->Role, PortRole, uListenChannel, uOpChannel));
		
	if(PortRole == P2P_DEVICE)
	{	
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("(Win8 Reset to WFD Device Port)\n"));

		// Win8: Let the device port use the locally-adminitered MAC address ----------------------------------------------
		cpMacAddr(pAdapter->CurrentAddress, pAdapter->PermanentAddress);
		pAdapter->CurrentAddress[0] |= BIT1;
		RT_PRINT_ADDR((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, "Device Port Address: ", pAdapter->CurrentAddress);
		// -------------------------------------------------------------------------------------------------------
			
		pNdis62Common->CurrentOpState = INIT_STATE;

		if(ResetLevel == RESET_LEVEL_FULL)
		{
			MgntActSet_ApType(pAdapter, FALSE);
			N62CResetAPVariables(pAdapter, FALSE);
		}
		
		// Stop the Device Port mode: Last 3 parameters are don't care
		MgntActSet_P2PMode(pAdapter, FALSE, FALSE, 0, 0, 0);
		// Start the Device Port mode
		MgntActSet_P2PMode(pAdapter, TRUE, FALSE, uListenChannel, uOpChannel, P2P_DEFAULT_GO_INTENT);
		
		// Win8: Let the device port do not conduct the extended listening.
		//	+ When the OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY is issued, start listening.
		pP2PInfo->ExtListenTimingPeriod = 0;
		pP2PInfo->ExtListenTimingDuration = 0;
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("In Win8, the device port and the extended listening are not started !\n"));

	}
	else if(PortRole == P2P_GO || PortRole == P2P_CLIENT)
	{
		pDevicePort = GetFirstDevicePort(pAdapter);
		if(pDevicePort == NULL)
		{
			RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: pDevicePort == NULL \n", __FUNCTION__));
			return NDIS_STATUS_FAILURE;
		}


		// Wifi Direct Role Port is Open --------------------------------------------
		//DM_DIG_WifiDirectBoostStart(GetDefaultAdapter(pAdapter));
		// --------------------------------------------------------------------

		if(PortRole == P2P_GO)
		{
			pP2PDevInfo = (PP2P_INFO)(pDevicePort->MgntInfo.pP2PInfo);
			
			if(pP2PDevInfo != NULL)				
			{
				RT_TRACE((COMP_OID_QUERY | COMP_OID_SET|COMP_P2P), DBG_LOUD, ("(Win8 Reset to WFD Group Owner Port): Device port State(%d)\n", pP2PDevInfo->State));

				if((pP2PDevInfo->State == P2P_STATE_INVITATION_REQ_SEND) &&
					(pP2PDevInfo->State != P2P_STATE_INVITATION_RSP_WAIT))
				{
					if(P2P_ADAPTER_OS_SUPPORT_P2P(pAdapter))
					{
						//<Roger_TODO>: Should we need to resume DIG here?
					}
				}
			}


			// Reset the AP parameters 
			pNdis62Common->CurrentOpState = INIT_STATE;

			if(ResetLevel == RESET_LEVEL_FULL)
			{
				N62CResetAPVariables(pAdapter, TRUE);
			}
			{
					HAL_AP_IBSS_INT_MODE	intMode = HAL_AP_IBSS_INT_DISABLE;
					pAdapter->HalFunc.SetHalDefVarHandler(pAdapter, HAL_DEF_AP_IBSS_INTERRUPT, (pu1Byte)&intMode);
			}
			// Restart the Role Port mode: Last 3 parameters are don't care
			MgntActSet_P2PMode(pAdapter, FALSE, FALSE, 0, 0, 0);
			MgntActSet_P2PMode(pAdapter, TRUE, FALSE, uListenChannel, uOpChannel, P2P_DEFAULT_GO_INTENT);	

			pP2PInfo->Role = P2P_GO;
			pP2PInfo->State = P2P_STATE_OPERATING;
	
			// Clause 3.1.4.3: Group Formation bit in the P2P Group Cap shall be set to 1 until Provisioning succeeds. 
			pP2PInfo->GroupCapability |= gcGroupFormation;
		}
		else if(PortRole == P2P_CLIENT)
		{
			RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("(Win8 Reset to WFD Client Port)\n"));

			// Reset the STA parameters
			pNdis62Common->CurrentOpState=INIT_STATE;

			if(ResetLevel == RESET_LEVEL_FULL)
			{
				MgntActSet_ApType(pAdapter, FALSE);
				N62CResetAPVariables(pAdapter, FALSE);
			}

			// Restart the Role Port mode: Last 3 parameters are don't care
			MgntActSet_P2PMode(pAdapter, FALSE, FALSE, 0, 0, 0);
			MgntActSet_P2PMode(pAdapter, TRUE, FALSE, uListenChannel, uOpChannel, P2P_DEFAULT_GO_INTENT);
				
			pP2PInfo->Role = P2P_CLIENT;
			pP2PInfo->State = P2P_STATE_OPERATING;
		}

		// Copy the scan list from the device port
		// Prefast warning C28182: Dereferencing NULL pointer.
		if (GET_P2P_INFO(pDevicePort) != NULL)
		{
			P2PScanListCopy(
				pP2PInfo->ScanList,
				&pP2PInfo->ScanListSize,
				GET_P2P_INFO(pDevicePort)->ScanList,
				GET_P2P_INFO(pDevicePort)->ScanListSize
				);
		}
		
	}
	else
	{
		RT_TRACE_F((COMP_OID_QUERY | COMP_OID_SET | COMP_P2P), DBG_LOUD, ("[WARNING] Unknown P2P_ROLE: PortRole: %d\n", PortRole));
		return NDIS_STATUS_FAILURE;
	}

	FunctionOut(COMP_P2P);
	
	return NDIS_STATUS_SUCCESS;
}

//======================================================================================
// Private Functions
//======================================================================================

// Compute the required memory for representing the device list in the form of DOT11_WFD_DEVICE_ENTRY
static u4Byte
N63CComputeRequiredDeviceEntriesSize(
	IN PP2P_INFO pP2PInfo
)
{
	u4Byte i = 0;
	u4Byte requiredMemory = 0;
	
	// Skip the MAC header and the first three fields (non-IE) in the beacon and the probe response
	u4Byte skippedBytes = sMacHdrLng + 12;	
	
	for(i = 0; i < pP2PInfo->DeviceListForQuery.uNumberOfDevices; i++)
	{
		requiredMemory += sizeof(DOT11_WFD_DEVICE_ENTRY);	// Entry Size

		// Only consider the valid packet : Beacon
		if(pP2PInfo->DeviceListForQuery.DeviceEntry[i].BeaconPacket.Length >= skippedBytes)
		{
			requiredMemory += pP2PInfo->DeviceListForQuery.DeviceEntry[i].BeaconPacket.Length - skippedBytes;
		}

		// Only consider the valid packet : ProbeResponse
		if(pP2PInfo->DeviceListForQuery.DeviceEntry[i].ProbeResponsePacket.Length >= skippedBytes)
		{
			requiredMemory += pP2PInfo->DeviceListForQuery.DeviceEntry[i].ProbeResponsePacket.Length - skippedBytes;
		}
	}

	RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Required Memory Size %d\n", __FUNCTION__, requiredMemory));

	return requiredMemory;
}

static VOID
N63CConstruct_DOT11_WFD_DEVICE_ENTRY(
	IN PP2P_INFO pP2PInfo,
	IN	PP2P_DEVICE_LIST_ENTRY p2pEntry,
	OUT PDOT11_WFD_DEVICE_ENTRY  wfdEntry,
	OUT pu4Byte	bytesWritten
)
{
#if 0    
    typedef struct _DOT11_WFD_DEVICE_ENTRY 
    {
        ULONG uPhyId;
        DOT11_BSS_ENTRY_PHY_SPECIFIC_INFO PhySpecificInfo;
        DOT11_MAC_ADDRESS dot11BSSID;
        DOT11_BSS_TYPE dot11BSSType;
        DOT11_MAC_ADDRESS TransmitterAddress;
        LONG lRSSI;
        ULONG uLinkQuality;
        USHORT usBeaconPeriod;
        ULONGLONG ullTimestamp;
        ULONGLONG ullBeaconHostTimestamp;
        ULONGLONG ullProbeResponseHostTimestamp;
        USHORT usCapabilityInformation;
        ULONG uBeaconIEsOffset;
        ULONG uBeaconIEsLength;	// Can be 0
        ULONG uProbeResponseIEsOffset;
        ULONG uProbeResponseIEsLength;	// Can be 0
    } DOT11_WFD_DEVICE_ENTRY, *PDOT11_WFD_DEVICE_ENTRY;
#endif

	pu1Byte header = NULL;
	PADAPTER pAdapter = pP2PInfo->pAdapter;

	// Skip the MAC header and the first three fields (non-IE) in the beacon and the probe response
	u4Byte skippedBytes = sMacHdrLng + 12;

	// Select the most recent packet
	if(p2pEntry->ProbeResponseHostTimestamp > p2pEntry->BeaconHostTimestamp)
	{
		header = p2pEntry->ProbeResponsePacket.Buffer;
	}
	else
	{
		header = p2pEntry->BeaconPacket.Buffer;
	}

	if(header == NULL) 
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s() : Not a Good Header!\n", __FUNCTION__));
		return;
	}

	// Clear the structure
	PlatformZeroMemory(wfdEntry, sizeof(DOT11_WFD_DEVICE_ENTRY));

	wfdEntry->uPhyId = N6CQuery_DOT11_OPERATING_PHYID(GetDefaultAdapter(pP2PInfo->pAdapter));

	wfdEntry->PhySpecificInfo.uChCenterFrequency = MgntGetChannelFrequency(p2pEntry->ChannelNumber);

	GET_80211_HDR_ADDRESS3(header, wfdEntry->dot11BSSID);
	
	wfdEntry->dot11BSSType = dot11_BSS_type_infrastructure;

	GET_80211_HDR_ADDRESS2(header, wfdEntry->TransmitterAddress);

	wfdEntry->lRSSI = ( (p2pEntry->SignalStrength+1)>>1 ) - 95;	// Trick from N6CTranslateRtBssToDot11Bss

	wfdEntry->uLinkQuality = p2pEntry->SignalStrength;			// Trick from N6CTranslateRtBssToDot11Bss

	wfdEntry->usBeaconPeriod = GET_BEACON_PROBE_RSP_BEACON_INTERVAL(header);

	wfdEntry->ullTimestamp = ((u8Byte) GET_BEACON_PROBE_RSP_TIME_STAMP_HIGH(header) << 32) +  GET_BEACON_PROBE_RSP_TIME_STAMP_LOW(header);

	wfdEntry->usCapabilityInformation = GET_BEACON_PROBE_RSP_CAPABILITY_INFO(header);


	//
	// <Roger_Notes>
	// We have to take care of ullBeaconHostTimestamp and ullProbeResponseHostTimestamp in DOT11_WFD_DEVICE_ENTRY structure
	// for OID_DOT11_WFD_ENUM_DEVICE_LIST request. Micfosoft would check this indicated probe response/beacon timestamp from ver9477.
	// The unit in ullBeaconHostTimestamp and ullProbeResponseHostTimestamp must be 100-nanosecond, 2013.08.21.
	//
	
	// Beacon Related

	if(RUNTIME_OS_WIN_FROM_WINBLUE(pAdapter))
	wfdEntry->ullBeaconHostTimestamp = p2pEntry->BeaconHostTimestamp*10;// Convert to 100-nanosecond units
	else
		wfdEntry->ullBeaconHostTimestamp = p2pEntry->BeaconHostTimestamp;
	wfdEntry->uBeaconIEsOffset = sizeof(DOT11_WFD_DEVICE_ENTRY);

	// Only consider the valid packet 
	if(p2pEntry->BeaconPacket.Length >= skippedBytes)
	{
		wfdEntry->uBeaconIEsLength = p2pEntry->BeaconPacket.Length - skippedBytes;

		PlatformMoveMemory(
			(pu1Byte) wfdEntry + wfdEntry->uBeaconIEsOffset, 
			(pu1Byte) p2pEntry->BeaconPacket.Buffer + skippedBytes,
			wfdEntry->uBeaconIEsLength
		);
	}
	else
		wfdEntry->uBeaconIEsOffset = 0;

	// Probe Response Related
	
	if(RUNTIME_OS_WIN_FROM_WINBLUE(pAdapter))
	wfdEntry->ullProbeResponseHostTimestamp = p2pEntry->ProbeResponseHostTimestamp*10;// Convert to 100-nanosecond units
	else
		wfdEntry->ullProbeResponseHostTimestamp = p2pEntry->ProbeResponseHostTimestamp;	
	wfdEntry->uProbeResponseIEsOffset = sizeof(DOT11_WFD_DEVICE_ENTRY) + wfdEntry->uBeaconIEsLength;
      		
	// Only consider the valid packet 
	if(p2pEntry->ProbeResponsePacket.Length  >= skippedBytes)
	{
		wfdEntry->uProbeResponseIEsLength =p2pEntry->ProbeResponsePacket.Length - skippedBytes;

		PlatformMoveMemory(
			(pu1Byte) wfdEntry + wfdEntry->uProbeResponseIEsOffset, 
			(pu1Byte) p2pEntry->ProbeResponsePacket.Buffer + skippedBytes,
			wfdEntry->uProbeResponseIEsLength
		);

	}	
	else
		wfdEntry->uProbeResponseIEsOffset = 0;

	// Return the size of the total DOT11_WFD_DEVICE_ENTRY
	*bytesWritten = sizeof(DOT11_WFD_DEVICE_ENTRY) + wfdEntry->uBeaconIEsLength + wfdEntry->uProbeResponseIEsLength;
	
}


static VOID
DumpN63CConstruct_DOT11_WFD_DEVICE_ENTRY(
	PDOT11_WFD_DEVICE_ENTRY  wfdEntry,
	u4Byte length
)
{
	
	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "WFD_DEVICE_ENTRY:===========================================\n",
			wfdEntry, sizeof(DOT11_WFD_DEVICE_ENTRY)
		);

	RT_PRINT_ADDR(COMP_P2P, DBG_LOUD, "wfdEntry->dot11BSSID:", wfdEntry->dot11BSSID);
	RT_PRINT_ADDR(COMP_P2P, DBG_LOUD, "wfdEntry->TransmitterAddress:", wfdEntry->TransmitterAddress);		
	
	RT_TRACE(COMP_P2P, DBG_LOUD, ("(wfdEntry->uBeaconIEsOffset, wfdEntry->uBeaconIEsLength) = (0x%X, 0x%X)\n", wfdEntry->uBeaconIEsOffset, wfdEntry->uBeaconIEsLength));
	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "Beacon IE:-----------\n", ((pu1Byte)wfdEntry) + wfdEntry->uBeaconIEsOffset, wfdEntry->uBeaconIEsLength);

	RT_TRACE(COMP_P2P, DBG_LOUD, ("(wfdEntry->uProbeResponseIEsOffset, wfdEntry->uProbeResponseIEsLength) = (0x%X, 0x%X)\n", wfdEntry->uProbeResponseIEsOffset, wfdEntry->uProbeResponseIEsLength));
	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "ProbeResponse IE:-----------\n", ((pu1Byte)wfdEntry) + wfdEntry->uProbeResponseIEsOffset, wfdEntry->uProbeResponseIEsLength);
}
	
	
static VOID
N63CIndicateDeviceDiscoveryComplete(
	PP2P_INFO pP2PInfo
)
{

#if 0
    #define DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS_REVISION_1    1
    #define DOT11_WFD_DISCOVER_COMPLETE_MAX_LIST_SIZE   128

    typedef struct _DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        NDIS_STATUS Status;
        ULONG uNumOfEntries;
        ULONG uTotalNumOfEntries;
        ULONG uListOffset;
        ULONG uListLength;
    } DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS, * PDOT11_WFD_DISCOVER_COMPLETE_PARAMETERS;
#endif

	u4Byte i = 0;
	u4Byte bytesWritten = 0;
	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_WFD_DISCOVER_COMPLETE_PARAMETERS	pParameters = NULL;
	PDOT11_WFD_DEVICE_ENTRY  wfdEntry = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
		
	// Compute the requried memory length ------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS); 		// Parameter Structure Size
	mbObject.Length += N63CComputeRequiredDeviceEntriesSize(pP2PInfo);
	//-------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_WFD_DISCOVER_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS)
		);

	pParameters->Status = NDIS_STATUS_SUCCESS;
	pParameters->uNumOfEntries = pP2PInfo->DeviceListForQuery.uNumberOfDevices;
	pParameters->uTotalNumOfEntries = pP2PInfo->DeviceListForQuery.uNumberOfDevices;

	pParameters->uListOffset = sizeof(DOT11_WFD_DISCOVER_COMPLETE_PARAMETERS);

	bytesWritten = 0;
	wfdEntry = (PDOT11_WFD_DEVICE_ENTRY)((pu1Byte) mbObject.Buffer + pParameters->uListOffset);

	for(i = 0; i < pP2PInfo->DeviceListForQuery.uNumberOfDevices; i++)
	{
		wfdEntry = (PDOT11_WFD_DEVICE_ENTRY)((pu1Byte) wfdEntry + bytesWritten);

		N63CConstruct_DOT11_WFD_DEVICE_ENTRY(	
				pP2PInfo,
				&pP2PInfo->DeviceListForQuery.DeviceEntry[i],
				wfdEntry,
				&bytesWritten
			);

		// This one may have crashed some mark this temporailiy
		//DumpN63CConstruct_DOT11_WFD_DEVICE_ENTRY(
		//		wfdEntry,
		//		bytesWritten
		//	);

			pParameters->uListLength += bytesWritten;
		}

	// Assertion --------------------------------------------------------------------------------------------
	if(pParameters->uListLength != N63CComputeRequiredDeviceEntriesSize(pP2PInfo))
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: N63CComputeRequiredMemoryForDeviceList value mismatch!\n", __FUNCTION__));
	}
	//-----------------------------------------------------------------------------------------------------
	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_DISCOVER_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);

	//DumpDOT11_WFD_DISCOVER_COMPLETE_PARAMETERS(mbObject);


	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateInvitationRequestSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1    1
	
    typedef struct _DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_MAC_ADDRESS ReceiverAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS, * PDOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS;
 #endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS); 	// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;

		GET_80211_HDR_ADDRESS1(header, pParameters->PeerDeviceAddress);

		GET_80211_HDR_ADDRESS1(header, pParameters->ReceiverAddress);
		
		pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
		
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_INVITATION_REQUEST_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}

		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_INVITATION_REQUEST_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "mbObject.Buffer\n", mbObject.Buffer, mbObject.Length);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}

static VOID
N63CIndicateReceivedInvitationRequest(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS TransmitterDeviceAddress;
        DOT11_MAC_ADDRESS BSSID;
        DOT11_DIALOG_TOKEN DialogToken;
        PVOID RequestContext;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS, * PDOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS)
		);

	header = pEventData->Packet.Buffer;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->TransmitterDeviceAddress);
	
	GET_80211_HDR_ADDRESS3(header, pParameters->BSSID);

	pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);

	pParameters->RequestContext = NULL;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_INVITATION_REQUEST_PARAMETERS);

	// Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);
	
	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_INVITATION_REQUEST, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateInvitationResponseSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS ReceiverDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS, * PDOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;


		GET_80211_HDR_ADDRESS1(header, pParameters->ReceiverDeviceAddress);

		pParameters->DialogToken = pP2PInfo->ProvisionResponseDialogToken;
	
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_INVITATION_RESPONSE_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}
		
		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_INVITATION_RESPONSE_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateReceivedInvitationResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
)
{
#if 0
    #define DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS TransmitterDeviceAddress;
        DOT11_MAC_ADDRESS BSSID;
        DOT11_DIALOG_TOKEN DialogToken;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS, * PDOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS	 pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;

	FunctionIn(COMP_OID_SET);
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		mbObject.Length += frameLen - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS)
		);

	header = frameBuf;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->TransmitterDeviceAddress);
	
	GET_80211_HDR_ADDRESS3(header, pParameters->BSSID);

	pParameters->DialogToken = ctx->token;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_INVITATION_RESPONSE_PARAMETERS);

	// Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		pParameters->uIEsLength = frameLen - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_INVITATION_RESPONSE, 
			mbObject.Buffer, 
			mbObject.Length
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "mbObject.Buffer\n", mbObject.Buffer, mbObject.Length);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

	FunctionOut(COMP_OID_SET);

}

static VOID
N63CIndicateProvisionDiscoveryRequestSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1    1

    
    typedef struct _DOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_MAC_ADDRESS ReceiverAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS, * PDOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS;
 #endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += DOT11_SIZEOF_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1;  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!: %d\n", __FUNCTION__, mbObject.Length));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, DOT11_SIZEOF_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1);
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1,
			DOT11_SIZEOF_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;

		GET_80211_HDR_ADDRESS1(header, pParameters->PeerDeviceAddress);

		GET_80211_HDR_ADDRESS1(header, pParameters->ReceiverAddress);
		
		pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
		
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}

		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "mbObject.Buffer\n", mbObject.Buffer, mbObject.Length);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateReceivedProvisionDiscoveryRequest(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS_REVISION_1    1
    
    typedef struct _DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS TransmitterDeviceAddress;
        DOT11_MAC_ADDRESS BSSID;
        DOT11_DIALOG_TOKEN DialogToken;
        PVOID RequestContext;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS, * PDOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS)
		);

	header = pEventData->Packet.Buffer;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->TransmitterDeviceAddress);
	
	GET_80211_HDR_ADDRESS3(header, pParameters->BSSID);

	pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);

	pParameters->RequestContext = NULL;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_REQUEST_PARAMETERS);

	// Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);
	
	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_PROVISION_DISCOVERY_REQUEST, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}

static VOID
N63CIndicateProvisionDiscoveryResponseSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1    1

    typedef struct _DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS ReceiverDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS, * PDOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS;    
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;


		GET_80211_HDR_ADDRESS1(header, pParameters->ReceiverDeviceAddress);

		pParameters->DialogToken = pP2PInfo->ProvisionResponseDialogToken;
	
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}
		
		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}

static VOID
N63CIndicateReceivedProvisionDiscoveryResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
	)
{
#if 0
    #define DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS TransmitterDeviceAddress;
        DOT11_MAC_ADDRESS BSSID;
        DOT11_DIALOG_TOKEN DialogToken;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS, * PDOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS;    
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;

	FunctionIn(COMP_OID_SET);
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		mbObject.Length += frameLen - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS)
		);

	header = frameBuf;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->TransmitterDeviceAddress);
	
	GET_80211_HDR_ADDRESS3(header, pParameters->BSSID);

	pParameters->DialogToken = ctx->token;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_PROVISION_DISCOVERY_RESPONSE_PARAMETERS);

	// Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		pParameters->uIEsLength = frameLen - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_PROVISION_DISCOVERY_RESPONSE, 
			mbObject.Buffer, 
			mbObject.Length
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "mbObject.Buffer\n", mbObject.Buffer, mbObject.Length);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

	FunctionOut(COMP_OID_SET);

}


static VOID
N63CIndicateNegotiationRequestSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS, * PDOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successful ------------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!: %u\n", __FUNCTION__, mbObject.Length));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;


		GET_80211_HDR_ADDRESS1(header, pParameters->PeerDeviceAddress);
		
		pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
	
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_GO_NEGOTIATION_REQUEST_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}
		
		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_GO_NEGOTIATION_REQUEST_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}

static VOID
N63CIndicateReceivedGONegotiationRequest(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS_REVISION_1    1
    typedef struct _DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        PVOID RequestContext;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS, * PDOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS;    
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS)
		);

	header = pEventData->Packet.Buffer;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->PeerDeviceAddress);

	pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_GO_NEGOTIATION_REQUEST_PARAMETERS);

	// Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_GO_NEGOTIATION_REQUEST, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateNegotiationResponseSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0 
    #define DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1    1
    
    typedef struct _DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS, * PDOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successful ------------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;


		GET_80211_HDR_ADDRESS1(header, pParameters->PeerDeviceAddress);
		
		pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
	
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}
		
		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateReceivedGONegotiationResponse(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
)
{
#if 0
    #define DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS_REVISION_1    1

    typedef struct _DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        PVOID ResponseContext;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS, * PDOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;

	FunctionIn(COMP_OID_SET);
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		mbObject.Length += frameLen - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successfull -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS)
		);

	header = frameBuf;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->PeerDeviceAddress);

	pParameters->DialogToken = ctx->token;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_GO_NEGOTIATION_RESPONSE_PARAMETERS);

	// Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		pParameters->uIEsLength = frameLen - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_GO_NEGOTIATION_RESPONSE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

	FunctionOut(COMP_OID_SET);

}


static VOID
N63CIndicateNegotiationConfirmSendComplete(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
#if 0
    #define DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS_REVISION_1    1
    
    typedef struct _DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        NDIS_STATUS Status;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS, * PDOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS;
#endif

	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	PP2P_EVENT_DATA pEventData = (PP2P_EVENT_DATA) pInformation->Buffer;
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(pEventData->Packet.Length >= skippedBytes)
	{
		mbObject.Length += pEventData->Packet.Length - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successful ------------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS_REVISION_1,
			sizeof(DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS)
		);

	if(pEventData->rtStatus == RT_STATUS_FAILURE)
	{		
		pParameters->Status = NDIS_STATUS_FAILURE;
	}
	else
	{
		header = pEventData->Packet.Buffer;


		GET_80211_HDR_ADDRESS1(header, pParameters->PeerDeviceAddress);
		
		pParameters->DialogToken = *(header + FRAME_OFFSET_P2P_PUB_ACT_DIALOG_TOKEN);
	
		pParameters->Status = NDIS_STATUS_SUCCESS;

		pParameters->uIEsOffset = sizeof(DOT11_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE_PARAMETERS);

		// Only the valid packet 
		if(pEventData->Packet.Length >= skippedBytes)
		{
			pParameters->uIEsLength = pEventData->Packet.Length - skippedBytes;
		}
		
		PlatformMoveMemory(
				(pu1Byte) pParameters + pParameters->uIEsOffset, 
				header + skippedBytes, 
				pParameters->uIEsLength
			);
	
	}

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_GO_NEGOTIATION_CONFIRMATION_SEND_COMPLETE, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}


static VOID
N63CIndicateReceivedGONegotiationConfirm(
	PP2P_INFO pP2PInfo,
	const N63C_SEND_ACTION_CTX *ctx,
	u4Byte frameLen,
	u1Byte *frameBuf
)
{
#if 0
    #define DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS_REVISION_1    1    
    typedef struct _DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS 
    {
        NDIS_OBJECT_HEADER Header;
        DOT11_MAC_ADDRESS PeerDeviceAddress;
        DOT11_DIALOG_TOKEN DialogToken;
        ULONG uIEsOffset;
        ULONG uIEsLength;
    } DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS, * PDOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS;
#endif

    	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS	pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	pu1Byte header = NULL;
	
	// Skip the mac header (24) and the fields from Category to Dialog Token (8)
	u2Byte skippedBytes = FRAME_OFFSET_P2P_PUB_ACT_ELEMENTS;

	FunctionIn(COMP_OID_SET);
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS);  		// Parameter Structure Size

	// + Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		mbObject.Length += frameLen - skippedBytes;
	}
	//----------------------------------------------------------------------------------------------------------
	
	// This must be successful -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS));
	
	N6_ASSIGN_OBJECT_HEADER(
			pParameters->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS_REVISION_1,
			sizeof(DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS)
		);

	header = frameBuf;
		
	GET_80211_HDR_ADDRESS2(header, pParameters->PeerDeviceAddress);

	pParameters->DialogToken = ctx->token;
	  
	pParameters->uIEsOffset = sizeof(DOT11_RECEIVED_GO_NEGOTIATION_CONFIRMATION_PARAMETERS);

	// Only the valid packet 
	if(frameLen >= skippedBytes)
	{
		pParameters->uIEsLength = frameLen - skippedBytes;
	}	
		
	PlatformMoveMemory(
			(pu1Byte) pParameters + pParameters->uIEsOffset, 
			header + skippedBytes, 
			pParameters->uIEsLength
		);

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_RECEIVED_GO_NEGOTIATION_CONFIRMATION, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

	FunctionOut(COMP_OID_SET);

}

static VOID
N63CIndicateGOOperatingChannel(
	PP2P_INFO pP2PInfo,
	PMEMORY_BUFFER pInformation
)
{
	RT_STATUS rtStatus = RT_STATUS_FAILURE;
	PDOT11_WFD_CHANNEL pParameters = NULL;
	MEMORY_BUFFER mbObject = {NULL, 0};
	
	// Compute the requried memory length --------------------------------------------------------------------------
	mbObject.Length += sizeof(DOT11_WFD_CHANNEL); 	// Parameter Structure Size

	// This must be successful -----------------------------------------------------------------------
	rtStatus = PlatformAllocateMemory(pP2PInfo->pAdapter, &mbObject.Buffer, mbObject.Length);
	if(rtStatus == RT_STATUS_FAILURE)
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Memory Allocation Failure!\n", __FUNCTION__));
		return;
	}
	//--------------------------------------------------------------------------------------------

	pParameters = (PDOT11_WFD_CHANNEL) mbObject.Buffer;
	PlatformZeroMemory(pParameters, sizeof(DOT11_WFD_CHANNEL));
	
	PlatformMoveMemory((pu1Byte)pParameters->CountryRegionString, pP2PInfo->CountryString, 3);
	pParameters->OperatingClass = pP2PInfo->RegulatoryClass;
	pParameters->ChannelNumber = pP2PInfo->OperatingChannel;

	// Indicate the status to the OS -------------------------------------------------------------	
	N6IndicateStatus(
			pP2PInfo->pAdapter, 
			NDIS_STATUS_DOT11_WFD_GROUP_OPERATING_CHANNEL, 
			mbObject.Buffer, 
			mbObject.Length
		);
	//---------------------------------------------------------------------------------------

	PlatformFreeMemory(mbObject.Buffer, mbObject.Length);

}

static NDIS_STATUS
N63CValidateOIDCorrectnessGetReturnStatus(
	OID_CORRECT_STATE	correctState, 
	MP_PORT_TYPE		portType,
	P2P_ROLE			portRole,
	MP_PORT_OP_STATE	portState
)
{
// 
// Return Value:
//		NDIS_STATUS_INVALID_STATE: 		That port can process that OID in other state
//		NDIS_STATUS_SUCCESS:			Correct port in the correct state
//

	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_STATE; 

	if(portType == EXT_P2P_DEVICE_PORT)
	{
		if(portState == INIT_STATE) 
		{
			if(correctState & STATE_DEVICE_INIT)
			{
				ndisStatus = NDIS_STATUS_SUCCESS;
			}
		}
	}
	else if(portType == EXT_P2P_ROLE_PORT)
	{
		if(portRole == P2P_GO)
		{
			if(portState == INIT_STATE)
			{
				if(correctState & STATE_GO_INIT)
				{
					ndisStatus = NDIS_STATUS_SUCCESS;
				}
				else if(correctState & STATE_GO_OP)
				{
					ndisStatus = NDIS_STATUS_INVALID_STATE;
				}
			}
			else if(portState == OP_STATE)
			{
				if(correctState & STATE_GO_OP)
				{
					ndisStatus = NDIS_STATUS_SUCCESS;
				}
				else if(correctState & STATE_GO_INIT)
				{
					ndisStatus = NDIS_STATUS_INVALID_STATE;
				}
			}
		}
		else if(portRole == P2P_CLIENT)
		{
			if(portState == INIT_STATE)
			{
				if(correctState & STATE_CLIENT_INIT)
				{
					ndisStatus = NDIS_STATUS_SUCCESS;
				}
				else if(correctState & STATE_CLIENT_OP)
				{
					ndisStatus = NDIS_STATUS_INVALID_STATE;
				}
			}
			else if(portState == OP_STATE)
			{
				if(correctState & STATE_CLIENT_OP)
				{
					ndisStatus = NDIS_STATUS_SUCCESS;
				}
				else if(correctState & STATE_CLIENT_INIT)
				{
					ndisStatus = NDIS_STATUS_INVALID_STATE;
				}
			}
		}
	}

	return ndisStatus;
}

static NDIS_STATUS
N63CValidateQueryOIDCorrectness(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS 		ndisStatus = NDIS_STATUS_INVALID_STATE; 
	NDIS_OID 			oid = NdisRequest->DATA.QUERY_INFORMATION.Oid;
	PADAPTER 			pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER 			pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);
	PP2P_INFO			pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	
	MP_PORT_TYPE		portType = pTargetAdapter->pNdis62Common->PortType;
	P2P_ROLE			portRole	= pP2PInfo->Role;	
	MP_PORT_OP_STATE	portState = pTargetAdapter->pNdis62Common->CurrentOpState;
	
	switch (oid)
	{
		case OID_DOT11_WFD_ENUM_DEVICE_LIST:
		case OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY:
		case OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL:
		case OID_DOT11_WFD_GET_DIALOG_TOKEN:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_DEVICE_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
			
		default:
			ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
			break;
	}

	return ndisStatus;
}

static NDIS_STATUS
N63CValidateSetOIDCorrectness(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS 		ndisStatus = NDIS_STATUS_INVALID_STATE; 
	NDIS_OID 			oid = NdisRequest->DATA.QUERY_INFORMATION.Oid;
	PADAPTER 			pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER 			pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);
	PP2P_INFO			pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	
	MP_PORT_TYPE		portType = pTargetAdapter->pNdis62Common->PortType;
	P2P_ROLE			portRole	= pP2PInfo->Role;	
	MP_PORT_OP_STATE	portState = pTargetAdapter->pNdis62Common->CurrentOpState;
	
	switch (oid)
	{
		case OID_DOT11_WFD_DEVICE_CAPABILITY:
		case OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST:
    		case OID_DOT11_WFD_ADDITIONAL_IE:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_DEVICE_INIT | STATE_GO_INIT | STATE_GO_OP | STATE_CLIENT_INIT | STATE_CLIENT_OP,
					portType, 
					portRole, 
					portState
				);
			break;
			
		case OID_DOT11_WFD_GROUP_OWNER_CAPABILITY:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_GO_INIT | STATE_GO_OP | STATE_DEVICE_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
			
    		case OID_DOT11_WFD_DEVICE_INFO:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_DEVICE_INIT | STATE_GO_INIT | STATE_CLIENT_INIT,
					portType, 
					portRole, 
					portState
				);
			break;

		case OID_DOT11_WFD_DISCOVER_REQUEST:
		case OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY:
		case OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL:
		case OID_DOT11_WFD_FLUSH_DEVICE_LIST:
		case OID_DOT11_WFD_SEND_GO_NEGOTIATION_REQUEST:
		case OID_DOT11_WFD_SEND_GO_NEGOTIATION_RESPONSE:
    		case OID_DOT11_WFD_SEND_GO_NEGOTIATION_CONFIRMATION:
		case OID_DOT11_WFD_SEND_INVITATION_REQUEST:
		case OID_DOT11_WFD_SEND_INVITATION_RESPONSE:
		case OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_REQUEST:
    		case OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_RESPONSE:
		case OID_DOT11_WFD_STOP_DISCOVERY:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_DEVICE_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
			
		case OID_DOT11_WFD_DESIRED_GROUP_ID:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_GO_INIT | STATE_CLIENT_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
				
		case OID_DOT11_WFD_START_GO_REQUEST:
		case OID_DOT11_WFD_GROUP_START_PARAMETERS:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_GO_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
			
    		case OID_DOT11_WFD_CONNECT_TO_GROUP_REQUEST:
		case OID_DOT11_WFD_GROUP_JOIN_PARAMETERS:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_CLIENT_INIT,
					portType, 
					portRole, 
					portState
				);
			break;
			
		case OID_DOT11_WFD_DISCONNECT_FROM_GROUP_REQUEST:
			ndisStatus = N63CValidateOIDCorrectnessGetReturnStatus(
					STATE_CLIENT_OP,
					portType, 
					portRole, 
					portState
				);
			break;

		default:
			ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
			break;
	}

	return ndisStatus;
}

static NDIS_STATUS
N63CValidateMethodOIDCorrectness(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	// Currently, the driver does not accept the query call based on the Microsoft Wi-Fi Direct Spec v1.4
	UNREFERENCED_PARAMETER(pAdapter);
	UNREFERENCED_PARAMETER(NdisRequest);

	return NDIS_STATUS_NOT_SUPPORTED;
}

// Use this function to check the correctness of each OID issue based on the Mircosoft Wi-Fi Direct Spec v1.1
static NDIS_STATUS
N63CValidateOIDCorrectness(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS 		ndisStatus = NDIS_STATUS_INVALID_STATE; 
	PADAPTER 			pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER 			pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);
	PP2P_INFO			pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	MP_PORT_TYPE		portType = pTargetAdapter->pNdis62Common->PortType;
	P2P_ROLE			portRole	= pP2PInfo->Role;	
	MP_PORT_OP_STATE	portState = pTargetAdapter->pNdis62Common->CurrentOpState;

	RT_TRACE(COMP_OID_SET | COMP_OID_QUERY | COMP_P2P, DBG_LOUD, 
		("MP_PORT_TYPE: %d, P2P_ROLE: %d, MP_PORT_OP_STATE: %d\n", portType, portRole, portState));	
	
	do
	{
		// Currently, only handle these OIDs in the Wi-Fi Driect ports (Device Port or Role Port) since these ports are the upmost port type
		if(portType != EXT_P2P_DEVICE_PORT && portType != EXT_P2P_ROLE_PORT)
		{
			ndisStatus = NDIS_STATUS_INVALID_STATE;
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63CValidateQueryOIDCorrectness(pAdapter, NdisRequest);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63CValidateSetOIDCorrectness(pAdapter, NdisRequest);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = N63CValidateMethodOIDCorrectness(pAdapter, NdisRequest);
				break;
	
			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
		}
	}while(FALSE);

	return ndisStatus;
}


// Set operation for OID_DOT11_WFD_DEVICE_CAPABILITY
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DEVICE_CAPABILITY(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	// OS-given structure
	PDOT11_WFD_DEVICE_CAPABILITY_CONFIG deviceCapability = 
		(PDOT11_WFD_DEVICE_CAPABILITY_CONFIG) InformationBuffer;

	#if 0	// Implementation Status
	typedef struct _DOT11_WFD_DEVICE_CAPABILITY_CONFIG 
	{
    		NDIS_OBJECT_HEADER Header;						// SKIP
    		BOOLEAN bServiceDiscoveryEnabled;					// OK
    		BOOLEAN bClientDiscoverabilityEnabled;				// OK
    		BOOLEAN bConcurrentOperationSupported;				// OK
    		BOOLEAN bInfrastructureManagementEnabled;			// OK
    		BOOLEAN bDeviceLimitReached;						// OK
    		BOOLEAN bInvitationProcedureEnabled;				// OK
    		ULONG WPSVersionsEnabled;							// TODO
    	} DOT11_WFD_DEVICE_CAPABILITY_CONFIG, * PDOT11_WFD_DEVICE_CAPABILITY_CONFIG;
	#endif

	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;
	
	// Set the device capability
	pP2PInfo->DeviceCapability = 
		0x00 | 
		(deviceCapability->bServiceDiscoveryEnabled 			? dcServiceDiscovery : 0) 			|
		(deviceCapability->bClientDiscoverabilityEnabled 		? dcP2PClientDiscoverability : 0) 		|
    		(deviceCapability->bConcurrentOperationSupported 		? dcConcurrentOperation : 0) 		|
    		(deviceCapability->bInfrastructureManagementEnabled 	? dcP2PInfrastructureManaged : 0)	|
    		(deviceCapability->bDeviceLimitReached 				? dcP2PDeviceLimit : 0)				|
    		(deviceCapability->bInvitationProcedureEnabled 		? dcP2PInvitationProcedure : 0)		;


	// Currently, do not know how to implement this -------
	// 	Represent the WPS versions that are currently enabled for the Wi-Fi Direct Device
	RT_TRACE(COMP_P2P, DBG_LOUD, ("deviceCapability->WPSVersionsEnabled: %d\n", deviceCapability->WPSVersionsEnabled));
	
	#if 0	// Possible Solution
	Currently, this may be ignored since our driver does not necessarily process the WPS IE.
	#endif
	// ---------------------------------------------
		
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_GROUP_OWNER_CAPABILITY
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_GROUP_OWNER_CAPABILITY(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	P2P_ROLE		portRole	= pP2PInfo->Role;
	
	// OS-given structure
	PDOT11_WFD_GROUP_OWNER_CAPABILITY_CONFIG groupCapability = 
			(PDOT11_WFD_GROUP_OWNER_CAPABILITY_CONFIG) InformationBuffer;

	#if 0	// Implementation Status
    	typedef struct _DOT11_WFD_GROUP_OWNER_CAPABILITY_CONFIG 
   	{
    		NDIS_OBJECT_HEADER Header;				// SKIP
      		BOOLEAN bPersistentGroupEnabled;			// OK
    		BOOLEAN bIntraBSSDistributionSupported;		// OK
    		BOOLEAN bCrossConnectionSupported;		// OK
    		BOOLEAN bPersistentReconnectSupported;		// OK
    		BOOLEAN bGroupFormationEnabled;			// OK
    		ULONG uMaximumGroupLimit;					// TODO
    	} DOT11_WFD_GROUP_OWNER_CAPABILITY_CONFIG, *PDOT11_WFD_GROUP_OWNER_CAPABILITY_CONFIG;
	#endif
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	// Set the group capability
	pP2PInfo->GroupCapability = 
		0x00 | 
		(portRole == P2P_GO 											? gcP2PGroupOwner : 0) 		|
		(groupCapability->bPersistentGroupEnabled 						? gcPersistentP2PGroup : 0) 		|
    		(P2PClientInfoGetCount(pP2PInfo) >= P2P_MAX_P2P_CLIENT 		? gcP2PGroupLimit : 0) 			|
    		(groupCapability->bIntraBSSDistributionSupported 					? gcIntraBSSDistribution : 0)		|
    		(groupCapability->bCrossConnectionSupported 						? gcCrossConnection : 0)		|
    		(groupCapability->bPersistentReconnectSupported 					? gcPersistentReconnect : 0)	|
		(groupCapability->bGroupFormationEnabled 						? gcGroupFormation : 0)		;

	RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: pP2PInfo->GroupCapability: 0x%0X\n", __FUNCTION__, pP2PInfo->GroupCapability));
	
	// Currently, do not know how to implement this -------
	//	We use the P2P_MAX_P2P_CLIENT macro, while the OS uses the following statement
	groupCapability->uMaximumGroupLimit;

	#if 0	// Possible Solution
	Since our driver uses the macro P2P_MAX_P2P_CLIENT to set the maximum value, we may be able to omit this setting.
	The default value will be set in the DOT11_WFD_ATTRIBUTES.uGORoleClientTableSize during the initialization.
	#endif
	
	// ---------------------------------------------
		
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_DEVICE_INFO
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DEVICE_INFO(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	
	// OS-given structure
	PDOT11_WFD_DEVICE_INFO deviceInfo = 	
		(PDOT11_WFD_DEVICE_INFO) InformationBuffer;

	#if 0	// Implementation Status
	typedef struct _DOT11_WFD_DEVICE_INFO 
    	{
      		NDIS_OBJECT_HEADER Header;					// SKIP
       	DOT11_MAC_ADDRESS DeviceAddress;			// OK
       	USHORT ConfigMethods;						// OK
       	DOT11_WFD_DEVICE_TYPE PrimaryDeviceType;		// OK
       	DOT11_WPS_DEVICE_NAME DeviceName;			// OK
	} DOT11_WFD_DEVICE_INFO, *PDOT11_WFD_DEVICE_INFO;
	#endif
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	// Start to set the device information
	
	// Set the device address: Should we set the interface address based on the role of the port?
	cpMacAddr(pP2PInfo->DeviceAddress, deviceInfo->DeviceAddress);
	RT_PRINT_ADDR(COMP_P2P, DBG_LOUD, "Set pP2PInfo->DeviceAddress: ", pP2PInfo->DeviceAddress);
		
	// Set the config method for WPS
	pP2PInfo->WpsAttributes.ConfigMethod = deviceInfo->ConfigMethods;
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->WpsAttributes.ConfigMethod: 0x%X\n", pP2PInfo->WpsAttributes.ConfigMethod));
	
	// Set the primary device type for WPS	
	pP2PInfo->WpsAttributes.PrimaryDeviceType.CategoryId 		= deviceInfo->PrimaryDeviceType.CategoryID;
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->WpsAttributes.PrimaryDeviceType.CategoryId : %d\n", pP2PInfo->WpsAttributes.PrimaryDeviceType.CategoryId ));
	
	pP2PInfo->WpsAttributes.PrimaryDeviceType.SubCategoryId 	= deviceInfo->PrimaryDeviceType.SubCategoryID;
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->WpsAttributes.PrimaryDeviceType.SubCategoryId: %d\n", pP2PInfo->WpsAttributes.PrimaryDeviceType.SubCategoryId));
	
	PlatformMoveMemory(
			pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui, 
			deviceInfo->PrimaryDeviceType.OUI, 
			4
		);	
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui: %02X-%02X-%02X-%02X\n", 
			pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui[0],
			pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui[1],
			pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui[2],
			pP2PInfo->WpsAttributes.PrimaryDeviceType.Oui[3])
		);
	
	// Set the device name for WPS
	pP2PInfo->WpsAttributes.DeviceNameLength = (u1Byte) deviceInfo->DeviceName.uDeviceNameLength;
	PlatformMoveMemory(
			pP2PInfo->WpsAttributes.DeviceName, 
			deviceInfo->DeviceName.ucDeviceName, 
			deviceInfo->DeviceName.uDeviceNameLength
		);
	RT_PRINT_STR(COMP_P2P, DBG_LOUD, "DeviceName: ", pP2PInfo->WpsAttributes.DeviceName, pP2PInfo->WpsAttributes.DeviceNameLength);

	P2P_CorrectDeviceCategory(pTargetAdapter);
		
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	// Loop iteration variables
	ULONG			i = 0;
	
	// OS-given structure
	PDOT11_WFD_SECONDARY_DEVICE_TYPE_LIST secondaryDeviceTypeList = 	
		(PDOT11_WFD_SECONDARY_DEVICE_TYPE_LIST) InformationBuffer;

	#if 0	// Implementation Status
    	typedef struct _DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST {	
        	NDIS_OBJECT_HEADER Header;						// SKIP
        	ULONG uNumOfEntries;								// OK
        	ULONG uTotalNumOfEntries;							// TODO
      		DOT11_WFD_DEVICE_TYPE SecondaryDeviceTypes[1];	// OK
    	} DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST, * PDOT11_WFD_SECONDARY_DEVICE_TYPE_LIST;
	#endif
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	// Start to set the secondary device type list

	// Set the number of secondary device type list entries
	pP2PInfo->WpsAttributes.SecondaryDeviceTypeLength = 
		(u1Byte) secondaryDeviceTypeList->uNumOfEntries;

	RT_TRACE_F(COMP_P2P, DBG_LOUD, ("secondaryDeviceType number = %d\n", secondaryDeviceTypeList->uNumOfEntries));

	// Set each secondary device type from the OS-given structure
	for(i = 0; i < secondaryDeviceTypeList->uNumOfEntries; i++)
	{
		pP2PInfo->WpsAttributes.SecondaryDeviceTypeList[i].CategoryId = 
			secondaryDeviceTypeList->SecondaryDeviceTypes[i].CategoryID;
		
		pP2PInfo->WpsAttributes.SecondaryDeviceTypeList[i].SubCategoryId = 
			secondaryDeviceTypeList->SecondaryDeviceTypes[i].SubCategoryID;

		PlatformMoveMemory(
				pP2PInfo->WpsAttributes.SecondaryDeviceTypeList[i].Oui, 
				secondaryDeviceTypeList->SecondaryDeviceTypes[i].OUI, 
				4
			);

		RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "N63C_SET_OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST:\n",
			&(pP2PInfo->WpsAttributes.SecondaryDeviceTypeList[i]), sizeof(P2P_WPS_ATTRIBUTES_DEVICE_TYPE));
	}

	// Currently, do not know how to implement this -------
	//	The maximum number of entries that the SecondaryDeviceTypes array can contain. 
	//	The default value is 0.
	secondaryDeviceTypeList->uTotalNumOfEntries;

	#if 0	// Possible Solution
	This variable may be used to limit the maximum number of elements in SecondaryDeviceTypeList.
	Currently, the struct _P2P_WPS_ATTRIBUTES accepts 8 emements for SecondaryDeviceTypeList.
	#endif
	
	// ---------------------------------------------
	
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_ADDITIONAL_IE
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_ADDITIONAL_IE(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	RT_STATUS 		rtStatus = RT_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	MEMORY_BUFFER	mbOidIe = {NULL, 0};
		
	// OS-given structure
	PDOT11_WFD_ADDITIONAL_IE additionalIEs = 	
		(PDOT11_WFD_ADDITIONAL_IE) InformationBuffer;

	#if 0	// Implementation Status
	typedef struct _DOT11_WFD_ADDITIONAL_IE 
    	{
       	NDIS_OBJECT_HEADER Header;		// SKIP
        	ULONG uBeaconIEsOffset;			// OK
        	ULONG uBeaconIEsLength;			// OK
        	ULONG uProbeResponseIEsOffset;		// OK
        	ULONG uProbeResponseIEsLength;	// OK
        	ULONG uDefaultRequestIEsOffset;	// OK
        	ULONG uDefaultRequestIEsLength;	// OK
    	} DOT11_WFD_ADDITIONAL_IE, *PDOT11_WFD_ADDITIONAL_IE;
	#endif
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;
	
	// Additional Beacon IEs ------------------------------------------------------
	mbOidIe.Buffer = (PVOID) ((pu1Byte) additionalIEs + additionalIEs->uBeaconIEsOffset);
	mbOidIe.Length = additionalIEs->uBeaconIEsLength;

	P2P_AddIe_Set(
			&pP2PInfo->AdditionalIEs, 
			P2P_ADD_IE_BEACON, 
			additionalIEs->uBeaconIEsLength, 
			(u1Byte *) additionalIEs + additionalIEs->uBeaconIEsOffset
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "Beacon Additional IE: ====\n", mbOidIe.Buffer, mbOidIe.Length);
	//-------------------------------------------------------------------------

	// Additional Probe Response IEs ------------------------------------------------------
	mbOidIe.Buffer = (PVOID) ((pu1Byte) additionalIEs + additionalIEs->uProbeResponseIEsOffset);
	mbOidIe.Length = additionalIEs->uProbeResponseIEsLength;

	P2P_AddIe_Set(
			&pP2PInfo->AdditionalIEs, 
			P2P_ADD_IE_PROBE_RESPONSE,
			additionalIEs->uProbeResponseIEsLength,
			(u1Byte *) additionalIEs + additionalIEs->uProbeResponseIEsOffset
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "ProbeResponse Additional IE: ====\n", mbOidIe.Buffer, mbOidIe.Length);
	
	// -------------------------------------------------------------------------------
	
	// Additional Default Probe Request IEs --------------------------------------------------
	mbOidIe.Buffer = (PVOID) ((pu1Byte) additionalIEs + additionalIEs->uDefaultRequestIEsOffset);
	mbOidIe.Length = additionalIEs->uDefaultRequestIEsLength;

	P2P_AddIe_Set(
			&pP2PInfo->AdditionalIEs, 
			P2P_ADD_IE_DEFAULT_REQUEST,
			additionalIEs->uDefaultRequestIEsLength,
			(u1Byte *) additionalIEs + additionalIEs->uDefaultRequestIEsOffset
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "DefaultRequest Additional IE: ====\n", mbOidIe.Buffer, mbOidIe.Length);
	//----------------------------------------------------------------------------------

	P2P_CorrectDeviceCategory(pTargetAdapter);
	
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PADAPTER 		pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	BOOLEAN			bFilterOutNonAssociatedBSSID = FALSE;
			
	// OS-given data
	ULONG			deviceDiscoverability = *((PULONG) InformationBuffer);

	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	#if 0
		#define DOT11_WFD_DEVICE_NOT_DISCOVERABLE   0
		#define DOT11_WFD_DEVICE_AUTO_AVAILABILITY  16
		#define DOT11_WFD_DEVICE_HIGH_AVAILABILITY  24
	#endif

	FunctionIn(COMP_P2P);
	
	// Set the the corresponding variable in the structure
	pP2PInfo->uListenStateDiscoverability = deviceDiscoverability;
	RT_TRACE(COMP_OID_SET|COMP_P2P, DBG_LOUD, ("pP2PInfo->uListenStateDiscoverability: %d\n", pP2PInfo->uListenStateDiscoverability));

	switch(pP2PInfo->uListenStateDiscoverability)
	{
		case DOT11_WFD_DEVICE_NOT_DISCOVERABLE:
		{
			pP2PInfo->ExtListenTimingPeriod = 0;
			pP2PInfo->ExtListenTimingDuration = 0;
		}
		break;

		case DOT11_WFD_DEVICE_AUTO_AVAILABILITY:
		case DOT11_WFD_DEVICE_HIGH_AVAILABILITY:
		default: 
		{
			pP2PInfo->ExtListenTimingPeriod = 3 * P2P_MGNT_PERIOD;	// In the unit of P2P_MGNT_PERIOD
			pP2PInfo->ExtListenTimingDuration = 150;
			pP2PInfo->ExtListenTimingPeriodSlotCount = (u4Byte)P2P_EXT_LISTEN_TIMING_PERIOD_SC;

			// Do not check BSSID since the device port need to receive packets -----------------------------------------------------
			bFilterOutNonAssociatedBSSID = FALSE;
			pDefaultAdapter->HalFunc.SetHwRegHandler(pDefaultAdapter, HW_VAR_CHECK_BSSID, (pu1Byte)(&bFilterOutNonAssociatedBSSID));
			// -------------------------------------------------------------------------------------------------------------
			
			// Device Port is Open --------------------------------------------
			//DM_DIG_WifiDirectBoostStart(GetDefaultAdapter(pTargetAdapter));
			// -------------------------------------------------------------

			if(P2P_STATE_INITIALIZED == pP2PInfo->State)
			{
				PlatformCancelTimer(pP2PInfo->pAdapter, &pP2PInfo->P2PMgntTimer);
				PlatformSetTimer(pP2PInfo->pAdapter, &pP2PInfo->P2PMgntTimer, 0); 
				RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("start extended listen immediately\n"));
			}
		}
		break;
	}
	
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->ExtListenTimingPeriod: %d\n", pP2PInfo->ExtListenTimingPeriod));
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->ExtListenTimingDuration: %d\n", pP2PInfo->ExtListenTimingDuration));
		
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	
	// OS-given data
	PDOT11_WFD_DEVICE_LISTEN_CHANNEL	pListenChannel = (PDOT11_WFD_DEVICE_LISTEN_CHANNEL) InformationBuffer;

	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	#if 0
		// OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL
		#define OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL NWF_DEFINE_OID(0x13,NWF_WFD_DEVICE_OID,NWF_MANDATORY_OID)
		#define DOT11_WFD_DEVICE_LISTEN_CHANNEL_REVISION_1
		#define DOT11_SIZEOF_WFD_DEVICE_LISTEN_CHANNEL_REVISION_1

		typedef
		    struct _DOT11_WFD_DEVICE_LISTEN_CHANNEL
		    {
		        NDIS_OBJECT_HEADER Header;
		        UCHAR ChannelNumber;
		    } DOT11_WFD_DEVICE_LISTEN_CHANNEL, *PDOT11_WFD_DEVICE_LISTEN_CHANNEL;
	#endif

	// Set the the corresponding variable in the structure -------------------------------------------
	pP2PInfo->ListenChannel = pListenChannel->ChannelNumber;
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->ListenChannel: %d\n", pP2PInfo->ListenChannel));
	// --------------------------------------------------------------------------------------
	
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_DISCOVER_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DISCOVER_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	BOOLEAN			bStatus = FALSE;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	MEMORY_BUFFER	mbOidIe = {NULL, 0};
	OCTET_STRING 	osSsid = {NULL, 0};
	PDOT11_SSID 	pDot11Ssid = NULL;
	BOOLEAN			bStartDiscovery = TRUE;
	PP2P_DEVICE_DISCRIPTOR pP2PDeviceDesc = NULL;
	PP2P_DEVICE_LIST_ENTRY pP2PDeviceListEntry = NULL;
	PADAPTER		pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);	
	// Loop iteration variables
	ULONG 			i = 0;
	u8Byte			curTime = PlatformGetCurrentTime();
	
	// OS-given structure
	PDOT11_WFD_DISCOVER_REQUEST discoverRequest = 	
		(PDOT11_WFD_DISCOVER_REQUEST) InformationBuffer;
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	FunctionIn(COMP_OID_SET);
	
	// Save the Oid Issue time for speeding up the Go Negotiation ----------------
	pP2PInfo->LastDeviceDiscoveryOidIssueTime = PlatformGetCurrentTime();
	// ------------------------------------------------------------------
		
	RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: uDiscoverTimeout: %d\n", __FUNCTION__, discoverRequest->uDiscoverTimeout));

	// Discover Type
	switch(discoverRequest->DiscoverType)
	{
		case dot11_wfd_discover_type_scan_only:
			pP2PInfo->DiscoverSequence = P2P_DISCOVERY_SCAN_PHASE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->DiscoverType: dot11_wfd_discover_type_scan_only\n"));
			break;

		case dot11_wfd_discover_type_find_only:
			pP2PInfo->DiscoverSequence = P2P_DISCOVERY_FIND_PHASE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->DiscoverType: dot11_wfd_discover_type_find_only\n"));
			break;

		case dot11_wfd_discover_type_auto:
			pP2PInfo->DiscoverSequence = P2P_DISCOVERY_FIND_PHASE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->DiscoverType: dot11_wfd_discover_type_auto\n"));
			break;
	
		case dot11_wfd_discover_type_scan_social_channels:
			pP2PInfo->DiscoverSequence = P2P_DISCOVERY_FIND_PHASE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->DiscoverType: dot11_wfd_discover_type_auto\n"));
			break;

        	case dot11_wfd_discover_type_forced:
		default:
			pP2PInfo->DiscoverSequence = P2P_DISCOVERY_SCAN_PHASE | P2P_DISCOVERY_FIND_PHASE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->DiscoverType: dot11_wfd_discover_type_forced\n"));
			break;
	}

	// Scan Type
	switch(discoverRequest->ScanType)
	{
		case dot11_wfd_scan_type_auto:
			pP2PInfo->ScanType = SCAN_ACTIVE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->ScanType: dot11_wfd_scan_type_auto\n"));
			break;
			
		case dot11_wfd_scan_type_active:
			pP2PInfo->ScanType = SCAN_ACTIVE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->ScanType: dot11_wfd_scan_type_active\n"));
			break;
			
		case dot11_wfd_scan_type_passive:
		default:
			pP2PInfo->ScanType = SCAN_PASSIVE;
			RT_TRACE(COMP_P2P, DBG_LOUD, ("discoverRequest->ScanType: dot11_wfd_scan_type_passive\n"));
			break;
	}

	// Set the device IDs for device discovery ------------------------------------------
	P2PClearScanDeviceID(&pP2PInfo->ScanDeviceIDs);

	for(i = 0; i < discoverRequest->uNumDeviceFilters; i++)
	{
		bStatus = P2PAddScanDeviceID(
				&pP2PInfo->ScanDeviceIDs,
				((pu1Byte) discoverRequest + discoverRequest->uDeviceFilterListOffset + i * sizeof(DOT11_WFD_DISCOVER_DEVICE_FILTER))
			);
		RT_PRINT_ADDR(COMP_P2P, DBG_LOUD, "Device ID List Entry:",
			((pu1Byte) discoverRequest + discoverRequest->uDeviceFilterListOffset + i * sizeof(DOT11_WFD_DISCOVER_DEVICE_FILTER)));

		if(bStatus != TRUE)
		{
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("%s: P2PInfo.ScanDeviceIDs is too small!\n", __FUNCTION__));
		}
	}

	if(discoverRequest->uNumDeviceFilters == 1)	
	{// specific peer search
		P2P_DEV_LIST_ENTRY 		*dev = NULL;
		u1Byte					chnl = 0;
		
		p2p_DevList_Lock(&pP2PInfo->devList);

		pP2PInfo->uNumberOfDiscoverForSpecificChannels = 0;
		
		if(NULL != (dev = p2p_DevList_GetGo(&pP2PInfo->devList, pP2PInfo->ScanDeviceIDs.DeviceIDs[0])))
		{// go
			if(0 != (chnl = p2p_DevList_GetDevChnl(dev)))
			{
				pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = chnl;
			}
		}

		if(NULL != (dev = p2p_DevList_Get(&pP2PInfo->devList, pP2PInfo->ScanDeviceIDs.DeviceIDs[0], P2P_DEV_TYPE_DEV)))
		{// dev
			if(0 != (chnl = p2p_DevList_GetDevChnl(dev)))
			{
				pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = chnl;
				pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = pP2PInfo->ListenChannel; // For Peer to Find us in GO Negotiation Procedure
				pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = pP2PInfo->ListenChannel; // For Peer to Find us in GO Negotiation Procedure
			}
			else
			{
				// For case we have only received PD req from peer, since PD does not give info about peer Listen Channel,
				// we add all social channels, otherwise we will do scan phase which wastes time
				if(0 == chnl)
				{
					if(dev->rxFrames[P2P_FID_PROBE_REQ] && dev->rxFrames[P2P_FID_PROBE_REQ]->msg->listenChannel)
					{
						RT_TRACE_F(COMP_P2P, DBG_LOUD, ("add listen channel %u from ProbeReq\n", dev->rxFrames[P2P_FID_PROBE_REQ]->msg->listenChannel));
						pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = dev->rxFrames[P2P_FID_PROBE_REQ]->msg->listenChannel;
					}
					else if(dev->rxFrames[P2P_FID_PD_REQ])
					{
						RT_TRACE_F(COMP_P2P, DBG_LOUD, ("only have peer's PD req, add all social channel\n"));
						pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = 1;
						pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = 6;
						pP2PInfo->DiscoverForSpecificChannels[pP2PInfo->uNumberOfDiscoverForSpecificChannels++] = 11;
					}
				}
			}
		}

		if(pP2PInfo->uNumberOfDiscoverForSpecificChannels)
		{
			pP2PInfo->bDiscoverForSpecificChannels = TRUE;
			pP2PInfo->uNumberOfDiscoverRounds = 10;
			RT_PRINT_DATA(COMP_OID_SET, DBG_LOUD, "to scan following channels to find specific peer:\n", 
				pP2PInfo->DiscoverForSpecificChannels, pP2PInfo->uNumberOfDiscoverForSpecificChannels);

			if(	pP2PInfo->DeviceListForQuery.uNumberOfDevices != 0 &&	// If empty device list, force to scan
				PlatformGetCurrentTime() - pP2PInfo->LastDeviceDiscoveryIndicatedTime < 300 * 1000)  // Do not discover for that short time 300 ms
			{
				RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: Discover Too Frequently, Complete Directly\n", __FUNCTION__));
				bStartDiscovery = FALSE;
			}
		}
		else
		{
			pP2PInfo->bDiscoverForSpecificChannels = FALSE;
			pP2PInfo->uNumberOfDiscoverRounds = 0;
			
			RT_TRACE_F(COMP_P2P, DBG_WARNING, ("device entry not found\n"));
			
			P2PDeviceListActionInterface(
					pP2PInfo, 
					P2P_DEVICE_LIST_ACTION_DUMP,
					&pP2PInfo->DeviceList, 
					NULL, NULL
				);
			
			P2PDumpScanList(pP2PInfo->ScanList, pP2PInfo->ScanListSize);
		}

		p2p_DevList_Unlock(&pP2PInfo->devList);
	}
	else
	{		
		RT_TRACE(COMP_P2P, DBG_LOUD, ("devicelist empty\n"));

		if(curTime < MultiportGetLastConnectionActionTime(pTargetAdapter) + P2P_BLOCK_NORMAL_SCAN_PERIOD)
		{
			RT_TRACE_F(COMP_P2P, DBG_WARNING, ("[WARNING] Last Connection time - current time = %d us, skip this discovery\n", (u4Byte)(MultiportGetLastConnectionActionTime(pTargetAdapter) + P2P_BLOCK_NORMAL_SCAN_PERIOD - curTime)));
			bStartDiscovery = FALSE;
		}
	}
	
	//----------------------------------------------------------------------------

	
	// Additional Probe Request IEs ---------------------------------------------------
	mbOidIe.Buffer = (PVOID) ((pu1Byte) discoverRequest + discoverRequest->uIEsOffset);
	mbOidIe.Length = discoverRequest->uIEsLength;

	P2P_AddIe_Set(
			&pP2PInfo->AdditionalIEs, 
			P2P_ADD_IE_PROBE_REQUEST,
			discoverRequest->uIEsLength,
			(u1Byte *) discoverRequest + discoverRequest->uIEsOffset
		);

	RT_PRINT_DATA(COMP_P2P, DBG_LOUD, "ProbeRequest Additional IE:\n", mbOidIe.Buffer, mbOidIe.Length);
	//----------------------------------------------------------------------------

	
	// Set the legacy network scanning ------------------------------------------------
	pP2PInfo->bForceScanLegacyNetworks = discoverRequest->bForceScanLegacyNetworks;
	RT_TRACE(COMP_OID_SET | COMP_P2P, DBG_LOUD, ("%s: pP2PInfo->bForceScanLegacyNetworks: %d\n", __FUNCTION__, pP2PInfo->bForceScanLegacyNetworks));
	//----------------------------------------------------------------------------
	
	if(bStartDiscovery)
	{
		// Win8: Guarantee the packet transmission in the right channel since the this OID will switch channel --------------------------------------------------------------------------------------------
		//RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Delay 1000 ms to guranatee the packet transmission in the right channel and also update the P2P state machine!\n"));
		//delay_ms(500);
		// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//		pP2PInfo->bDeviceDiscoveryInProgress = TRUE;

		//
		// <Roger_Notes>
		// Leave LPS mode prior to the process of P2P device discovery to improve discovery capability in DC power mode.
		// 2014.03.25.
		//
		LeisurePSLeave(pDefaultAdapter, LPS_DISABLE_DEVICE_DISCOVERY);// Leave LPS mode for P2P device discovery, added by Roger, 2014.03.25.

		RT_TRACE(COMP_OID_SET | COMP_P2P, DBG_LOUD, ("Delay 50 ms to guranatee the action frame transmission in the right channel!\n"));
		delay_ms(50);
		
		for(i = 0; MgntScanInProgress(&pP2PInfo->pAdapter->MgntInfo); i++)
		{
			RT_TRACE(COMP_OID_SET | COMP_P2P, DBG_LOUD, ("Delay 10 ms to guranatee the packet transmission in the right channel!\n"));
			delay_ms(10);

			if(i == 2) // Wait for 30 ms
			{	
				// Stop scan imediately to wait for new action
				P2PScanListCeaseScan(pP2PInfo);
				break;
			}
		}
	
		for(i = 0; pP2PInfo->State != P2P_STATE_INITIALIZED; i++)
		{
			RT_TRACE(COMP_OID_SET | COMP_P2P, DBG_LOUD, ("Delay 10 ms to update the P2P state machine!\n"));
			delay_ms(10);
			
			if(i == 50) break;
		}

		// Clear the device list
		P2PDeviceListActionInterface(
			pP2PInfo,
			P2P_DEVICE_LIST_ACTION_CLEAR,
			&pP2PInfo->DeviceList,
			NULL, NULL
		);
		
		// Clear the device list for query
		P2PDeviceListActionInterface(
			pP2PInfo,
			P2P_DEVICE_LIST_ACTION_CLEAR,
			&pP2PInfo->DeviceListForQuery,
			NULL, NULL
		);		
		
		// Simply do the device discovery since the device discovery may be used as the method to guarantee the common channel 
		P2PResetCommonChannelArrivingProcess(pP2PInfo);

		// Start the device discovery
		if(pP2PInfo->pAdapter->bInHctTest)
			P2PDeviceDiscovery(pP2PInfo, 2);
		else
			P2PDeviceDiscovery(pP2PInfo, 10);

		// Win8: The discover time is very limited so we delay the OID and do the discovery in the same time. --------------------------
		//RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Delay 3000 ms to get additional time to scan!\n"));
		//delay_ms(3000);
		// -------------------------------------------------------------------------------------------------------------
		
		// Set the indication to OS when the discovery completes
		pP2PInfo->bDeviceDiscoveryIndicateToOS = TRUE;
	}
	else
	{
		// Win8: Guarantee the packet transmission in the right channel since the this OID will switch channel --------------------------------------------------------------------------------------------
		RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Delay 50 ms to guranatee the packet transmission in the right channel and also update the P2P state machine!\n"));
		delay_ms(50);
		// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
		
		// Let this OID routine run faster than the P2POidPostProcessWorkItemCallback ------
		pP2PInfo->bPostoneP2POidPostProcessWorkItem = TRUE;
		// ----------------------------------------------------------------------
		
		// Scheduling the sending of the provision discovery response --------------------------------------
		pP2PInfo->OidOperation = OID_OPERATION_INDICATE_DISCOVERY_COMPLETE;
#if 1
		PlatformSetTimer(pTargetAdapter, &(pP2PInfo->P2POidPostProcessTimer), 10);
#else
		PlatformScheduleWorkItem(&pP2PInfo->P2POidPostProcessWorkItem);
#endif
		//----------------------------------------------------------------------------------------
	}
	
	// OS needs this status code
	ndisStatus = NDIS_STATUS_INDICATION_REQUIRED;

	// Let the P2POidPostProcessWorkItem Run
	pP2PInfo->bPostoneP2POidPostProcessWorkItem = FALSE;
	
	FunctionOut(COMP_OID_SET);

	return ndisStatus;
}


// Query operation for OID_DOT11_WFD_GET_DIALOG_TOKEN
static NDIS_STATUS
N63C_QUERY_OID_DOT11_WFD_GET_DIALOG_TOKEN(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
	u1Byte 			nextToken = 0;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	//-------------------------------------------------------

	// Preidict the next token generated by the marco of IncreaseDialogToken(pP2PInfo->DialogToken) : Non-Zero ----------
	nextToken = (pP2PInfo->DialogToken + 1 == 0) ? (pP2PInfo->DialogToken + 2) : (pP2PInfo->DialogToken + 1);
	
	RT_TRACE(COMP_P2P, DBG_LOUD, ("%s: nextToken: %d\n", __FUNCTION__, nextToken));
	// --------------------------------------------------------------------------------------------------------
	
	*BytesWritten = 1;
	*((pu1Byte) InformationBuffer) = nextToken;

	pP2PInfo->oidDialogToken = nextToken;

	return NDIS_STATUS_SUCCESS;
}


// Query operation for OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY
static NDIS_STATUS
N63C_QUERY_OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	//-------------------------------------------------------

	*BytesWritten = sizeof(pP2PInfo->uListenStateDiscoverability);
	*((PULONG) InformationBuffer) = pP2PInfo->uListenStateDiscoverability;
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->uListenStateDiscoverability: %d\n", pP2PInfo->uListenStateDiscoverability));
	
	return NDIS_STATUS_SUCCESS;
}

// Query operation for OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL
static NDIS_STATUS
N63C_QUERY_OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
	PP2P_INFO	pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PDOT11_WFD_DEVICE_LISTEN_CHANNEL 
		pListenChannel = (PDOT11_WFD_DEVICE_LISTEN_CHANNEL) InformationBuffer;

	#if 0
		#define DOT11_WFD_DEVICE_LISTEN_CHANNEL_REVISION_1 
		#define DOT11_SIZEOF_WFD_DEVICE_LISTEN_CHANNEL_REVISION_1 

		typedef struct _DOT11_WFD_DEVICE_LISTEN_CHANNEL { 
		 NDIS_OBJECT_HEADER Header; 
		 UCHAR ChannelNumber; 
		} DOT11_WFD_DEVICE_LISTEN_CHANNEL, *PDOT11_WFD_DEVICE_LISTEN_CHANNEL; 
	#endif

	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	//-------------------------------------------------------

	N6_ASSIGN_OBJECT_HEADER(
			pListenChannel->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_WFD_DEVICE_LISTEN_CHANNEL_REVISION_1,
			sizeof(DOT11_WFD_DEVICE_LISTEN_CHANNEL)
		);
	
	pListenChannel->ChannelNumber = pP2PInfo->ListenChannel;

	*BytesWritten = sizeof(DOT11_WFD_DEVICE_LISTEN_CHANNEL);
	
	RT_TRACE(COMP_P2P, DBG_LOUD, ("pP2PInfo->ListenChannel: %d\n",  pP2PInfo->ListenChannel));

	return NDIS_STATUS_SUCCESS;
}

// Query operation for OID_DOT11_WFD_ENUM_DEVICE_LIST
static NDIS_STATUS
N63C_QUERY_OID_DOT11_WFD_ENUM_DEVICE_LIST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
#if 0
	typedef struct DOT11_BYTE_ARRAY {
  		NDIS_OBJECT_HEADER Header;
  		ULONG              uNumOfBytes;
  		ULONG              uTotalNumOfBytes;
  		UCHAR              ucBuffer[1];
	} DOT11_BYTE_ARRAY, *PDOT11_BYTE_ARRAY;
#endif

	u4Byte			i = 0;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	MEMORY_BUFFER	mbObject = {NULL, 0};
	PDOT11_WFD_DEVICE_ENTRY wfdEntry = NULL;
	u4Byte			usedBytes = 0;
		
	// Skip the MAC header and the first three fields (non-IE) in the beacon and the probe response
	u4Byte skippedBytes = sMacHdrLng + 12;
	
	// OS-given data
	PDOT11_BYTE_ARRAY pByteArray = (PDOT11_BYTE_ARRAY) InformationBuffer;
	
	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	pByteArray->uNumOfBytes = 0;
	pByteArray->uTotalNumOfBytes = 0;
	PlatformZeroMemory(pByteArray, sizeof(DOT11_BYTE_ARRAY));
	//-------------------------------------------------------


	// Update the device list for the OS query ----------------------
	P2PDeviceListActionInterface(
			pP2PInfo, 
			P2P_DEVICE_LIST_ACTION_COPY_TO_QUERY_LIST,
			NULL, NULL, NULL
		);
	// -------------------------------------------------------
	
	
	N6_ASSIGN_OBJECT_HEADER(
			pByteArray->Header,
			NDIS_OBJECT_TYPE_DEFAULT,
			DOT11_DEVICE_ENTRY_BYTE_ARRAY_REVISION_1,
			sizeof(DOT11_BYTE_ARRAY)
		);
		
	// Compute the requried memory length ------------------------------------------------------------------
	*BytesNeeded += FIELD_OFFSET(DOT11_BYTE_ARRAY, ucBuffer);
	*BytesNeeded += N63CComputeRequiredDeviceEntriesSize(pP2PInfo);
	//-------------------------------------------------------------------------------------------------

	// Output buffer length checking ---------------
	if(*BytesNeeded > InformationBufferLength)
	{
		*BytesWritten = 0;
		return NDIS_STATUS_BUFFER_OVERFLOW;
	}
	//-----------------------------------------
	
	// Fill the device entries -------------------------------------------------------------------------------
	wfdEntry = (PDOT11_WFD_DEVICE_ENTRY)((pu1Byte) pByteArray + FIELD_OFFSET(DOT11_BYTE_ARRAY, ucBuffer));

	for(i = 0; i < pP2PInfo->DeviceListForQuery.uNumberOfDevices; i++)
	{
		wfdEntry = (PDOT11_WFD_DEVICE_ENTRY)((pu1Byte) wfdEntry + usedBytes);

		N63CConstruct_DOT11_WFD_DEVICE_ENTRY(	
				pP2PInfo,
				&pP2PInfo->DeviceListForQuery.DeviceEntry[i],
				wfdEntry,
				&usedBytes
			);
		
		pByteArray->uNumOfBytes += usedBytes;
	}
	//--------------------------------------------------------------------------------------------------
	
	// Assertion --------------------------------------------------------------------------------------------
	if(pByteArray->uNumOfBytes != N63CComputeRequiredDeviceEntriesSize(pP2PInfo))
	{
		RT_TRACE(COMP_P2P, DBG_LOUD, (__FUNCTION__": N63CComputeRequiredMemoryForDeviceList value mismatch!\n"));
	}
	//-----------------------------------------------------------------------------------------------------

	// Post settings for the successfull execution -----------------------------------
	pByteArray->uTotalNumOfBytes = pByteArray->uNumOfBytes;
	*BytesWritten = *BytesNeeded;
	*BytesNeeded = 0;

	return NDIS_STATUS_SUCCESS;
	// -----------------------------------------------------------------------
}


// Set operation for OID_DOT11_WFD_FLUSH_DEVICE_LIST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_FLUSH_DEVICE_LIST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PMGNT_INFO		pTargetMgntInfo = &pTargetAdapter->MgntInfo;
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	// Clear the scan list
	PlatformZeroMemory( pTargetMgntInfo->bssDesc, sizeof(RT_WLAN_BSS)*MAX_BSS_DESC );
	pTargetMgntInfo->NumBssDesc = 0;	
	
	// Clear the device list
	P2PDeviceListActionInterface(
			pP2PInfo,
			P2P_DEVICE_LIST_ACTION_CLEAR,
			&pP2PInfo->DeviceList,
			NULL, NULL
		);
	
	// Clear the device list for Query
	P2PDeviceListActionInterface(
			pP2PInfo,
			P2P_DEVICE_LIST_ACTION_CLEAR,
			&pP2PInfo->DeviceListForQuery,
			NULL, NULL
		);	
	
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_STOP_DISCOVERY
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_STOP_DISCOVERY(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	if(P2P_DOING_DEVICE_DISCOVERY(pP2PInfo))
	{
		P2PScanListCeaseScan(pP2PInfo);
		P2PDeviceDiscoveryComplete(pP2PInfo, TRUE);
	}
	
	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_SEND_INVITATION_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_INVITATION_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;
	VOID 				*req = NULL;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;

	do
	{
		DOT11_SEND_INVITATION_REQUEST_PARAMETERS *param = (DOT11_SEND_INVITATION_REQUEST_PARAMETERS *)InformationBuffer;
		
		if(NULL == (req = N63C_SendAction_InvitationReq(pTargetAdapter, param, n63c_SendP2pReqActionFrameStateCb)))
		{
			status = NDIS_STATUS_FAILURE;
			break;
		}

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);

	FunctionOut(COMP_OID_SET);
	
	return status;
}

// Set operation for OID_DOT11_WFD_SEND_INVITATION_RESPONSE
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_INVITATION_RESPONSE(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;

	do
	{
		PlatformMoveMemory(InfoBuf, InformationBuffer, InformationBufferLength);
		InfoBufLen = InformationBufferLength;

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);

	FunctionOut(COMP_OID_SET);
	
	return status;
}

// Set operation for OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;
	VOID 				*req = NULL;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;

	do
	{
		DOT11_SEND_PROVISION_DISCOVERY_REQUEST_PARAMETERS *param = (DOT11_SEND_PROVISION_DISCOVERY_REQUEST_PARAMETERS *)InformationBuffer;
	
		if(NULL == (req = N63C_SendAction_PdReq(pTargetAdapter, param, n63c_SendP2pReqActionFrameStateCb)))
		{
			status = NDIS_STATUS_FAILURE;
			break;
		}

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);
	
	FunctionOut(COMP_OID_SET);
	
	return status;
}


// Set operation for OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_RESPONSE
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_RESPONSE(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;

	do
	{
		PlatformMoveMemory(InfoBuf, InformationBuffer, InformationBufferLength);
		InfoBufLen = InformationBufferLength;

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);
	
	FunctionOut(COMP_OID_SET);
	
	return status;
}

// Set operation for OID_DOT11_WFD_SEND_GO_NEGOTIATION_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;
	VOID 				*req = NULL;
	
	PP2P_INFO	pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PADAPTER 	pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);
	
	// OS-given structure
	PDOT11_SEND_GO_NEGOTIATION_REQUEST_PARAMETERS pParameters = 
		(PDOT11_SEND_GO_NEGOTIATION_REQUEST_PARAMETERS) InformationBuffer;

	// Clear output variables -----
	*BytesRead = 0;
	*BytesNeeded = 0;
	//------------------------

	FunctionIn(COMP_OID_SET);

#if (MULTICHANNEL_SUPPORT == 1)
	// Consider the default port's link state -----------------------------------------------------------------------------------------------------------------
	if(MgntLinkStatusQuery(GetDefaultAdapter(pP2PInfo->pAdapter)) == RT_MEDIA_CONNECT)
	{
		pP2PInfo->OperatingChannel = MultiChannelGetPortConnected20MhzChannel(GetDefaultAdapter(pP2PInfo->pAdapter));
		RT_TRACE_F(COMP_P2P, DBG_LOUD, ("Change op ch to default port connected channel: %d\n", pP2PInfo->OperatingChannel));	
	}
	else
	{
		if(pDefaultAdapter->MgntInfo.WFDOpChannel !=0)
			pP2PInfo->OperatingChannel = pDefaultAdapter->MgntInfo.WFDOpChannel;
		else
		{
// Removed by Bruce, 2015-6-28.
// Shall not change OP channel after the GO has started or the channels for all P2P roles have been defined.
#if 0 // (AUTO_CHNL_SEL_NHM ==1)
			if(IS_AUTO_CHNL_SUPPORT(pDefaultAdapter))
				pP2PInfo->OperatingChannel =  P2PIsSocialChannel(GET_AUTO_CHNL_SELECTED_NUM(pDefaultAdapter)) ? 
					GET_AUTO_CHNL_SELECTED_NUM(pDefaultAdapter) : P2P_DEFAULT_OPERATING_CHANNEL;
			else
#endif
			{
				if(0 == pP2PInfo->OperatingChannel)
					pP2PInfo->OperatingChannel = P2P_DEFAULT_OPERATING_CHANNEL;
			}
		}
		RT_TRACE_F(COMP_P2P, DBG_LOUD, (" [ACS] Change op ch to P2P_DEFAULT_OPERATING_CHANNEL channel: %d\n", pP2PInfo->OperatingChannel));	
	}
	// -----------------------------------------------------------------------------------------------------------------------------------------------
#endif

	do
	{
		DOT11_SEND_GO_NEGOTIATION_REQUEST_PARAMETERS *param = (DOT11_SEND_GO_NEGOTIATION_REQUEST_PARAMETERS *)InformationBuffer;
	
		if(NULL == (req = N63C_SendAction_GoNegReq(pTargetAdapter, param, n63c_SendP2pReqActionFrameStateCb)))
		{
			status = NDIS_STATUS_FAILURE;
			break;
		}

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);
		
	FunctionOut(COMP_OID_SET);
	
	return status;
}

// Set operation for OID_DOT11_WFD_SEND_GO_NEGOTIATION_RESPONSE
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_RESPONSE(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;

	do
	{
		PlatformMoveMemory(InfoBuf, InformationBuffer, InformationBufferLength);
		InfoBufLen = InformationBufferLength;

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);
	
	FunctionOut(COMP_OID_SET);
	
	return status;
}



// Set operation for OID_DOT11_WFD_SEND_GO_NEGOTIATION_CONFIRM
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_CONFIRM(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS			status = NDIS_STATUS_SUCCESS;
	VOID 				*req = NULL;

	FunctionIn(COMP_OID_SET);

	*BytesRead = 0;
	*BytesNeeded = 0;
	
	do
    {
		PlatformMoveMemory(InfoBuf, InformationBuffer, InformationBufferLength);
		InfoBufLen = InformationBufferLength;

		status = NDIS_STATUS_INDICATION_REQUIRED;
	}while(FALSE);

	FunctionOut(COMP_OID_SET);

	return status;
}


// Set operation for OID_DOT11_WFD_START_GO_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_START_GO_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	PRT_NDIS62_COMMON 	pNdis62Common = pTargetAdapter->pNdis62Common;
	PADAPTER		pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PADAPTER 		pDevicePort = GetFirstDevicePort(pDefaultAdapter);
	PP2P_INFO		pDeviceP2PInfo = GET_P2P_INFO(pDevicePort);
	
	
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY | COMP_P2P);

	// Check the AP state
	if(GetAPState(pTargetAdapter) != AP_STATE_STOPPED)
	{
		RT_TRACE((COMP_OID_SET | COMP_OID_QUERY | COMP_INIT | COMP_INDIC), DBG_LOUD, ("AP is not in INIT STATE\n"));

		return NDIS_STATUS_INVALID_STATE;
	}

	// Radio is in the OFF state
	if(N6CQuery_DOT11_NIC_POWER_STATE(GetDefaultAdapter(pTargetAdapter)) == FALSE)
	{
		RT_TRACE(COMP_OID_SET, DBG_LOUD, ("RF is OFF, return NDIS_STATUS_DOT11_POWER_STATE_INVALID.\n"));

		return NDIS_STATUS_DOT11_POWER_STATE_INVALID;
	}


#if 1 // The Extended Listening will let the AP's Channel be wrong when during extended listening

	//if(MgntScanInProgress(&pDefaultAdapter->MgntInfo))
	//{
		// Postpone the extended listening for a while
	//	P2PExtendedListenResetCounter(pDeviceP2PInfo);
			
		// Stop potential device port extended listening 
	//	P2PScanListCeaseScan(pDeviceP2PInfo);

		// Delay for a while for running ScanTimer callback by context switch
		//	+ Otherwise, this scan will be skipped due to MgntScanInProgress flag
	//	delay_ms(20);
	//}

	if(P2P_DOING_DEVICE_DISCOVERY(pDeviceP2PInfo))
	{// Doing P2P Device Discovery
		//msDelayStart += 10; // if scan in progress, we have to wait until ScanComplete() finishes
		P2PScanListCeaseScan(pDeviceP2PInfo);
		P2PDeviceDiscoveryComplete(pDeviceP2PInfo, TRUE); // P2P State is restored in this function.
	}
	else if(pDeviceP2PInfo->bExtendedListening)
	{// Doing extended listening
		P2PScanListCeaseScan(pDeviceP2PInfo);
		P2PExtendedListenComplete(pDeviceP2PInfo);
	}
	else if(MgntScanInProgress(&pDefaultAdapter->MgntInfo)) 
	{// Doing normal scan
		//msDelayStart += 10; // if scan in progress, we have to wait until ScanComplete() finishes
		P2PScanListCeaseScan(pDeviceP2PInfo);
	}

	delay_ms(20);

#endif


#if (MULTICHANNEL_SUPPORT == 1)
{
	if(MultiChannelSwitchNeeded(pDefaultAdapter))
	{
		// Stop MultiChannel switch for initializing the GO Tsf -----------
		MultiChannelStopChannelSwitchScheduler(pDefaultAdapter,TRUE);
		// -----------------------------------------------------
	}
}
#endif

	// Change the last scan complete time to block scan request for a while from the default port.
	// If the scan process is executed, the next handshake may fail because this port is not in the
	// correct channel now.
	MultiportRecordLastScanTime(pDefaultAdapter);

	if(0 == pP2PInfo->OperatingChannel)
		pP2PInfo->OperatingChannel = P2P_DEFAULT_OPERATING_CHANNEL;
	
	// Note:
	// Do not change operating channel here becuase the OP channel has been negotiated from the previous
	// handshake or set by OS.
#if 0


	// Consider the default port's link state
	if(MgntLinkStatusQuery(pDefaultAdapter) == RT_MEDIA_CONNECT)
	{
		#if (MULTICHANNEL_SUPPORT == 1)	// For Compilation: Only PCI-E Support MultiChannel Now

		pP2PInfo->OperatingChannel = MultiChannelGetPortConnected20MhzChannel(pDefaultAdapter);

		#endif
		
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("Use Default Port Channel: Channel %d\n", pP2PInfo->OperatingChannel));
	}
	else
	{
		if(pDefaultAdapter->MgntInfo.WFDOpChannel != 0)
			pP2PInfo->OperatingChannel = pDefaultAdapter->MgntInfo.WFDOpChannel;
		else
		{
#if (AUTO_CHNL_SEL_NHM ==1)
			if(IS_AUTO_CHNL_SUPPORT(pDefaultAdapter))
				pP2PInfo->OperatingChannel =  P2PIsSocialChannel(GET_AUTO_CHNL_SELECTED_NUM(pDefaultAdapter)) ?
					GET_AUTO_CHNL_SELECTED_NUM(pDefaultAdapter) : P2P_DEFAULT_OPERATING_CHANNEL;
			else
#endif
				pP2PInfo->OperatingChannel = P2P_DEFAULT_OPERATING_CHANNEL;
		}
		RT_TRACE((COMP_OID_QUERY | COMP_OID_SET), DBG_LOUD, ("[ACS] Start GO at Channel %d\n", pP2PInfo->OperatingChannel));
	}
#endif


	// Update Device Port's Operating Channel Number (Win8 Action Frame is Processed by Only Device Port)
	{
		PADAPTER pDevicePort = GetFirstDevicePort(pDefaultAdapter);
		PP2P_INFO pDeviceP2PInfo = GET_P2P_INFO(pDevicePort);
		
		pDeviceP2PInfo->OperatingChannel = pP2PInfo->OperatingChannel;
	}

	RT_TRACE_F(COMP_MLME, DBG_LOUD, ("ChangeWirelessModeHandler\n"));
	HalChangeWirelessMode(pTargetAdapter, pP2PInfo->OperatingChannel);

	RT_TRACE_F(COMP_MLME, DBG_LOUD, ("MgntActSet_802_11_CHANNEL_AND_BANDWIDTH\n"));

	if(pDefaultAdapter->MgntInfo.mAssoc)
	{		
		HalChangeWirelessMode(pDefaultAdapter, pP2PInfo->OperatingChannel);
		// Set the operating channel
		MgntActSet_802_11_CHANNEL_AND_BANDWIDTH(
			pTargetAdapter, 
			pP2PInfo->OperatingChannel, 
			pDefaultAdapter->MgntInfo.pChannelInfo->CurrentChannelBandWidth, 
			pDefaultAdapter->MgntInfo.pChannelInfo->Ext20MHzChnlOffsetOf40MHz, 
			pDefaultAdapter->MgntInfo.pChannelInfo->Ext40MHzChnlOffsetOf80MHz, 
			0
			);
		RT_TRACE_F(COMP_P2P, DBG_LOUD, ("Default port is conencted, switch to channel (%d), Bandwidth (%d), 20/40M_Offset (%d), 40/80M_Offset (%d)\n",
			pDefaultAdapter->MgntInfo.dot11CurrentChannelNumber, 
			pDefaultAdapter->MgntInfo.pChannelInfo->CurrentChannelBandWidth, 
			pDefaultAdapter->MgntInfo.pChannelInfo->Ext20MHzChnlOffsetOf40MHz, 
			pDefaultAdapter->MgntInfo.pChannelInfo->Ext40MHzChnlOffsetOf80MHz));
	}
	else
	{
		// Set the operating channel		
		MgntActSet_802_11_CHANNEL_AND_BANDWIDTH(
				pTargetAdapter, 
				pP2PInfo->OperatingChannel, 
				CHANNEL_WIDTH_20, 
				EXTCHNL_OFFSET_NO_EXT, 
				EXTCHNL_OFFSET_NO_EXT, 
				0
			);
	}
	

	// Resolve the Wrong GO active channel -----------------------------------------------------------------------
	{
		PADAPTER	pLoopAdapter = GetDefaultAdapter(pTargetAdapter);

		RT_TRACE(COMP_P2P, DBG_LOUD, ("Set All SettingBeforeScan.ChannelNumber = %d\n", pP2PInfo->OperatingChannel));
		
		while(pLoopAdapter !=NULL)
		{
			pLoopAdapter->MgntInfo.dot11CurrentChannelNumber = pP2PInfo->OperatingChannel;
			pLoopAdapter->MgntInfo.SettingBeforeScan.ChannelNumber = pP2PInfo->OperatingChannel;
			pLoopAdapter->MgntInfo.SettingBeforeScan.CenterFrequencyIndex1 = CHNL_GetCenterFrequency(pP2PInfo->OperatingChannel,pLoopAdapter->MgntInfo.SettingBeforeScan.ChannelBandwidth, pLoopAdapter->MgntInfo.SettingBeforeScan.Ext20MHzChnlOffsetOf40MHz);
			pLoopAdapter = GetNextExtAdapter(pLoopAdapter);
		}
		
		pLoopAdapter = GetDefaultAdapter(pTargetAdapter);
		// Prefast warning 6011 Dereferencing NULL pointer 'pLoopAdapter'.
		if (pLoopAdapter != NULL)
		{
			RT_TRACE_F(COMP_P2P | COMP_HT, DBG_LOUD,
				("SettingBeforeScan() chnl: %u, CenterFrequencyIndex1: %u\n",
					pLoopAdapter->MgntInfo.SettingBeforeScan.ChannelNumber,
					pLoopAdapter->MgntInfo.SettingBeforeScan.CenterFrequencyIndex1));
		}
		
	}
	// --------------------------------------------------------------------------------------------------------

	// Start AP mode
	ndisStatus=N62CStartApMode(pTargetAdapter);
	pNdis62Common->CurrentOpState = OP_STATE;


	// Debug Message
	{
		PADAPTER pAdapter = GetDefaultAdapter(pTargetAdapter);

		while(pAdapter != NULL)
		{
			RT_TRACE(COMP_P2P, DBG_LOUD, ("pAdapter->pNdis62Common->PortNumber: %d\n", pAdapter->pNdis62Common->PortNumber));
			RT_PRINT_ADDR(COMP_P2P, DBG_LOUD, "pAdapter->MgntInfo.Bssid: ", pAdapter->MgntInfo.Bssid);
								
			pAdapter = GetNextExtAdapter(pAdapter);
		}
	}
	
	if(ndisStatus == NDIS_STATUS_SUCCESS)
	{
		PlatformIndicateP2PEvent(pP2PInfo, P2P_EVENT_GO_OPERATING_CHANNEL, NULL);
	}

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY | COMP_P2P);

	return ndisStatus;
}

// Set operation for OID_DOT11_WFD_GROUP_START_PARAMETERS
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_GROUP_START_PARAMETERS(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
#if 0
    #define DOT11_WFD_GROUP_START_PARAMETERS_REVISION_1     1
    
    typedef struct _DOT11_WFD_GROUP_START_PARAMETERS {	
        NDIS_OBJECT_HEADER Header;
        DOT11_WFD_CHANNEL AdvertisedOperatingChannel;
    } DOT11_WFD_GROUP_START_PARAMETERS, * PDOT11_WFD_GROUP_START_PARAMETERS;

    typedef struct _DOT11_WFD_CHANNEL 
    {
        DOT11_COUNTRY_OR_REGION_STRING CountryRegionString;
        UCHAR OperatingClass;
        UCHAR ChannelNumber;
    } DOT11_WFD_CHANNEL, * PDOT11_WFD_CHANNEL;
#endif

	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);

	// OS-given structure
	PDOT11_WFD_GROUP_START_PARAMETERS pParameters = 
		(PDOT11_WFD_GROUP_START_PARAMETERS) InformationBuffer;
		
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;
	
	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);
	

	// Only consider the channel number
	pP2PInfo->OperatingChannel = pParameters->AdvertisedOperatingChannel.ChannelNumber; 
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pParameters->AdvertisedOperatingChannel.ChannelNumber: %d\n", pParameters->AdvertisedOperatingChannel.ChannelNumber));
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return NDIS_STATUS_SUCCESS;

}
	
// Set operation for OID_DOT11_WFD_GROUP_JOIN_PARAMETERS
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_GROUP_JOIN_PARAMETERS(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
#if 0
    typedef 
    struct _DOT11_WFD_GROUP_JOIN_PARAMETERS {	
        NDIS_OBJECT_HEADER Header;
        DOT11_WFD_CHANNEL GOOperatingChannel;
        ULONG GOConfigTime;
        BOOLEAN bInGroupFormation;					// TODO
        BOOLEAN bWaitForWPSReady;					// TODO
    } DOT11_WFD_GROUP_JOIN_PARAMETERS, * PDOT11_WFD_GROUP_JOIN_PARAMETERS;
#endif

	PADAPTER 	pDevicePort = GetFirstDevicePort(pTargetAdapter);
	PP2P_INFO	pDeviceP2PInfo = GET_P2P_INFO(pDevicePort);
	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PP2P_DEVICE_DISCRIPTOR pP2PDeviceDesc = NULL;
	PP2P_DEVICE_LIST_ENTRY pP2PDeviceListEntry = NULL;

	// OS-given structure
	PDOT11_WFD_GROUP_JOIN_PARAMETERS pParameters = 
		(PDOT11_WFD_GROUP_JOIN_PARAMETERS) InformationBuffer;
		
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;
	
	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);
	
	// Only consider the channel number
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pParameters->GOConfigTime: %d\n", pParameters->GOConfigTime));

	pP2PInfo->OperatingChannel = pParameters->GOOperatingChannel.ChannelNumber; 
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));

	pP2PInfo->ClientJoinGroupContext.bInGroupFormation = pParameters->bInGroupFormation;
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->ClientJoinGroupContext.bInGroupFormation: %d\n", pP2PInfo->ClientJoinGroupContext.bInGroupFormation));
	
	pP2PInfo->ClientJoinGroupContext.WpsState = (pParameters->bWaitForWPSReady) ? P2P_CLIETN_JOIN_GROUP_WPS_STATE_SCANNING : P2P_CLIETN_JOIN_GROUP_WPS_STATE_NONE;
	RT_TRACE(COMP_OID_SET, DBG_LOUD, ("pP2PInfo->ClientJoinGroupContext.WpsState: %d\n", pP2PInfo->ClientJoinGroupContext.WpsState));


	// Select the Target Channel in AutoGO mode ----------------------------------------------------------------------------------
	if(pP2PInfo->OperatingChannel == 0)
	{
		RT_PRINT_ADDR(COMP_OID_SET, DBG_LOUD, "pP2PInfo->DesiredTargetMacAddress: ", pP2PInfo->DesiredTargetMacAddress);
	
		// From Win8 Specific Device Information Pool	
		pP2PDeviceListEntry = P2PDeviceListFind(&pDeviceP2PInfo->DeviceList, pP2PInfo->DesiredTargetMacAddress);
		
		// From Common Device Information Pool: Use Interface Address
		pP2PDeviceDesc = P2PScanListFind(pDeviceP2PInfo->ScanList, pDeviceP2PInfo->ScanListSize, NULL, pP2PInfo->DesiredTargetMacAddress, NULL);

		if(pP2PDeviceListEntry)
		{
			pP2PInfo->OperatingChannel = pP2PDeviceListEntry->ChannelNumber;
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Win8 Pool: Revised for AutoGO: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));
		}
		else if(pP2PDeviceDesc && pP2PDeviceDesc->OperatingChannel != 0)
		{
			pP2PInfo->OperatingChannel = pP2PDeviceDesc->OperatingChannel;
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Common Pool: Revised for AutoGO: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));
		}
		else
		{
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("No Peer Observed: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));

			P2PDeviceListActionInterface(
						pDeviceP2PInfo, 
						P2P_DEVICE_LIST_ACTION_DUMP,
						&pDeviceP2PInfo->DeviceList, 
						NULL, NULL
				);

			P2PDumpScanList(pDeviceP2PInfo->ScanList, pDeviceP2PInfo->ScanListSize);
		}
	}
	// ----------------------------------------------------------------------------------------------------------------------


	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return NDIS_STATUS_SUCCESS;

}


// Set operation for OID_DOT11_WFD_DESIRED_GROUP_ID
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DESIRED_GROUP_ID(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
#if 0
	typedef struct _DOT11_WFD_GROUP_ID 
	{
    		DOT11_MAC_ADDRESS DeviceAddress;
    		DOT11_SSID SSID;	
	} DOT11_WFD_GROUP_ID, * PDOT11_WFD_GROUP_ID;

	typedef struct _DOT11_SSID
	{
  		ULONG uSSIDLength;
  		UCHAR ucSSID[DOT11_SSID_MAX_LENGTH];
	} DOT11_SSID, *PDOT11_SSID;

	typedef struct DOT11_SSID_LIST {
         NDIS_OBJECT_HEADER Header;
         ULONG uNumOfEntries;
         ULONG uTotalNumOfEntries;
         DOT11_SSID SSIDs[1];
    	} DOT11_SSID_LIST, *PDOT11_SSID_LIST;
#endif

	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	PRT_NDIS6_COMMON	pNdisCommon = pTargetAdapter->pNdisCommon;
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	DOT11_SSID_LIST	SsidList;

	// OS-given structure
	PDOT11_WFD_GROUP_ID pParameters = (PDOT11_WFD_GROUP_ID) InformationBuffer;
		
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;
	
	FunctionIn(COMP_OID_SET);

	RT_PRINT_ADDR(COMP_OID_SET, DBG_LOUD, "Target GO MAC Address: ", pParameters->DeviceAddress);
	PlatformMoveMemory(pP2PInfo->DesiredTargetMacAddress, pParameters->DeviceAddress, 6);

	RT_PRINT_STR(COMP_OID_SET, DBG_LOUD, "Target GO SSID: ", pParameters->SSID.ucSSID,  pParameters->SSID.uSSIDLength);
	pP2PInfo->uGroupTargetSSIDLength = pParameters->SSID.uSSIDLength;
	PlatformMoveMemory(pP2PInfo->GroupTargetSSID, pParameters->SSID.ucSSID, pParameters->SSID.uSSIDLength);

	// NdisTest: If no SSID exists
	if(pParameters->SSID.uSSIDLength == 0)
	{
		return NDIS_STATUS_INVALID_LENGTH;
	}
	

	N6_ASSIGN_OBJECT_HEADER(
			SsidList.Header,
			NDIS_OBJECT_TYPE_DEFAULT, 
			DOT11_SSID_LIST_REVISION_1, 
			sizeof(DOT11_SSID_LIST)
		);
	
	SsidList.uNumOfEntries = 1;
	SsidList.SSIDs[0] = pParameters->SSID;

	pNdisCommon->dot11DesiredSSIDList = SsidList;
		
	
	FunctionOut(COMP_OID_SET);
	
	return ndisStatus;

}

// Set operation for OID_DOT11_WFD_CONNECT_TO_GROUP_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_CONNECT_TO_GROUP_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{

	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PADAPTER 	pDevicePort = GetFirstDevicePort(pTargetAdapter);
	PP2P_INFO	pDeviceP2PInfo = GET_P2P_INFO(pDevicePort);
	PP2P_DEVICE_DISCRIPTOR pP2PDeviceDesc = NULL;
	PP2P_DEVICE_LIST_ENTRY pP2PDeviceListEntry = NULL;	
	u1Byte i = 0;
		
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	FunctionIn(COMP_OID_SET);

	// Change the last scan complete time to block scan request for a while from the default port.
	// If the scan process is executed, the next handshake may fail because this port is not in the
	// correct channel now.
	MultiportRecordLastScanTime(pTargetAdapter);

	if(pP2PInfo->OperatingChannel == 0)
	{
		RT_PRINT_ADDR(COMP_OID_SET, DBG_LOUD, "pP2PInfo->DesiredTargetMacAddress: ", pP2PInfo->DesiredTargetMacAddress);

		// From Win8 Specific Device Information Pool	
		pP2PDeviceListEntry = P2PDeviceListFind(&pDeviceP2PInfo->DeviceList, pP2PInfo->DesiredTargetMacAddress);
		
		// From Common Device Information Pool: Use Interface Address
		pP2PDeviceDesc = P2PScanListFind(pDeviceP2PInfo->ScanList, pDeviceP2PInfo->ScanListSize, NULL, pP2PInfo->DesiredTargetMacAddress, NULL);

		if(pP2PDeviceListEntry)
		{
			pP2PInfo->OperatingChannel = pP2PDeviceListEntry->ChannelNumber;
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Win8 Pool: Revised for AutoGO: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));
		}
		else if(pP2PDeviceDesc && pP2PDeviceDesc->OperatingChannel != 0)
		{
			pP2PInfo->OperatingChannel = pP2PDeviceDesc->OperatingChannel;
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("Common Pool: Revised for AutoGO: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));
		}
		else
		{
			RT_TRACE(COMP_OID_SET, DBG_LOUD, ("No Peer Observed: pP2PInfo->OperatingChannel: %d\n", pP2PInfo->OperatingChannel));

			P2PDeviceListActionInterface(
						pDeviceP2PInfo, 
						P2P_DEVICE_LIST_ACTION_DUMP,
						&pDeviceP2PInfo->DeviceList, 
						NULL, NULL
				);

			P2PDumpScanList(pDeviceP2PInfo->ScanList, pDeviceP2PInfo->ScanListSize);
		}
	}

#if 0
	// Switch band
	if(IS_DUAL_BAND_SUPPORT(pTargetAdapter))
	{
		WIRELESS_MODE wirelessmode;
		
		if(IS_WIRELESS_MODE_5G(pTargetAdapter))
		{
			if(pP2PInfo->OperatingChannel < 14)
			{
				wirelessmode = WIRELESS_MODE_N_24G;
				RT_TRACE(COMP_MLME, DBG_LOUD,("Change to right 2G wirlessmode.\n"));
				pTargetAdapter->HalFunc.SetWirelessModeHandler(pTargetAdapter, (u1Byte)(wirelessmode));
			}
		}
		else if(IS_WIRELESS_MODE_24G(pTargetAdapter))
		{
			if(pP2PInfo->OperatingChannel > 14)
			{
				wirelessmode = WIRELESS_MODE_N_5G;
				RT_TRACE(COMP_MLME, DBG_LOUD,("Change to right 5G wirlessmode .\n"));
				pTargetAdapter->HalFunc.SetWirelessModeHandler(pTargetAdapter, (u1Byte)(wirelessmode));
			}
		}
	}
#else	
	RT_TRACE_F(COMP_MLME, DBG_LOUD, ("ChangeWirelessModeHandler\n"));
	HalChangeWirelessMode(pTargetAdapter, pP2PInfo->OperatingChannel);

//	SetupWirelessMode(pTargetAdapter, pP2PInfo->OperatingChannel); 
#endif
	// Swith to the Right Target Channel specified in OID_DOT11_WFD_GROUP_JOIN_PARAMETERS --
	MgntActSet_802_11_CHANNEL_AND_BANDWIDTH(
			pTargetAdapter, 
			pP2PInfo->OperatingChannel, 
			CHANNEL_WIDTH_20, 
			EXTCHNL_OFFSET_NO_EXT, 
			EXTCHNL_OFFSET_NO_EXT, 
			0
		);	
	// -------------------------------------------------------------------------------

	if(P2P_CLIETN_JOIN_GROUP_WPS_STATE_NONE == pP2PInfo->ClientJoinGroupContext.WpsState)
	{
	pTargetAdapter->pNdis62Common->CurrentOpState = OP_STATE;

	ndisStatus = N6CSet_DOT11_CONNECT_REQUEST(
			pTargetAdapter,
			InformationBuffer,
			InformationBufferLength,
			BytesRead,
			BytesNeeded
		);
	}
	else
	{
		VOID *customScanReq = NULL;
		VOID *customScanInfo = GET_CUSTOM_SCAN_INFO(pTargetAdapter);
		ADAPTER *pDevAdapter = NULL;
		P2P_INFO *pDevP2PInfo = NULL;
		
		pP2PInfo->ClientJoinGroupContext.uWaitForWpsSlotCount = 50;	// Wait for Connection : 5 sec
		PlatformSetTimer(pTargetAdapter, &pP2PInfo->ClientJoinGroupContext.P2PWaitForWpsReadyTimer, 120);

		if(NULL != (pDevAdapter = GetFirstDevicePort(pTargetAdapter))
			&& (NULL != (pDevP2PInfo = GET_P2P_INFO(pDevAdapter)))
			&& NULL != (customScanReq = CustomScan_AllocReq(customScanInfo, NULL, NULL))
			)
		{
			CustomScan_AddScanChnl(customScanReq, pDevP2PInfo->ListenChannel, 
				1, SCAN_PASSIVE, 100, P2P_LOWEST_RATE, NULL);
			
			CustomScan_IssueReq(customScanInfo, customScanReq, CUSTOM_SCAN_SRC_TYPE_P2P, "make cli dev discoverable by GO");
		}
	}

	FunctionOut(COMP_OID_SET);
	
	return ndisStatus;

}

	
// Set operation for OID_DOT11_WFD_DISCONNECT_FROM_GROUP_REQUEST
static NDIS_STATUS	
N63C_SET_OID_DOT11_WFD_DISCONNECT_FROM_GROUP_REQUEST(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{

	PP2P_INFO		pP2PInfo = GET_P2P_INFO(pTargetAdapter);
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	
	// OS-given structure
	//PDOT11_WFD_GROUP_ID pParameters = (PDOT11_WFD_GROUP_ID) InformationBuffer;
		
	// Output variables (Currently no use)
	*BytesRead = 0;
	*BytesNeeded = 0;

	FunctionIn(COMP_OID_SET);

	pTargetAdapter->pNdis62Common->CurrentOpState = INIT_STATE;

	ndisStatus = N6CSet_DOT11_DISCONNECT_REQUEST(
			pTargetAdapter,
			InformationBuffer,
			InformationBufferLength,
			BytesRead,
			BytesNeeded
		);

	// Reset the variable in OID_DOT11_WFD_GROUP_START_PARAMETERS ------------------
	P2PResetClientJoinGroupContext(pP2PInfo);
	// ---------------------------------------------------------------------------
	
	FunctionOut(COMP_OID_SET);
	
	return ndisStatus;

}

NDIS_STATUS
N63C_QUERY_OID_PACKET_COALESCING_FILTER_MATCH_COUNT(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
		
	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	//-------------------------------------------------------

	FunctionIn(COMP_OID_QUERY);

	return ndisStatus;
}

//======================================================================================
// Public Functions 
//======================================================================================

VOID
N63CIndicateP2PEvent(
	PVOID pvP2PInfo, 
	u4Byte EventID,
	PMEMORY_BUFFER pInformation
) 
{
	PP2P_INFO	pP2PInfo = (PP2P_INFO) pvP2PInfo;
	PADAPTER	pAdapter = pP2PInfo->pAdapter;

	if(OS_SUPPORT_WDI(pP2PInfo->pAdapter))
	{
		WDI_IndicateP2PEvent(pvP2PInfo, EventID, pInformation);
		return;
	}

	// rx req action
	if(P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_REQUEST == EventID
		|| P2P_EVENT_RECEIVED_INVITATION_REQUEST == EventID
		)
	{
		N63C_SendAction_TerminateReq(pP2PInfo->pAdapter);

	}

	// rx rsp action
	if(P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_RESPONSE == EventID
		|| P2P_EVENT_RECEIVED_INVITATION_RESPONSE == EventID
		|| P2P_EVENT_RECEIVED_GO_NEGOTIATION_RESPONSE == EventID
		|| P2P_EVENT_RECEIVED_GO_NEGOTIATION_CONFIRM == EventID
		)
	{
		N63C_SendAction_TerminateReq(pP2PInfo->pAdapter);

	}

	switch(EventID)
	{
		case P2P_EVENT_DEVICE_DISCOVERY_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_DEVICE_DISCOVERY_COMPLETE\n"));
			N63CIndicateDeviceDiscoveryComplete(pP2PInfo);
			break;
			
		case P2P_EVENT_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_PROVISION_DISCOVERY_REQUEST_SEND_COMPLETE\n"));
			N63CIndicateProvisionDiscoveryRequestSendComplete(pP2PInfo, pInformation);
			break;
			
		case P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_REQUEST:
			{
				RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_REQUEST\n"));
				InfoBufLen = 0;
				N63CIndicateReceivedProvisionDiscoveryRequest(pP2PInfo, pInformation);
				if(InfoBufLen)
				{
					if(NULL == N63C_SendAction_PdRsp(pAdapter, (DOT11_SEND_PROVISION_DISCOVERY_RESPONSE_PARAMETERS *)InfoBuf, n63c_SendP2pRspActionFrameStateCb))
					{
						RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("N63C_SendAction_PdRsp failed\n"));
						p2p_IndicateActionFrameSendCompleteWithToken(pP2PInfo, P2P_EVENT_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE, RT_STATUS_FAILURE, NULL, 0, 0);
					}
					InfoBufLen = 0;
				}
			}
			break;
			
		case P2P_EVENT_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_PROVISION_DISCOVERY_RESPONSE_SEND_COMPLETE\n"));
			N63CIndicateProvisionDiscoveryResponseSendComplete(pP2PInfo, pInformation);
			break;
			
		case P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_RESPONSE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_PROVISION_DISCOVERY_RESPONSE\n"));
			//N63CIndicateReceivedProvisionDiscoveryResponse(pP2PInfo, pInformation);
			break;
			
		case P2P_EVENT_GO_NEGOTIATION_REQUEST_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_GO_NEGOTIATION_REQUEST_SEND_COMPLETE\n"));
			N63CIndicateNegotiationRequestSendComplete(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_RECEIVED_GO_NEGOTIATION_REQUEST:
			{
				RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_GO_NEGOTIATION_REQUEST\n"));
				InfoBufLen = 0;
				N63CIndicateReceivedGONegotiationRequest(pP2PInfo, pInformation);
				if(InfoBufLen)
				{
					N63C_SendAction_TerminateReq(pP2PInfo->pAdapter);

					if(NULL == N63C_SendAction_GoNegRsp(pAdapter, (DOT11_SEND_GO_NEGOTIATION_RESPONSE_PARAMETERS *)InfoBuf, n63c_SendP2pRspActionFrameStateCb))
					{
						RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("N63C_SendAction_GoNegRsp failed\n"));
						p2p_IndicateActionFrameSendCompleteWithToken(pP2PInfo, P2P_EVENT_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE, RT_STATUS_FAILURE, NULL, 0, 0);
					}
					InfoBufLen = 0;
				}
			}
			break;
			
		case P2P_EVENT_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE:
			{
				RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_GO_NEGOTIATION_RESPONSE_SEND_COMPLETE\n"));
				N63CIndicateNegotiationResponseSendComplete(pP2PInfo, pInformation);
			}
			break;

		case P2P_EVENT_RECEIVED_GO_NEGOTIATION_RESPONSE:			
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_GO_NEGOTIATION_RESPONSE\n"));
			//N63CIndicateReceivedGONegotiationResponse(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_GO_NEGOTIATION_CONFIRM_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_GO_NEGOTIATION_CONFIRM_SEND_COMPLETE\n"));
			N63CIndicateNegotiationConfirmSendComplete(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_RECEIVED_GO_NEGOTIATION_CONFIRM:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_GO_NEGOTIATION_CONFIRM\n"));
			//N63CIndicateReceivedGONegotiationConfirm(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_INVITATION_REQUEST_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_INVITATION_REQUEST_SEND_COMPLETE\n"));
			N63CIndicateInvitationRequestSendComplete(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_RECEIVED_INVITATION_REQUEST:
			{
				RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_INVITATION_REQUEST\n"));
				InfoBufLen = 0;
				N63CIndicateReceivedInvitationRequest(pP2PInfo, pInformation);
				if(InfoBufLen)
				{
					if(NULL == N63C_SendAction_InvitationRsp(pAdapter, (DOT11_SEND_INVITATION_RESPONSE_PARAMETERS *)InfoBuf, n63c_SendP2pRspActionFrameStateCb))
					{
						RT_TRACE_F(COMP_OID_SET, DBG_LOUD, ("N63C_SendAction_InvitationRsp failed\n"));
						p2p_IndicateActionFrameSendCompleteWithToken(pP2PInfo, P2P_EVENT_INVITATION_RESPONSE_SEND_COMPLETE, RT_STATUS_FAILURE, NULL, 0, 0);
					}
					InfoBufLen = 0;
				}
			}
			break;

		case P2P_EVENT_INVITATION_RESPONSE_SEND_COMPLETE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_INVITATION_RESPONSE_SEND_COMPLETE\n"));
			N63CIndicateInvitationResponseSendComplete(pP2PInfo, pInformation);
			break;

		case P2P_EVENT_RECEIVED_INVITATION_RESPONSE:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_RECEIVED_INVITATION_RESPONSE\n"));
			//N63CIndicateReceivedInvitationResponse(pP2PInfo, pInformation);
			break;
			
		case P2P_EVENT_GO_OPERATING_CHANNEL:
			RT_TRACE_F(COMP_INDIC | COMP_P2P, DBG_LOUD, ("P2P_EVENT_GO_OPERATING_CHANNEL\n"));
			N63CIndicateGOOperatingChannel(pP2PInfo, pInformation);
			break;
			
		case P2P_EVENT_NONE:
		default:
			RT_TRACE_F(COMP_P2P, DBG_TRACE, ("EventID (0x%08X) Unrecognized!\n", EventID));
			break;
	}
}


NDIS_STATUS
N63C_OID_DOT11_WFD_DEVICE_CAPABILITY(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DEVICE_CAPABILITY(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_GROUP_OWNER_CAPABILITY(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_GROUP_OWNER_CAPABILITY(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_DEVICE_INFO(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DEVICE_INFO(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SECONDARY_DEVICE_TYPE_LIST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_DISCOVER_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}

		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DISCOVER_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_GET_DIALOG_TOKEN(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_DOT11_WFD_GET_DIALOG_TOKEN(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_ENUM_DEVICE_LIST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_DOT11_WFD_ENUM_DEVICE_LIST(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_LISTEN_STATE_DISCOVERABILITY(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DEVICE_LISTEN_CHANNEL(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

	
NDIS_STATUS
N63C_OID_DOT11_WFD_ADDITIONAL_IE(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;

	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pTargetAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_ADDITIONAL_IE(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_FLUSH_DEVICE_LIST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_FLUSH_DEVICE_LIST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_STOP_DISCOVERY(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_STOP_DISCOVERY(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_GO_NEGOTIATION_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_GO_NEGOTIATION_RESPONSE(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);
	
	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_RESPONSE(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_GO_NEGOTIATION_CONFIRMATION(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus =N63C_SET_OID_DOT11_WFD_SEND_GO_NEGOTIATION_CONFIRM(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_INVITATION_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_INVITATION_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_INVITATION_RESPONSE(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_INVITATION_RESPONSE(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_RESPONSE(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_SEND_PROVISION_DISCOVERY_RESPONSE(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);
		
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_DESIRED_GROUP_ID(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DESIRED_GROUP_ID(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}


NDIS_STATUS
N63C_OID_DOT11_WFD_START_GO_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_START_GO_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));
	
	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_GROUP_START_PARAMETERS(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_GROUP_START_PARAMETERS(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_CONNECT_TO_GROUP_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));			
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_CONNECT_TO_GROUP_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_DISCONNECT_FROM_GROUP_REQUEST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!", __FUNCTION__));
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_DISCONNECT_FROM_GROUP_REQUEST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_WFD_GROUP_JOIN_PARAMETERS(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		if(ndisStatus != NDIS_STATUS_SUCCESS) {
			RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));			
			break;
		}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_WFD_GROUP_JOIN_PARAMETERS(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

//
//  Note : This OID not define in NDIS_63 .
//		 It may support in Vista or Win7 futher. 
//		So it may remove to N6CSet_ , if N6 support !!
//
//
static NDIS_STATUS
N63C_SET_OID_DOT11_OFFLOAD_NETWORK_LIST(
	IN	PADAPTER			pTargetAdapter,
	IN	NDIS_OID			Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	PDOT11_OFFLOAD_NETWORK_LIST_INFO pONLInfo = NULL;
	u4Byte							Index = 0;
	PADAPTER 						pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);
	PMGNT_INFO						pMgntInfo = &( pDefaultAdapter->MgntInfo);
	PRT_NDIS6_COMMON				pNdisCommon = pDefaultAdapter->pNdisCommon;
	PRT_NLO_INFO					pNLOInfo = &(pMgntInfo->NLOInfo);
	PDOT11_OFFLOAD_NETWORK			pONList;
	u4Byte							ChannelIndex = 0;
	NDIS_STATUS 						ndisStatus = NDIS_STATUS_SUCCESS;
	
	pONLInfo = (PDOT11_OFFLOAD_NETWORK_LIST_INFO) InformationBuffer;

	RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===>N63CSet_OID_DOT11_OFFLOAD_NETWORK_LIST\n") );

	if( InformationBufferLength <  (ULONG)DOT11_MIN_SIZEOF_OFFLOAD_NETWORK_LIST_INFO_REVISION_1)
	{
		ndisStatus = NDIS_STATUS_INVALID_LENGTH;
		*BytesNeeded = sizeof(DOT11_OFFLOAD_NETWORK_LIST_INFO);
		RT_TRACE_F( (COMP_MLME|COMP_OID_SET), DBG_LOUD , ("ndisStatus = NDIS_STATUS_INVALID_LENGTH\n") );
		return ndisStatus;
	}

	switch(pONLInfo->ulFlags)
	{
		case DOT11_NLO_FLAG_STOP_NLO_INDICATION:
			RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD, ("Set DOT11_NLO_FLAG_STOP_NLO_INDICATION\n"));
			break;
		case DOT11_NLO_FLAG_SCAN_ON_AOAC_PLATFORM:
			RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD, ("Set DOT11_NLO_FLAG_SCAN_ON_AOAC_PLATFORM\n"));
			break;
		case DOT11_NLO_FLAG_SCAN_AT_SYSTEM_RESUME:
			RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD, ("Set DOT11_NLO_FLAG_SCAN_AT_SYSTEM_RESUME\n"));
			break;
		default:
			break;
	}

	pNdisCommon->Oidcounter++;
	if( pONLInfo->uNumOfEntries == 0 )
	{
		// Stop NLO mode !!
		pNLOInfo->NumDot11OffloadNetwork = 0;
		pNLOInfo->FastScanIterations = 0;
		pNLOInfo->FastScanPeriod = 0;
		pNLOInfo->SlowScanPeriod = 0;
		pNdisCommon->ScanPeriod = 0;
		
		RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===>pONLInfo->uNumOfEntries == 0 \n") );
		return NDIS_STATUS_SUCCESS;
	}
	else
	{
		RT_PRINT_DATA(COMP_OID_SET, DBG_LOUD, "NLO INFO: \n", InformationBuffer, sizeof(DOT11_OFFLOAD_NETWORK_LIST_INFO));
		pNLOInfo->NumDot11OffloadNetwork =  pONLInfo->uNumOfEntries;
		pNLOInfo->FastScanIterations = pONLInfo->FastScanIterations;
		pNLOInfo->FastScanPeriod = pONLInfo->FastScanPeriod ;
		pNLOInfo->SlowScanPeriod = pONLInfo->SlowScanPeriod ;


		RT_TRACE(COMP_MLME , DBG_LOUD , (" NumDot11OffloadNetwork : %d\n", pNLOInfo->NumDot11OffloadNetwork) );
		RT_TRACE(COMP_MLME , DBG_LOUD , (" FastScanIterations : %d\n", pNLOInfo->FastScanIterations) );
		RT_TRACE(COMP_MLME , DBG_LOUD , (" FastScanPeriod : %d sec\n", pNLOInfo->FastScanPeriod) );
		RT_TRACE(COMP_MLME , DBG_LOUD , (" SlowScanPeriod : %d sec\n", pNLOInfo->SlowScanPeriod) );

		pNdisCommon->bNLOActiveScan 	= TRUE;
		pNdisCommon->bFilterHiddenAP 	= FALSE;

		// Start the NLO scan when the first PlatformHandleNLOnScanRequest is called
		pNdisCommon->ScanPeriod = pNLOInfo->FastScanPeriod;
	}


	if( pONLInfo->uNumOfEntries >  NATIVE_802_11_MAX_NETWORKOFFLOAD_SIZE)
	{
		ndisStatus = NDIS_STATUS_INVALID_LENGTH;
		return ndisStatus;
	}


	pONList = pONLInfo->offloadNetworkList;
	
	for( Index = 0 ; Index <  pONLInfo->uNumOfEntries ; Index++ )
	{
		// Copy SSID 
		CopySsid( pNLOInfo->dDot11OffloadNetworkList[Index].ssidbuf , 
				 pNLOInfo->dDot11OffloadNetworkList[Index].ssidlen,
				 pONList[Index].Ssid.ucSSID ,
				 pONList[Index].Ssid.uSSIDLength);

		RT_PRINT_STR(COMP_MLME, DBG_LOUD , ("N63CSet_OID_DOT11_OFFLOAD_NETWORK_LIST : SSID\n"), pNLOInfo->dDot11OffloadNetworkList[Index].ssidbuf, pNLOInfo->dDot11OffloadNetworkList[Index].ssidlen);

		// Set chuiper !!
		switch(  pONList[Index].UnicastCipher )
		{
			case DOT11_CIPHER_ALGO_NONE :
				pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_NONE;
				pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
				pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= 0;
				break;
			case DOT11_CIPHER_ALGO_WEP40 :
			case DOT11_CIPHER_ALGO_WEP104 :
			case DOT11_CIPHER_ALGO_WEP:
				pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_NONE;
				pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
				pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= 0x0;
				break;
			case DOT11_CIPHER_ALGO_TKIP :  
				if(	pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_WPA || 
				     	pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_WPA_PSK )
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA_TKIP;
				}
				else if( pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_RSNA || 
				     	    pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_RSNA_PSK )
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA2;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA2_TKIP;
				}else
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA_TKIP;
					RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===>NLO Not define TKIP\n") );
				}
				break;
			case DOT11_CIPHER_ALGO_CCMP :
				if(	pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_WPA || 
				     	pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_WPA_PSK )
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA_AES;
				}
				else if( pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_RSNA || 
				     	    pONList[Index].AuthAlgo == DOT11_AUTH_ALGO_RSNA_PSK )
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA2;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA2_AES;
				}
				else
				{
					pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_WPA2;
					pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
					pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= WPA2_AES;
					RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===>NLO Not define AES\n") );
				}
				break;
			default :
				pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl    	= RT_SEC_LVL_NONE;
				pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy 	= TRUE;
				pNLOInfo->dDot11OffloadNetworkList[Index].chiper	 	= 0;
				RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===> Unknow UnicastCipher : %x\n",pONList[Index].UnicastCipher) );
				break;
		}

		RT_TRACE(COMP_MLME , DBG_LOUD , (" Seclvl : %d\n", pNLOInfo->dDot11OffloadNetworkList[Index].SecLvl) );
		RT_TRACE(COMP_MLME , DBG_LOUD , (" bPrivacy : %d\n", pNLOInfo->dDot11OffloadNetworkList[Index].bPrivacy) );
		RT_TRACE(COMP_MLME , DBG_LOUD , (" chiper : %d\n", pNLOInfo->dDot11OffloadNetworkList[Index].chiper) );

		// Save channel info 
		// DOT11_MAX_CHANNEL_HINTS 4
		for(  ChannelIndex = 0 ;  ChannelIndex < 4 ;  ChannelIndex++ )
		{
			pNLOInfo->dDot11OffloadNetworkList[Index].channelNumberHit[ChannelIndex] = pONList[Index].Dot11ChannelHints[ChannelIndex].uChannelNumber;
		}
		
		// Show Entry information !! 
		RT_TRACE( (COMP_MLME|COMP_OID_SET), DBG_LOUD , (" ===> Show NLO info Index (%x):\n",Index) );
		RT_PRINT_STR((COMP_MLME|COMP_OID_SET), DBG_LOUD, (" SSID :\n"), pNLOInfo->dDot11OffloadNetworkList[Index].ssidbuf, pNLOInfo->dDot11OffloadNetworkList[Index].ssidlen);
		RT_TRACE( (COMP_MLME|COMP_OID_SET) , DBG_LOUD , ( "chiper : %x  \n", pNLOInfo->dDot11OffloadNetworkList[Index].chiper) )
		
	}

	return ndisStatus;
}

NDIS_STATUS
N63C_OID_DOT11_OFFLOAD_NETWORK_LIST(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		//ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		//if(ndisStatus != NDIS_STATUS_SUCCESS) {
		//	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));			
		//	break;
		//}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_OFFLOAD_NETWORK_LIST(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

static NDIS_STATUS
N63C_SET_OID_DOT11_POWER_MGMT_MODE_AUTO_ENABLED(
	IN	PADAPTER			pTargetAdapter,
	IN	NDIS_OID			Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesRead,
	OUT	PULONG			BytesNeeded
)
{
	/*
	#define DOT11_POWER_MGMT_AUTO_MODE_ENABLED_REVISION_1 
	#define DOT11_SIZEOF_POWER_MGMT_AUTO_MODE_ENABLED_REVISION_1 
	typedef struct _ DOT11_POWER_MGMT_MODE_AUTO_INFO { 
	 		NDIS_OBJECT_HEADER Header; 
			BOOLEAN bEnabled; 
	} DOT11_POWER_MGMT_AUTO_MODE_ENABLED_INFO, * PDOT11_POWER_MGMT_AUTO_MODE_ENABLED_INFO; 
*/
	PDOT11_POWER_MGMT_AUTO_MODE_ENABLED_INFO	pPMGAuModInf;
	PADAPTER 									pDefaultAdapter = GetDefaultAdapter(pTargetAdapter);
	PMGNT_INFO									pMgntInfo = &( pDefaultAdapter->MgntInfo);


	if( InformationBufferLength  < DOT11_SIZEOF_POWER_MGMT_AUTO_MODE_ENABLE_INFO_REVISION_1  )
	{
		*BytesNeeded = DOT11_SIZEOF_POWER_MGMT_AUTO_MODE_ENABLE_INFO_REVISION_1;
		return NDIS_STATUS_INVALID_LENGTH;
		
	}
	
	pPMGAuModInf = (PDOT11_POWER_MGMT_AUTO_MODE_ENABLED_INFO)InformationBuffer;


	pMgntInfo->bInAutoPowerSavemode = pPMGAuModInf->bEnabled;

	RT_TRACE( COMP_TEST, DBG_LOUD , ("===>OID_DOT11_POWERMGMT_MODE_AUTO_ENABLED =  %d \n",pMgntInfo->bInAutoPowerSavemode) );
	return NDIS_STATUS_SUCCESS;
}


NDIS_STATUS
N63C_OID_DOT11_POWER_MGMT_MODE_AUTO_ENABLED(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		//ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		//if(ndisStatus != NDIS_STATUS_SUCCESS) {
		//	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));			
		//	break;
		//}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = N63C_SET_OID_DOT11_POWER_MGMT_MODE_AUTO_ENABLED(
						pTargetAdapter,
						NdisRequest->DATA.SET_INFORMATION.Oid,
						NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
						NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
						(PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded
					);
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}


static NDIS_STATUS
N63C_QUERY_OID_DOT11_POWER_MGMT_MODE_STATUS(
	IN	PADAPTER		pTargetAdapter,
	IN	NDIS_OID		Oid,
	IN	PVOID			InformationBuffer,
	IN	ULONG			InformationBufferLength,
	OUT	PULONG			BytesWritten,
	OUT	PULONG			BytesNeeded
)
{
	NDIS_STATUS		ndisStatus = NDIS_STATUS_SUCCESS;
	PDOT11_POWER_MGMT_MODE_STATUSINFO		pDot11PMStaInfo = NULL;
	PMGNT_INFO	pMgntInfo = &pTargetAdapter->MgntInfo;
	PRT_POWER_SAVE_CONTROL	pPSC = GET_POWER_SAVE_CONTROL(pMgntInfo);

				
	// Clean output variables -----------------------------------
	*BytesWritten = 0;
	*BytesNeeded = 0;
	//-------------------------------------------------------



	// This has errors. Please contact CCW. 
	return NDIS_STATUS_FAILURE;


		
	if( !pMgntInfo->bInAutoPowerSavemode )
	{
		pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_active;
		pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_NO_POWER_SAVING;
		pDot11PMStaInfo->Reason			= dot11_power_mode_reason_no_change;
	}
	else 
	{
		if(pMgntInfo->dot11PowerSaveMode == eActive)
		//if( pPSC->PowerSaveLevel == POWER_SAVING_NO_POWER_SAVING)
		{
			pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_active;
			pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_NO_POWER_SAVING;
			pDot11PMStaInfo->Reason			= dot11_power_mode_reason_no_change;
		}
		else if( pMgntInfo->dot11PowerSaveMode == eMaxPs )
		//else if( pPSC->PowerSaveLevel == POWER_SAVING_MAX_PSP )
		{
			pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_powersave;
			pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_MAX_PSP;
			pDot11PMStaInfo->Reason			= dot11_power_mode_reason_compliant_AP;
		}
		else if( pMgntInfo->dot11PowerSaveMode == eFastPs )
		//else if( pPSC->PowerSaveLevel == POWER_SAVING_FAST_PSP  )
		{
			pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_powersave;
			pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_FAST_PSP;
			pDot11PMStaInfo->Reason			= dot11_power_mode_reason_compliant_AP;
		}
		//else if(pPSC->PowerSaveLevel == POWER_SAVING_MAXIMUM_LEVEL )
		//{
		//	pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_powersave;
		//	pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_MAXIMUM_LEVEL;
		//	pDot11PMStaInfo->Reason			= dot11_power_mode_reason_compliant_AP;
		//}
		else 
		{
			pDot11PMStaInfo->PowerSaveMode 	= dot11_power_mode_powersave;
			pDot11PMStaInfo->uPowerSaveLevel 	= DOT11_POWER_SAVING_FAST_PSP;
			pDot11PMStaInfo->Reason			= dot11_power_mode_reason_compliant_AP;
		}
	}

	RT_TRACE( COMP_TEST, DBG_LOUD , ("===>_DOT11_POWER_MGMT_MODE_STATUS  \n") );
	
	return ndisStatus;
}


NDIS_STATUS
N63C_OID_DOT11_POWER_MGMT_MODE_STATUS(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		// Validate if the OID is issued in the correct state and mode
		//ndisStatus = N63CValidateOIDCorrectness(pAdapter, NdisRequest);
		//if(ndisStatus != NDIS_STATUS_SUCCESS) {
		//	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("%s: State Error!\n", __FUNCTION__));			
		//	break;
		//}
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_DOT11_POWER_MGMT_MODE_STATUS(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
}

NDIS_STATUS
N63C_OID_PACKET_COALESCING_FILTER_MATCH_COUNT(
	PADAPTER pAdapter,
	PNDIS_OID_REQUEST NdisRequest
)
{
	NDIS_STATUS ndisStatus = NDIS_STATUS_INVALID_OID;
	
	// Adapter selection
	PADAPTER pDefaultAdapter = GetDefaultAdapter(pAdapter);
	PADAPTER pTargetAdapter = GetAdapterByPortNum(pAdapter, (u1Byte)NdisRequest->PortNumber);

	FunctionIn(COMP_OID_SET | COMP_OID_QUERY);

	do
	{
		
		switch (NdisRequest->RequestType)
		{
			// Query
			case NdisRequestQueryInformation:
			case NdisRequestQueryStatistics:
				ndisStatus = N63C_QUERY_OID_PACKET_COALESCING_FILTER_MATCH_COUNT(
						pTargetAdapter,
						NdisRequest->DATA.QUERY_INFORMATION.Oid,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
						NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
						(PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded 
					);
				break;
			
			// Set
			case NdisRequestSetInformation:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			// Method
			case NdisRequestMethod:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

			default:
				ndisStatus = NDIS_STATUS_NOT_SUPPORTED;
				break;

		}
	}while(FALSE);

	RT_TRACE((COMP_OID_SET | COMP_OID_QUERY), DBG_LOUD, ("ndisStatus: %d\n", ndisStatus));

	FunctionOut(COMP_OID_SET | COMP_OID_QUERY);
	
	return ndisStatus;
	
}


<|start_filename|>network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp<|end_filename|>
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   Copyright (c) 2014 Microsoft Corporation.  All Rights Reserved.
//
//   Module Name:
//      ClassifyFunctions_BasicPacketModificationCallouts.cpp
//
//   Abstract:
//      This module contains WFP Classify functions for modifying and injecting packets back into 
//         the data path using the clone / block / inject method.
//
//   Naming Convention:
//
//      <Module><Scenario>
//  
//      i.e.
//       ClassifyBasicPacketModification
//
//       <Module>
//          Classify                - Function is an FWPS_CALLOUT_CLASSIFY_FN
//       <Scenario>
//          BasicPacketModification - Function demonstrates the clone / block / modify / inject 
//                                       model.
//
//      <Action><Scenario><Modifier>
//
//      i.e.
//       TriggerBasicPacketModificationOutOfBand
//
//       <Action>
//        {
//                                    -
//          Trigger                   - Initiates the desired scenario.
//          Perform                   - Executes the desired scenario.
//        }
//       <Scenario>
//          BasicPacketModification   - Function demonstrates the clone / block / modify / inject 
//                                         model.
//       <Modifier>
//          DeferredProcedureCall     - DPC routine for Out of Band injection which dispatches the 
//                                         proper Perform Function.
//          WorkItemRoutine           - WorkItem Routine for Out of Band Injection which dispatches
//                                         the proper Perform Function.
//          AtInboundMACFrame         - Function operates on:
//                                         FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, and 
//                                         FWPM_LAYER_INBOUND_MAC_NATIVE.
//          AtOutboundMACFrame        - Function operates on:
//                                         FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, and 
//                                         FWPM_LAYER_OUTBOUND_MAC_NATIVE.
//          AtEgressVSwitchEthernet   - Function operates on:
//                                         FWPM_LAYER_EGRESS_VSWITCH_ETHERNET.
//          AtIngressVSwitchEthernet  - Function operates on:
//                                         FWPM_LAYER_INGRESS_VSWITCH_ETHERNET.
//          AtInboundNetwork          - Function operates on:
//                                         FWPM_LAYER_INBOUND_IPPACKET_V{4/6}
//          AtOutboundNetwork         - Function operates on:
//                                         FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}
//          AtForward                 - Function operates on:
//                                         FWPM_LAYER_IPFORWARD_V{4/6}
//          AtInboundTransport        - Function operates on: 
//                                         FWPM_LAYER_INBOUND_TRANSPORT_V{4/6},
//                                         FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6},
//                                         FWPM_LAYER_DATAGRAM_DATA_V{4/6},
//                                         FWPM_LAYER_STREAM_PACKET_V{4/6}, and 
//                                         FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}
//                                         FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
//          AtOutboundTransport       - Function operates on:
//                                         FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6},
//                                         FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6},
//                                         FWPM_LAYER_DATAGRAM_DATA_V{4/6},
//                                         FWPM_LAYER_STREAM_PACKET_V{4/6}, and 
//                                         FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}
//                                         FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
//
//   Private Functions:
//      BasicPacketModificationDeferredProcedureCall(),
//      BasicPacketModificationWorkItemRoutine(),
//      PerformBasicPacketModificationAtEgressVSwitchEthernet(),
//      PerformBasicPacketModificationAtForward(),
//      PerformBasicPacketModificationAtInboundMACFrame(),
//      PerformBasicPacketModificationAtInboundNetwork(),
//      PerformBasicPacketModificationAtInboundTransport(),
//      PerformBasicPacketModificationAtIngressVSwitchEthernet(),
//      PerformBasicPacketModificationAtOutboundMACFrame(),
//      PerformBasicPacketModificationAtOutboundNetwork(),
//      PerformBasicPacketModificationAtOutboundTransport(),
//      TriggerBasicPacketModificationInline(),
//      TriggerBasicPacketModificationOutOfBand(),
//
//   Public Functions:
//      ClassifyBasicPacketModification(),
//
//   Author:
//      <NAME>      (DHarper)
//
//   Revision History:
//
//      [ Month ][Day] [Year] - [Revision]-[ Comments ]
//      May       01,   2010  -     1.0   -  Creation
//      December  13,   2013  -     1.1   -  Enhance function declaration for IntelliSense, enhance 
//                                              traces, fix weakhost injection, fix expected 
//                                              offsets,fix copy / paste issues with modifying dst 
//                                              port, and add support for multiple injectors and 
//                                              controlData.
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Framework_WFPSamplerCalloutDriver.h"                   /// .
#include "ClassifyFunctions_BasicPacketModificationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ 

#if(NTDDI_VERSION >= NTDDI_WIN8)

/**
 @private_function="PerformBasicPacketModificationAtInboundMACFrame"
 
   Purpose:  Clones the NET_BUFFER_LIST, modifies it with data from the associated context and 
             injects the clone back to the stack's inbound path from the incoming MAC Layers 
             using FwpsInjectMacReceiveAsync().                                                 <br>
                                                                                                <br>
   Notes:    Applies to the following inbound layers:                                           <br>
                FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET                                           <br>
                FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE                                             <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtInboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData,
                                                         _In_ INJECTION_DATA** ppInjectionData,
                                                         _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                         _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtInboundMACFrame()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata       = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   IF_INDEX                                   interfaceIndex  = 0;
   NDIS_PORT_NUMBER                           ndisPort        = 0;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;
   UINT32                                     bytesRetreated  = 0;
   FWP_VALUE*                                 pInterfaceIndex = 0;
   FWP_VALUE*                                 pNDISPort       = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_INTERFACE_INDEX);
   if(pInterfaceIndex &&
      pInterfaceIndex->type == FWP_UINT32)
      interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32;

   pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                         &FWPM_CONDITION_NDIS_PORT);
   if(pNDISPort &&
      pNDISPort->type == FWP_UINT32)
      ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32;

   /// If NATIVE, initial offset is at the MAC Header ...
   if(pClassifyValues->layerId != FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE &&
      FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE))
      bytesRetreated = pMetadata->ethernetMacHeaderSize;

   if(bytesRetreated)
   {
      /// ... otherwise the offset is at the IP Header, so retreat the size of the MAC Header ...
      status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                             bytesRetreated,
                                             0,
                                             0);
      if(status != STATUS_SUCCESS)
      {
         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                    status);

         HLPR_BAIL;
      }
   }

   /// ... clone the entire NET_BUFFER_LIST ...
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);

   if(bytesRetreated)
   {
      /// ... and advance the offset back to the original position.
      NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                    bytesRetreated,
                                    FALSE,
                                    0);
   }

   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// No Transport Modification if IPsec encrypted
         if(protocol != IPPROTO_ESP &&
            protocol != IPPROTO_AH)
         {
            /// ... advance by the size of the IP Header.
            NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                          ipHeaderSize,
                                          FALSE,
                                          0);

            switch(protocol)
            {
               case IPPROTO_ICMP:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
                  
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_TCP:
               {
                  UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     tcpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_UDP:
               {
                  UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     udpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_ICMPV6:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
            
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
            }

            /// ToDo: Recalculate the Transport Checksum Here

            HLPR_BAIL_LABEL_2:

            /// return the data offset to the beginning of the IP Header
            tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                      ipHeaderSize,
                                                      0,
                                                      0);
            if(tmpStatus != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                          status);
            
               HLPR_BAIL;
            }

            HLPR_BAIL_ON_FAILURE(status);
         }

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         HLPR_BAIL_LABEL_3:

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }
      }
*/
      if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER)
      {
         if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            HLPR_NEW(value.byteArray6,
                     FWP_BYTE_ARRAY6,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);
            HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                       status);
            
            value.type = FWP_BYTE_ARRAY6_TYPE;
            
            RtlCopyMemory(value.byteArray6->byteArray6,
                          pModificationData->macData.pSourceMACAddress,
                          ETHERNET_ADDRESS_SIZE);

            status = KrnlHlprMACHeaderModifySourceAddress(&value,
                                                          pNetBufferList);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

            HLPR_NEW(value.byteArray6,
                     FWP_BYTE_ARRAY6,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);
            HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                       status);

#pragma warning(pop)
            
            value.type = FWP_BYTE_ARRAY6_TYPE;
            
            RtlCopyMemory(value.byteArray6->byteArray6,
                          pModificationData->macData.pDestinationMACAddress,
                          ETHERNET_ADDRESS_SIZE);

            status = KrnlHlprMACHeaderModifyDestinationAddress(&value,
                                                               pNetBufferList);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }
      }
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList,
                                                              TRUE);

   status = FwpsInjectMacReceiveAsync(pCompletionData->pInjectionData->injectionHandle,
                                      pCompletionData->pInjectionData->injectionContext,
                                      0,
                                      pClassifyValues->layerId,
                                      interfaceIndex,
                                      ndisPort,
                                      pNetBufferList,
                                      CompleteBasicPacketModification,
                                      pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsInjectMacReceiveAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtInboundMACFrame() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketInjectionAtOutboundMACFrame"
 
   Purpose:  Clones the NET_BUFFER_LIST and injects the clone back to the stack from the 
             outgoing MAC Layers using FwpsInjectMacSendAsync().                                <br>
                                                                                                <br>
   Notes:    Applies to the following inbound layers:                                           <br>
                FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET                                          <br>
                FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE                                            <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtOutboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData,
                                                          _In_ INJECTION_DATA** ppInjectionData,
                                                          _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                          _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtOutboundMACFrame()\n");

#endif ///  DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   IF_INDEX                                   interfaceIndex  = 0;
   NDIS_PORT_NUMBER                           ndisPort        = 0;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;
   FWP_VALUE*                                 pInterfaceIndex = 0;
   FWP_VALUE*                                 pNDISPort       = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_INTERFACE_INDEX);
   if(pInterfaceIndex &&
      pInterfaceIndex->type == FWP_UINT32)
      interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32;

   pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                         &FWPM_CONDITION_NDIS_PORT);
   if(pNDISPort &&
      pNDISPort->type == FWP_UINT32)
      ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32;

   /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList,
                                                              TRUE);

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available.
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// No Transport Modification if IPsec encrypted
         if(protocol != IPPROTO_ESP &&
            protocol != IPPROTO_AH)
         {
            /// ... advance by the size of the IP Header.
            NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                          ipHeaderSize,
                                          FALSE,
                                          0);

            switch(protocol)
            {
               case IPPROTO_ICMP:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
                  
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_TCP:
               {
                  UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     tcpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_UDP:
               {
                  UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     udpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_ICMPV6:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
            
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
            }

            /// ToDo: Recalculate the Transport Checksum Here

            HLPR_BAIL_LABEL_2:

            /// return the data offset to the beginning of the IP Header
            tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                      ipHeaderSize,
                                                      0,
                                                      0);
            if(tmpStatus != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtOutboundMacFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                          status);
            
               HLPR_BAIL;
            }

            HLPR_BAIL_ON_FAILURE(status);
         }

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         HLPR_BAIL_LABEL_3:

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }
      }
*/
         if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER)
         {
            if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS)
            {
               FWP_VALUE value;
   
               RtlZeroMemory(&value,
                             sizeof(FWP_VALUE));
   
               HLPR_NEW(value.byteArray6,
                        FWP_BYTE_ARRAY6,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                          status);
               
               value.type = FWP_BYTE_ARRAY6_TYPE;
               
               RtlCopyMemory(value.byteArray6->byteArray6,
                             pModificationData->macData.pSourceMACAddress,
                             ETHERNET_ADDRESS_SIZE);
   
               status = KrnlHlprMACHeaderModifySourceAddress(&value,
                                                             pNetBufferList);
   
               KrnlHlprFwpValuePurgeLocalCopy(&value);
   
               HLPR_BAIL_ON_FAILURE(status);
            }
   
            if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS)
            {
               FWP_VALUE value;
   
               RtlZeroMemory(&value,
                             sizeof(FWP_VALUE));
   
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray6,
                        FWP_BYTE_ARRAY6,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                          status);

#pragma warning(pop)
               
               value.type = FWP_BYTE_ARRAY6_TYPE;
               
               RtlCopyMemory(value.byteArray6->byteArray6,
                             pModificationData->macData.pDestinationMACAddress,
                             ETHERNET_ADDRESS_SIZE);
   
               status = KrnlHlprMACHeaderModifyDestinationAddress(&value,
                                                                  pNetBufferList);
   
               KrnlHlprFwpValuePurgeLocalCopy(&value);
   
               HLPR_BAIL_ON_FAILURE(status);
            }
         }
      }

   status = FwpsInjectMacSendAsync(pCompletionData->pInjectionData->injectionHandle,
                                   pCompletionData->pInjectionData->injectionContext,
                                   0,
                                   pClassifyValues->layerId,
                                   interfaceIndex,
                                   ndisPort,
                                   pNetBufferList,
                                   CompleteBasicPacketModification,
                                   pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsInjectMacSendAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtOutboundMACFrame() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtIngressVSwitchEthernet"
 
   Purpose:  Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's 
             ingress path from the ingress VSwitch Layers using 
             FwpsInjectvSwitchEthernetIngressAsync0().                                          <br>
                                                                                                <br>
   Notes:    Applies to the following ingress layers:                                           <br>
                FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                                             <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtIngressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData,
                                                                _In_ INJECTION_DATA** ppInjectionData,
                                                                _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                                _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtIngressVSwitchEthernet()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata       = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   FWP_VALUE*                                 pVSwitchIDValue = 0;
   FWP_BYTE_BLOB*                             pVSwitchID      = 0;
   NDIS_SWITCH_PORT_ID                        sourcePortID    = 0;
   NDIS_SWITCH_NIC_INDEX                      sourceNICIndex  = 0;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID))
      sourcePortID = pMetadata->vSwitchSourcePortId;

   if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX))
      sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex;

   pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_VSWITCH_ID);
   if(pVSwitchIDValue)
      pVSwitchID = pVSwitchIDValue->byteBlob;

   if(pVSwitchID == 0)
   {
      status = STATUS_INVALID_MEMBER;

       DbgPrintEx(DPFLTR_IHVNETWORK_ID,
           DPFLTR_ERROR_LEVEL,
           " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n",
           status,
           pVSwitchID);

      HLPR_BAIL;
   }

   /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList,
                                                              TRUE);

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available.
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// No Transport Modification if IPsec encrypted
         if(protocol != IPPROTO_ESP &&
            protocol != IPPROTO_AH)
         {
            /// ... advance by the size of the IP Header.
            NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                          ipHeaderSize,
                                          FALSE,
                                          0);

            switch(protocol)
            {
               case IPPROTO_ICMP:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
                  
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_TCP:
               {
                  UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     tcpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_UDP:
               {
                  UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     udpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_ICMPV6:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
            
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
            }

            /// ToDo: Recalculate the Transport Checksum Here

            HLPR_BAIL_LABEL_2:

            /// return the data offset to the beginning of the IP Header
            tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                      ipHeaderSize,
                                                      0,
                                                      0);
            if(tmpStatus != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                          status);
            
               HLPR_BAIL;
            }

            HLPR_BAIL_ON_FAILURE(status);
         }

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         HLPR_BAIL_LABEL_3:

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }
      }
*/
         if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER)
         {
            if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS)
            {
               FWP_VALUE value;
   
               RtlZeroMemory(&value,
                             sizeof(FWP_VALUE));
   
               HLPR_NEW(value.byteArray6,
                        FWP_BYTE_ARRAY6,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                          status);
               
               value.type = FWP_BYTE_ARRAY6_TYPE;
               
               RtlCopyMemory(value.byteArray6->byteArray6,
                             pModificationData->macData.pSourceMACAddress,
                             ETHERNET_ADDRESS_SIZE);
   
               status = KrnlHlprMACHeaderModifySourceAddress(&value,
                                                             pNetBufferList);
   
               KrnlHlprFwpValuePurgeLocalCopy(&value);
   
               HLPR_BAIL_ON_FAILURE(status);
            }
   
            if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS)
            {
               FWP_VALUE value;
   
               RtlZeroMemory(&value,
                             sizeof(FWP_VALUE));
   
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray6,
                        FWP_BYTE_ARRAY6,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                          status);

#pragma warning(pop)

               value.type = FWP_BYTE_ARRAY6_TYPE;
               
               RtlCopyMemory(value.byteArray6->byteArray6,
                             pModificationData->macData.pDestinationMACAddress,
                             ETHERNET_ADDRESS_SIZE);
   
               status = KrnlHlprMACHeaderModifyDestinationAddress(&value,
                                                                  pNetBufferList);
   
               KrnlHlprFwpValuePurgeLocalCopy(&value);
   
               HLPR_BAIL_ON_FAILURE(status);
            }
         }
      }


   status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle,
                                                  pCompletionData->pInjectionData->injectionContext,
                                                  0,
                                                  0,
                                                  pVSwitchID,
                                                  sourcePortID,
                                                  sourceNICIndex,
                                                  pNetBufferList,
                                                  CompleteBasicPacketModification,
                                                  pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtEgressVSwitchEthernet"
 
   Purpose:  Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's 
             ingress path from the egress VSwitch Layers using 
             FwpsInjectvSwitchEthernetIngressAsync0().                                          <br>
                                                                                                <br>
   Notes:    Applies to the following egress layers:                                            <br>
                FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                                              <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtEgressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData,
                                                               _In_ INJECTION_DATA** ppInjectionData,
                                                               _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                               _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtEgressVSwitchEthernet()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata       = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   FWP_VALUE*                                 pVSwitchIDValue = 0;
   FWP_BYTE_BLOB*                             pVSwitchID      = 0;
   NDIS_SWITCH_PORT_ID                        sourcePortID    = 0;
   NDIS_SWITCH_NIC_INDEX                      sourceNICIndex  = 0;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID))
      sourcePortID = pMetadata->vSwitchSourcePortId;

   if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX))
      sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex;

   pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_VSWITCH_ID);
   if(pVSwitchIDValue)
      pVSwitchID = pVSwitchIDValue->byteBlob;

   if(pVSwitchID == 0)
   {
      status = STATUS_INVALID_MEMBER;

       DbgPrintEx(DPFLTR_IHVNETWORK_ID,
           DPFLTR_ERROR_LEVEL,
           " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n",
           status,
           pVSwitchID);

      HLPR_BAIL;
   }

   /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList,
                                                              TRUE);

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available.
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// No Transport Modification if IPsec encrypted
         if(protocol != IPPROTO_ESP &&
            protocol != IPPROTO_AH)
         {
            /// ... advance by the size of the IP Header.
            NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                          ipHeaderSize,
                                          FALSE,
                                          0);

            switch(protocol)
            {
               case IPPROTO_ICMP:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
                  
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_TCP:
               {
                  UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     tcpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     tcpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_UDP:
               {
                  UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     udpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
                  {
                     FWP_VALUE0 srcPort;
            
                     srcPort.type   = FWP_UINT16;
                     srcPort.uint16 = pModificationData->transportData.sourcePort;
            
                     status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                                pNetBufferList,
                                                                udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
                  {
                     FWP_VALUE0 dstPort;
            
                     dstPort.type   = FWP_UINT16;
                     dstPort.uint16 = pModificationData->transportData.destinationPort;
            
                     status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                     pNetBufferList,
                                                                     udpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
               case IPPROTO_ICMPV6:
               {
                  UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
            
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                     icmpHeaderSize = pMetadata->transportHeaderSize;
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
                  {
                     FWP_VALUE0 icmpType;
            
                     icmpType.type  = FWP_UINT8;
                     icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
            
                     status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
                  {
                     FWP_VALUE0 icmpCode;
            
                     icmpCode.type  = FWP_UINT8;
                     icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
            
                     status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                             pNetBufferList,
                                                             icmpHeaderSize);
                     HLPR_BAIL_ON_FAILURE_2(status);
                  }
            
                  break;
               }
            }

            /// ToDo: Recalculate the Transport Checksum Here

            HLPR_BAIL_LABEL_2:

            /// return the data offset to the beginning of the IP Header
            tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                      ipHeaderSize,
                                                      0,
                                                      0);
            if(tmpStatus != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                          status);
            
               HLPR_BAIL;
            }

            HLPR_BAIL_ON_FAILURE(status);
         }

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header...
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ethernetHeaderSize,
                                       FALSE,
                                       0);

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16,
                                                     status,
                                                     HLPR_BAIL_LABEL_3);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE_WITH_LABEL(status,
                                            HLPR_BAIL_LABEL_3);
         }

         HLPR_BAIL_LABEL_3:

         /// return the data offset to the beginning of the Ethernet Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }
      }
*/
      if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER)
      {
         if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            HLPR_NEW(value.byteArray6,
                     FWP_BYTE_ARRAY6,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);
            HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                       status);
            
            value.type = FWP_BYTE_ARRAY6_TYPE;
            
            RtlCopyMemory(value.byteArray6->byteArray6,
                          pModificationData->macData.pSourceMACAddress,
                          ETHERNET_ADDRESS_SIZE);

            status = KrnlHlprMACHeaderModifySourceAddress(&value,
                                                          pNetBufferList);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

            HLPR_NEW(value.byteArray6,
                     FWP_BYTE_ARRAY6,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);
            HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6,
                                       status);

#pragma warning(pop)
            
            value.type = FWP_BYTE_ARRAY6_TYPE;
            
            RtlCopyMemory(value.byteArray6->byteArray6,
                          pModificationData->macData.pDestinationMACAddress,
                          ETHERNET_ADDRESS_SIZE);

            status = KrnlHlprMACHeaderModifyDestinationAddress(&value,
                                                               pNetBufferList);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }
      }
   }

   status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle,
                                                  pCompletionData->pInjectionData->injectionContext,
                                                  0,
                                                  0,
                                                  pVSwitchID,
                                                  sourcePortID,
                                                  sourceNICIndex,
                                                  pNetBufferList,
                                                  CompleteBasicPacketModification,
                                                  pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}


#endif // (NTDDI_VERSION >= NTDDI_WIN8)

/**
 @private_function="PerformBasicPacketModificationAtInboundNetwork"
 
   Purpose:  Clones the NET_BUFFER_LIST and injects the clone back to the stack from the 
             incoming Network Layers using FwpsInjectNetworkReceiveAsync().                     <br>
                                                                                                <br>
   Notes:    Applies to the following inbound layers:                                           <br>
                FWPM_LAYER_INBOUND_IPPACKET_V{4/6}                                              <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData,
                                                        _In_ INJECTION_DATA** ppInjectionData,
                                                        _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                        _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtInboundNetwork()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status              = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues     = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata           = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   COMPARTMENT_ID                             compartmentID       = DEFAULT_COMPARTMENT_ID;
   IF_INDEX                                   interfaceIndex      = 0;
   IF_INDEX                                   subInterfaceIndex   = 0;
   UINT32                                     flags               = 0;
   NET_BUFFER_LIST*                           pNetBufferList      = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData     = 0;
   UINT32                                     ipHeaderSize        = 0;
   UINT32                                     bytesRetreated      = 0;
   UINT64                                     endpointHandle      = 0;
   IPPROTO                                    protocol            = IPPROTO_MAX;
   FWP_VALUE*                                 pInterfaceIndex     = 0;
   FWP_VALUE*                                 pSubInterfaceIndex  = 0;
   FWP_VALUE*                                 pFlags              = 0;
   BYTE*                                      pSourceAddress      = 0;
   BYTE*                                      pDestinationAddress = 0;
   NDIS_TCP_IP_CHECKSUM_PACKET_INFO           checksumInfo        = {0};

#if DBG

   KIRQL                                      irql               = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_COMPARTMENT_ID))
      compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE))
      endpointHandle = pMetadata->transportEndpointHandle;

   pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_INTERFACE_INDEX);
   if(pInterfaceIndex &&
      pInterfaceIndex->type == FWP_UINT32)
      interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32;

   pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                  &FWPM_CONDITION_SUB_INTERFACE_INDEX);
   if(pSubInterfaceIndex &&
      pSubInterfaceIndex->type == FWP_UINT32)
      subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32;

   pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                      &FWPM_CONDITION_FLAGS);
   if(pFlags &&
      pFlags->type == FWP_UINT32)
      flags = pFlags->uint32;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_IP_HEADER_SIZE))
      bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize;

   checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                                               TcpIpChecksumNetBufferListInfo);

   /// Initial offset is at the Transport Header, so retreat the size of the IP Header ...
   status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                          bytesRetreated,
                                          0,
                                          0);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   /// ... clone the entire NET_BUFFER_LIST ...
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);

   /// ... and advance the offset back to the original position.
   NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                 bytesRetreated,
                                 FALSE,
                                 0);

   if(status != STATUS_SUCCESS ||
      !pNetBufferList)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   /// Handle if this packet had the IP checksum offloaded or if it's loopback
   if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded ||
      flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
   {
      /// Prevent TCP/IP Zone crossing and recalculate the checksums
      if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
      {
         FWP_VALUE* pLocalAddress    = 0;
         FWP_VALUE* pRemoteAddress   = 0;
         FWP_VALUE* pLoopbackAddress = 0;

         pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_REMOTE_ADDRESS);
         if(pLocalAddress &&
            ((pLocalAddress->type == FWP_UINT32 &&
            RtlCompareMemory(&(pLocalAddress->uint32),
                             IPV4_LOOPBACK_ADDRESS,
                             IPV4_ADDRESS_SIZE)) ||
            (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE &&
            RtlCompareMemory(pLocalAddress->byteArray16->byteArray16,
                             IPV6_LOOPBACK_ADDRESS,
                             IPV6_ADDRESS_SIZE))))
            pLoopbackAddress = pLocalAddress;

         if(!pLoopbackAddress)
         {
            pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                       &FWPM_CONDITION_IP_REMOTE_ADDRESS);
            if(pRemoteAddress &&
               ((pRemoteAddress->type == FWP_UINT32 &&
               RtlCompareMemory(&(pRemoteAddress->uint32),
                                IPV4_LOOPBACK_ADDRESS,
                                IPV4_ADDRESS_SIZE)) ||
               (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
               RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16,
                                IPV6_LOOPBACK_ADDRESS,
                                IPV6_ADDRESS_SIZE))))
               pLoopbackAddress = pRemoteAddress;
         }

         if(pLoopbackAddress)
         {
            status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata,
                                                           pLoopbackAddress,
                                                           ipHeaderSize,
                                                           pNetBufferList,
                                                           (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                           pCompletionData->pInjectionData->controlDataLength);
            if(status != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n",
                          status);

               HLPR_BAIL;
            }
         }
      }
      else
      {
         /// Recalculate the checksum
         if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList,
                                                ipHeaderSize);
      }
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList);

   protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                               pCompletionData->pInjectionData->addressFamily);

   if(pModificationData->flags)
   {
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         NTSTATUS tmpStatus = STATUS_SUCCESS;

         /// The clone is at the IP Header, so advance by the size of the IP Header.
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ipHeaderSize,
                                       FALSE,
                                       0);

         switch(protocol)
         {
            case IPPROTO_ICMP:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
               
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_TCP:
            {
               UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  tcpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_UDP:
            {
               UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  udpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_ICMPV6:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
         
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
         }

         HLPR_BAIL_LABEL_2:

         /// return the data offset to the beginning of the IP Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         HLPR_BAIL_ON_FAILURE(status);
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX)
            interfaceIndex = pModificationData->ipData.interfaceIndex;

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

#pragma warning(pop)

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }
      }
   }

   /// Handle if this packet is destined for the software loopback
   if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
   {
      FWP_VALUE* pLocalAddress    = 0;
      FWP_VALUE* pRemoteAddress   = 0;
      FWP_VALUE* pLoopbackAddress = 0;

      pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                 &FWPM_CONDITION_IP_REMOTE_ADDRESS);
      if(pLocalAddress &&
         ((pLocalAddress->type == FWP_UINT32 &&
         RtlCompareMemory(&(pLocalAddress->uint32),
                          IPV4_LOOPBACK_ADDRESS,
                          IPV4_ADDRESS_SIZE)) ||
         (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE &&
         RtlCompareMemory(pLocalAddress->byteArray16->byteArray16,
                          IPV6_LOOPBACK_ADDRESS,
                          IPV6_ADDRESS_SIZE))))
         pLoopbackAddress = pLocalAddress;

      if(!pLoopbackAddress)
      {
         pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_REMOTE_ADDRESS);
         if(pRemoteAddress &&
            ((pRemoteAddress->type == FWP_UINT32 &&
            RtlCompareMemory(&(pRemoteAddress->uint32),
                             IPV4_LOOPBACK_ADDRESS,
                             IPV4_ADDRESS_SIZE)) ||
            (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
            RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16,
                             IPV6_LOOPBACK_ADDRESS,
                             IPV6_ADDRESS_SIZE))))
            pLoopbackAddress = pRemoteAddress;
      }

      if(pLoopbackAddress)
      {
         status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata,
                                                        pLoopbackAddress,
                                                        ipHeaderSize,
                                                        pNetBufferList,
                                                        (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                        pCompletionData->pInjectionData->controlDataLength);
         if(status != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }
      }
   }

   pSourceAddress = KrnlHlprIPHeaderGetSourceAddressField(pNetBufferList,
                                                          pCompletionData->pInjectionData->addressFamily);

   pDestinationAddress = KrnlHlprIPHeaderGetDestinationAddressField(pNetBufferList,
                                                                    pCompletionData->pInjectionData->addressFamily);

   status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList,
                                                    ipHeaderSize,
                                                    pCompletionData->pInjectionData->addressFamily,
                                                    (UCHAR*)pSourceAddress,
                                                    (UCHAR*)pDestinationAddress,
                                                    protocol,
                                                    endpointHandle,
                                                    (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                    pCompletionData->pInjectionData->controlDataLength,
                                                    0,
                                                    0,
                                                    interfaceIndex,
                                                    subInterfaceIndex);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                 status);
   
      HLPR_BAIL;
   }

   status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle,
                                          pCompletionData->pInjectionData->injectionContext,
                                          0,
                                          compartmentID,
                                          interfaceIndex,
                                          subInterfaceIndex,
                                          pNetBufferList,
                                          CompleteBasicPacketModification,
                                          pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsInjectNetworkReceiveAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtInboundNetwork() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtOutboundNetwork"
 
   Purpose:  Clones the NET_BUFFER_LIST and injects the clone back to the stack from the 
             outgoing Network Layers using FwpsInjectNetworkSendAsync().                        <br>
                                                                                                <br>
   Notes:    Applies to the following inbound layers:                                           <br>
                FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}                                             <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtOutboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData,
                                                         _In_ INJECTION_DATA** ppInjectionData,
                                                         _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                         _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtOutboundNetwork()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata       = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   COMPARTMENT_ID                             compartmentID   = DEFAULT_COMPARTMENT_ID;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_COMPARTMENT_ID))
      compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId;

   /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList);

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the Transport header at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available.
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// The clone is at the IP Header, so advance by the size of the IP Header.
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ipHeaderSize,
                                       FALSE,
                                       0);

         switch(protocol)
         {
            case IPPROTO_ICMP:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
               
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_TCP:
            {
               UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  tcpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_UDP:
            {
               UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  udpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_ICMPV6:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
         
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
         }

         /// ToDo: Recalculate the Transport Checksum Here

         HLPR_BAIL_LABEL_2:

         /// return the data offset to the beginning of the IP Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtOutboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         HLPR_BAIL_ON_FAILURE(status);
      }
*/
      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

#pragma warning(pop)

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }
      }
   }

   status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle,
                                       pCompletionData->pInjectionData->injectionContext,
                                       0,
                                       compartmentID,
                                       pNetBufferList,
                                       CompleteBasicPacketModification,
                                       pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsInjectNetworkSendAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtOutboundNetwork() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtForward"
 
   Purpose:  Clones the NET_BUFFER_LIST, modifies it with data from the associated context and 
             injects the clone back to the stack's forward path using FwpsInjectForwardAsync(). <br>
                                                                                                <br>
   Notes:    Applies to the following forwarding layers:                                        <br>
                FWPM_LAYER_IPFORWARD_V{4/6}                                                     <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtForward(_In_ CLASSIFY_DATA** ppClassifyData,
                                                 _In_ INJECTION_DATA** ppInjectionData,
                                                 _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                 _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtForward()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status            = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues   = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata         = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   COMPARTMENT_ID                             compartmentID     = DEFAULT_COMPARTMENT_ID;
   IF_INDEX                                   interfaceIndex    = 0;
   UINT32                                     flags             = 0;
   NET_BUFFER_LIST*                           pNetBufferList    = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData   = 0;
   UINT32                                     ipHeaderSize      = 0;
   FWP_VALUE*                                 pInterfaceIndex   = 0;
   FWP_VALUE*                                 pFlags            = 0;
   BOOLEAN                                    isWeakHostReceive = FALSE;
   BOOLEAN                                    isWeakHostSend    = FALSE;
   PSTR                                       pInjectionFn      = "FwpsInjectForwardAsync";
   NDIS_TCP_IP_CHECKSUM_PACKET_INFO           checksumInfo      = {0};

#if DBG

   KIRQL                                      irql              = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_COMPARTMENT_ID))
      compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_IP_HEADER_SIZE))
      ipHeaderSize = pMetadata->ipHeaderSize;

   pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX);
   if(pInterfaceIndex &&
      pInterfaceIndex->type == FWP_UINT32)
      interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32;

   pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                      &FWPM_CONDITION_FLAGS);
   if(pFlags &&
      pFlags->type == FWP_UINT32)
      flags = pFlags->uint32;

#if(NTDDI_VERSION >= NTDDI_WIN7)
   
      /// Determine if this is a weakhost forward
      if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU)
         isWeakHostReceive = TRUE;
   
      if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU)
         isWeakHostSend = TRUE;
   
#endif /// (NTDDI_VERSION >= NTDDI_WIN7)

   /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS ||
      !pNetBufferList)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtForward: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                                               TcpIpChecksumNetBufferListInfo);

   /// Handle if this packet had the IP checksum offloaded or if it's loopback
   if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded ||
      flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
   {
      /// Prevent TCP/IP Zone crossing and recalculate the checksums
      if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
      {
         FWP_VALUE* pLocalAddress    = 0;
         FWP_VALUE* pRemoteAddress   = 0;
         FWP_VALUE* pLoopbackAddress = 0;

         pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_REMOTE_ADDRESS);
         if(pLocalAddress &&
            ((pLocalAddress->type == FWP_UINT32 &&
            RtlCompareMemory(&(pLocalAddress->uint32),
                             IPV4_LOOPBACK_ADDRESS,
                             IPV4_ADDRESS_SIZE)) ||
            (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE &&
            RtlCompareMemory(pLocalAddress->byteArray16->byteArray16,
                             IPV6_LOOPBACK_ADDRESS,
                             IPV6_ADDRESS_SIZE))))
            pLoopbackAddress = pLocalAddress;

         if(!pLoopbackAddress)
         {
            pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                       &FWPM_CONDITION_IP_REMOTE_ADDRESS);
            if(pRemoteAddress &&
               ((pRemoteAddress->type == FWP_UINT32 &&
               RtlCompareMemory(&(pRemoteAddress->uint32),
                                IPV4_LOOPBACK_ADDRESS,
                                IPV4_ADDRESS_SIZE)) ||
               (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
               RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16,
                                IPV6_LOOPBACK_ADDRESS,
                                IPV6_ADDRESS_SIZE))))
               pLoopbackAddress = pRemoteAddress;
         }

         if(pLoopbackAddress)
         {
            status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata,
                                                           pLoopbackAddress,
                                                           ipHeaderSize,
                                                           pNetBufferList,
                                                           (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                           pCompletionData->pInjectionData->controlDataLength);
            if(status != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtForward: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n",
                          status);

               HLPR_BAIL;
            }
         }
      }
      else
      {
         /// Recalculate the checksum
         if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList,
                                                ipHeaderSize);
      }
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList);

   if(pModificationData->flags)
   {
      /// Various checks and balances must be performed to modify the Transport header at this modification point.
      /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be.
      /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers.
      /// The following block of code is to get you started with modifying the headers with info not readily available.
      /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present).
/*
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32  tmpStatus = STATUS_SUCCESS;
         IPPROTO protocol  = IPPROTO_MAX;

         protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList,
                                                     pCompletionData->pInjectionData->addressFamily);

         /// The clone is at the IP Header, so advance by the size of the IP Header.
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ipHeaderSize,
                                       FALSE,
                                       0);

         switch(protocol)
         {
            case IPPROTO_ICMP:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
               
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_TCP:
            {
               UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  tcpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_UDP:
            {
               UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  udpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_ICMPV6:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;
         
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
         
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
         }

         /// ToDo: Recalculate the Transport Checksum Here

         HLPR_BAIL_LABEL_2:

         /// return the data offset to the beginning of the IP Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtForward: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         HLPR_BAIL_ON_FAILURE(status);
      }
*/
      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX)
            interfaceIndex = pModificationData->ipData.interfaceIndex;

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

#pragma warning(pop)

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              TRUE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }
      }
   }

   /// If the Forwarded NBL is destined locally, inject using FwpsInjectNetworkReceiveAsync rather 
   /// than the traditional FwpsInjectForwardAsync otherwise STATUS_INVALID_PARAMETER will be 
   /// returned in the NBL.status and the injection fails.
   if(isWeakHostReceive)
   {
      UINT32     index              = WFPSAMPLER_INDEX;
      IF_INDEX   subInterfaceIndex  = 0;
      FWP_VALUE* pSubInterfaceIndex = 0;

      if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT)
         index = UNIVERSAL_INDEX;

      if(pCompletionData->pInjectionData->addressFamily == AF_INET)
         pCompletionData->pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index];
      else
         pCompletionData->pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index];

      pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                     &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX);
      if(pSubInterfaceIndex &&
         pSubInterfaceIndex->type == FWP_UINT32)
         subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32;

      status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle,
                                             pCompletionData->pInjectionData->injectionContext,
                                             0,
                                             compartmentID,
                                             interfaceIndex,
                                             subInterfaceIndex,
                                             pNetBufferList,
                                             CompleteBasicPacketInjection,
                                             pCompletionData);
   }
   /// If the Forwarded NBL is sourced locally, but another interface, inject using 
   /// FwpsInjectNetworkSendAsync rather than the traditional FwpsInjectForwardAsync otherwise 
   /// STATUS_INVALID_PARAMETER will be returned in the NBL.status and the injection fails
   else if(isWeakHostSend)
   {
      UINT32 index = WFPSAMPLER_INDEX;

      if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT)
         index = UNIVERSAL_INDEX;

      if(pCompletionData->pInjectionData->addressFamily == AF_INET)
         pCompletionData->pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index];
      else
         pCompletionData->pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index];

      status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle,
                                          pCompletionData->pInjectionData->injectionContext,
                                          0,
                                          compartmentID,
                                          pNetBufferList,
                                          CompleteBasicPacketInjection,
                                          pCompletionData);
   }
   else
      status = FwpsInjectForwardAsync(pCompletionData->pInjectionData->injectionHandle,
                                      pCompletionData->pInjectionData->injectionContext,
                                      0,
                                      pCompletionData->pInjectionData->addressFamily,
                                      compartmentID,
                                      interfaceIndex,
                                      pNetBufferList,
                                      CompleteBasicPacketModification,
                                      pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtForward: %s() [status: %#x]\n",
                 pInjectionFn,
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtForward() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtInboundTransport"
 
   Purpose:  Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and 
             injects the clone back to the stack's inbound path from the incoming Transport 
             Layers using FwpsInjectTransportRecveiveAsync().                                   <br>
                                                                                                <br>
   Notes:    Applies to the following inbound layers:                                           <br>
                FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}                                             <br>
                FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}                                            <br>
                FWPM_LAYER_DATAGRAM_DATA_V{4/6}        (Inbound only)                           <br>
                FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)                           <br>
                FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)                           <br>
                FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}     (Inbound, reauthorization only)          <br>
                FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only)                  <br>
                FWPM_LAYER_STREAM_PACKET_V{4/6}        (Inbound only)                           <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtInboundTransport(_In_ CLASSIFY_DATA** ppClassifyData,
                                                          _In_ INJECTION_DATA** ppInjectionData,
                                                          _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                          _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtInboundTransport()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status              = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues     = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata           = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   COMPARTMENT_ID                             compartmentID       = DEFAULT_COMPARTMENT_ID;
   IF_INDEX                                   interfaceIndex      = 0;
   IF_INDEX                                   subInterfaceIndex   = 0;
   UINT32                                     flags               = 0;
   NET_BUFFER_LIST*                           pNetBufferList      = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData     = 0;
   UINT32                                     ipHeaderSize        = 0;
   UINT32                                     transportHeaderSize = 0;
   UINT32                                     bytesRetreated      = 0;
   IPPROTO                                    protocol            = IPPROTO_MAX;
   FWP_VALUE*                                 pProtocol           = 0;
   FWP_VALUE*                                 pInterfaceIndex     = 0;
   FWP_VALUE*                                 pSubInterfaceIndex  = 0;
   FWP_VALUE*                                 pFlags              = 0;
   FWPS_PACKET_LIST_INFORMATION*              pPacketInformation  = 0;
   BOOLEAN                                    bypassInjection     = FALSE;
   UINT64                                     endpointHandle      = 0;
   BYTE*                                      pSourceAddress      = 0;
   BYTE*                                      pDestinationAddress = 0;
   NDIS_TCP_IP_CHECKSUM_PACKET_INFO           checksumInfo        = {0};

#if DBG

   KIRQL                                      irql                = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   HLPR_NEW(pPacketInformation,
            FWPS_PACKET_LIST_INFORMATION,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pPacketInformation,
                              status);
   pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_INTERFACE_INDEX);
   if(pInterfaceIndex &&
      pInterfaceIndex->type == FWP_UINT32)
      interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32;

   pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                  &FWPM_CONDITION_SUB_INTERFACE_INDEX);
   if(pSubInterfaceIndex &&
      pSubInterfaceIndex->type == FWP_UINT32)
      subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32;

   pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                      &FWPM_CONDITION_FLAGS);
   if(pFlags &&
      pFlags->type == FWP_UINT32)
      flags = pFlags->uint32;

   if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
      pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4)
      protocol = IPPROTO_ICMP;
   else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6)
      protocol = IPPROTO_ICMPV6;

#if(NTDDI_VERSION >= NTDDI_WIN7)

   else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)
      protocol = IPPROTO_TCP;

#endif /// (NTDDI_VERSION >= NTDDI_WIN7)

   else
   {
      pProtocol = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                            &FWPM_CONDITION_IP_PROTOCOL);
      HLPR_BAIL_ON_NULL_POINTER(pProtocol);

      protocol = (IPPROTO)pProtocol->uint8;
   }

   if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4)
   {
      ipHeaderSize = IPV4_HEADER_MIN_SIZE;
   
      if(protocol == IPPROTO_ICMP)
         transportHeaderSize = ICMP_HEADER_MIN_SIZE;
      else if(protocol == IPPROTO_TCP)
         transportHeaderSize = TCP_HEADER_MIN_SIZE;
      else if(protocol == IPPROTO_UDP)
         transportHeaderSize = UDP_HEADER_MIN_SIZE;
   }
   else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)
   {
      ipHeaderSize = IPV6_HEADER_MIN_SIZE;

      if(protocol == IPPROTO_ICMPV6)
         transportHeaderSize = ICMP_HEADER_MIN_SIZE;
      else if(protocol == IPPROTO_TCP)
         transportHeaderSize = TCP_HEADER_MIN_SIZE;
      else if(protocol == IPPROTO_UDP)
         transportHeaderSize = UDP_HEADER_MIN_SIZE;
   }

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_COMPARTMENT_ID))
      compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_IP_HEADER_SIZE) &&
      pMetadata->ipHeaderSize)
      ipHeaderSize = pMetadata->ipHeaderSize;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) &&
      pMetadata->transportHeaderSize)
      transportHeaderSize = pMetadata->transportHeaderSize;

   bytesRetreated = ipHeaderSize;

   if(protocol != IPPROTO_ICMP &&
      protocol != IPPROTO_ICMPV6)
   {
      if(!isInline &&
         protocol != IPPROTO_TCP &&
         !(protocol == IPPROTO_UDP &&
         flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) &&
         (pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6))
      {
         /// For asynchronous execution, the drop will cause the stack to continue processing on the 
         /// NBL for auditing purposes.  This processing retreats the NBL Offset to the Transport header.
         /// We need to take this into account because we only took a reference on the NBL.
      }
      else
      {
         if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                           FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
            bytesRetreated += transportHeaderSize;
      }
   }
   else
   {
      if(pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
         pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6)
      {
         if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                           FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
            bytesRetreated += transportHeaderSize;
      }
   }

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE))
      endpointHandle = pMetadata->transportEndpointHandle;

   /// Query to see if IPsec has applied tunnel mode SA's to this NET_BUFFER_LIST ...
   status = FwpsGetPacketListSecurityInformation((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                                 FWPS_PACKET_LIST_INFORMATION_QUERY_ALL_INBOUND,
                                                 pPacketInformation);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsGetPacketListSecurityInformation() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   /// ... if it has, then bypass the injection until the NET_BUFFER_LIST has come out of the tunnel
   if((pPacketInformation->ipsecInformation.inbound.isTunnelMode &&
      !(pPacketInformation->ipsecInformation.inbound.isDeTunneled)) ||
      pPacketInformation->ipsecInformation.inbound.isSecure)
   {
      bypassInjection = TRUE;

      HLPR_BAIL;
   }

   /// Initial offset is at the data, so retreat the size of the IP Header and Transport Header ...
   /// except for ICMP, offset is at the ICMP Header, so retreat the size of the IP Header ...
   status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                          bytesRetreated,
                                          0,
                                          0);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   /// ... clone the entire NET_BUFFER_LIST ...
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);

   /// ... and advance the offset back to the original position.
   NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket),
                                 bytesRetreated,
                                 FALSE,
                                 0);
   if(status != STATUS_SUCCESS ||
      !pNetBufferList)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                                               TcpIpChecksumNetBufferListInfo);

   /// Handle if the packet was IPsec secured
   if(pCompletionData->pInjectionData->isIPsecSecured)
   {
      /// For performance reasons, IPsec leaves the original ESP / AH information in the IP Header ...
      UINT32     headerIncludeSize   = 0;
      UINT32     ipv4Address         = 0;
      UINT32     addressSize         = 0;
      FWP_VALUE* pRemoteAddressValue = 0;
      FWP_VALUE* pLocalAddressValue  = 0;
      FWP_VALUE* pProtocolValue      = 0;

      pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                      &FWPM_CONDITION_IP_REMOTE_ADDRESS);
      if(pRemoteAddressValue)
      {
         if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE)
            addressSize = IPV6_ADDRESS_SIZE;
         else
            addressSize = IPV4_ADDRESS_SIZE;               

         HLPR_NEW_ARRAY(pSourceAddress,
                        BYTE,
                        addressSize,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
         HLPR_BAIL_ON_ALLOC_FAILURE(pSourceAddress,
                                    status);

         if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE)
            RtlCopyMemory(pSourceAddress,
                          pRemoteAddressValue->byteArray16->byteArray16,
                          addressSize);
         else
         {
            ipv4Address = htonl(pRemoteAddressValue->uint32);

            RtlCopyMemory(pSourceAddress,
                          &ipv4Address,
                          addressSize);
         }
      }

      pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                     &FWPM_CONDITION_IP_LOCAL_ADDRESS);
      if(pLocalAddressValue)
      {
         if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE)
            addressSize = IPV6_ADDRESS_SIZE;
         else
            addressSize = IPV4_ADDRESS_SIZE;

         HLPR_NEW_ARRAY(pDestinationAddress,
                        BYTE,
                        addressSize,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
         HLPR_BAIL_ON_ALLOC_FAILURE(pDestinationAddress,
                                    status);

         if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE)
            RtlCopyMemory(pDestinationAddress,
                          pLocalAddressValue->byteArray16->byteArray16,
                          addressSize);
         else
         {
            ipv4Address = htonl(pLocalAddressValue->uint32);

            RtlCopyMemory(pDestinationAddress,
                          &ipv4Address,
                          addressSize);
         }            
      }

      pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                 &FWPM_CONDITION_IP_PROTOCOL);
      if(pProtocolValue &&
         pProtocolValue->type == FWP_UINT8)
         protocol = (IPPROTO)pProtocolValue->uint8;
      else
        protocol = IPPROTO_MAX;

      NT_ASSERT(protocol != IPPROTO_MAX);

#if (NTDDI_VERSION >= NTDDI_WIN6SP1)

      if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER))
         headerIncludeSize = pMetadata->headerIncludeHeaderLength;

#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1)

      if(pSourceAddress == 0 ||
         pDestinationAddress == 0)
      {
         status = STATUS_INVALID_MEMBER;

         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! PerformBasicPacketModificationAtInboundTransport() [status: %#x][pSourceAddress: %#p][pDestinationAddress: %#p]\n",
                    status,
                    pSourceAddress,
                    pDestinationAddress);

         HLPR_BAIL;
      }

      /// ... so we must re-construct the IPHeader with the appropriate information
      /// for checksum offload, this will recalculate the checksums
      status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList,
                                                       headerIncludeSize,
                                                       pCompletionData->pInjectionData->addressFamily,
                                                       pSourceAddress,
                                                       pDestinationAddress,
                                                       protocol,
                                                       endpointHandle,
                                                       (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                       pCompletionData->pInjectionData->controlDataLength,
                                                       0,
                                                       0,
                                                       interfaceIndex,
                                                       subInterfaceIndex);
      if(status != STATUS_SUCCESS)
      {
         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n",
                    status);

         HLPR_BAIL;
      }
   }
   /// Handle if this packet had the IP or Transport checksums offloaded or if it's loopback
   else if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded ||
           checksumInfo.Receive.NdisPacketTcpChecksumSucceeded ||
           checksumInfo.Receive.NdisPacketUdpChecksumSucceeded ||
           flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
   {
      /// Prevent TCP/IP Zone crossing and recalculate the checksums
      if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
      {
         FWP_VALUE* pLocalAddress    = 0;
         FWP_VALUE* pRemoteAddress   = 0;
         FWP_VALUE* pLoopbackAddress = 0;

         pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_REMOTE_ADDRESS);
         if(pLocalAddress &&
            ((pLocalAddress->type == FWP_UINT32 &&
            RtlCompareMemory(&(pLocalAddress->uint32),
                             IPV4_LOOPBACK_ADDRESS,
                             IPV4_ADDRESS_SIZE)) ||
            (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE &&
            RtlCompareMemory(pLocalAddress->byteArray16->byteArray16,
                             IPV6_LOOPBACK_ADDRESS,
                             IPV6_ADDRESS_SIZE))))
            pLoopbackAddress = pLocalAddress;

         if(!pLoopbackAddress)
         {
            pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                       &FWPM_CONDITION_IP_REMOTE_ADDRESS);
            if(pRemoteAddress &&
               ((pRemoteAddress->type == FWP_UINT32 &&
               RtlCompareMemory(&(pRemoteAddress->uint32),
                                IPV4_LOOPBACK_ADDRESS,
                                IPV4_ADDRESS_SIZE)) ||
               (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
               RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16,
                                IPV6_LOOPBACK_ADDRESS,
                                IPV6_ADDRESS_SIZE))))
               pLoopbackAddress = pRemoteAddress;
         }

         if(pLoopbackAddress)
         {
            status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata,
                                                           pLoopbackAddress,
                                                           ipHeaderSize,
                                                           pNetBufferList,
                                                           (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                           pCompletionData->pInjectionData->controlDataLength);
            if(status != STATUS_SUCCESS)
            {
               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! PerformBasicPacketModificationAtInboundTransport: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n",
                          status);

               HLPR_BAIL;
            }
         }
      }
      else
      {
         /// Recalculate the checksum
         if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList,
                                                ipHeaderSize);
      }
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList);

   if(pModificationData->flags)
   {
      FWP_VALUE* pLocalAddressValue        = 0;
      FWP_VALUE* pRemoteAddressValue       = 0;
      BYTE       pIPDestinationAddress[16] = {0};
      BYTE       pIPSourceAddress[16]      = {0};

      pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                      &FWPM_CONDITION_IP_REMOTE_ADDRESS);
      HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pRemoteAddressValue,
                                            status);

      pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                     &FWPM_CONDITION_IP_LOCAL_ADDRESS);
      HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pLocalAddressValue,
                                            status);

      if(pCompletionData->pInjectionData->addressFamily == AF_INET6)
      {
         RtlCopyMemory(pIPSourceAddress,
                       pRemoteAddressValue->byteArray16->byteArray16,
                       IPV6_ADDRESS_SIZE);

         RtlCopyMemory(pIPDestinationAddress,
                       pLocalAddressValue->byteArray16->byteArray16,
                       IPV6_ADDRESS_SIZE);
      }
      else
      {
         UINT32 sourceAddress      = htonl(pRemoteAddressValue->uint32);
         UINT32 destinationAddress = htonl(pLocalAddressValue->uint32);
          
         RtlCopyMemory(pIPSourceAddress,
                        &sourceAddress,
                        IPV4_ADDRESS_SIZE);
         
         RtlCopyMemory(pIPDestinationAddress,
                       &destinationAddress,
                       IPV4_ADDRESS_SIZE);
      }

      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         UINT32     tmpStatus      = STATUS_SUCCESS;
         FWP_VALUE* pProtocolValue = 0;

         /// The clone is at the IP Header, so advance by the size of the IP Header.
         NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                       ipHeaderSize,
                                       FALSE,
                                       0);

         pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_PROTOCOL);
         if(pProtocolValue &&
            pProtocolValue->type == FWP_UINT8)
            protocol = (IPPROTO)pProtocolValue->uint8;
         else
            protocol = IPPROTO_MAX;

         NT_ASSERT(protocol != IPPROTO_MAX);
         
         if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4)
            protocol = IPPROTO_ICMP;
         else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6)
            protocol = IPPROTO_ICMPV6;
         
         switch(protocol)
         {
            case IPPROTO_ICMP:
            {
               UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE;

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
               
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_TCP:
            {
               UINT32 tcpHeaderSize = transportHeaderSize ? transportHeaderSize : TCP_HEADER_MIN_SIZE;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_UDP:
            {
               UINT32 udpHeaderSize = transportHeaderSize ? transportHeaderSize : UDP_HEADER_MIN_SIZE;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;
         
                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;
         
                  status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;
         
                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;
         
                  status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
            case IPPROTO_ICMPV6:
            {
               UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE;
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;
         
                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);
         
                  status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
         
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);
         
                  status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE_2(status);
               }
         
               break;
            }
         }

         HLPR_BAIL_LABEL_2:

         /// return the data offset to the beginning of the IP Header
         tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList),
                                                   ipHeaderSize,
                                                   0,
                                                   0);
         if(tmpStatus != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         HLPR_BAIL_ON_FAILURE(status);
      }

      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX)
            interfaceIndex = pModificationData->ipData.interfaceIndex;

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);

               RtlCopyMemory(pIPSourceAddress,
                             pModificationData->ipData.sourceAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);                             
            }
            else
            {
               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);

               RtlCopyMemory(pIPSourceAddress,
                             pModificationData->ipData.sourceAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifySourceAddress(&value,
                                                         pNetBufferList,
                                                         FALSE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);
         }

         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            FWP_VALUE value;

            RtlZeroMemory(&value,
                          sizeof(FWP_VALUE));

            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
            {
               value.type = FWP_UINT32;

               RtlCopyMemory(&(value.uint32),
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);

               RtlCopyMemory(pIPDestinationAddress,
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            }
            else
            {
#pragma warning(push)
#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy

               HLPR_NEW(value.byteArray16,
                        FWP_BYTE_ARRAY16,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
               HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16,
                                          status);

#pragma warning(pop)

               value.type = FWP_BYTE_ARRAY16_TYPE;

               RtlCopyMemory(value.byteArray16->byteArray16,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);

               RtlCopyMemory(pIPDestinationAddress,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
            }

            status = KrnlHlprIPHeaderModifyDestinationAddress(&value,
                                                              pNetBufferList,
                                                              FALSE);

            KrnlHlprFwpValuePurgeLocalCopy(&value);

            HLPR_BAIL_ON_FAILURE(status);

         }
      }

      status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList,
                                                       ipHeaderSize,
                                                       pCompletionData->pInjectionData->addressFamily,
                                                       (UCHAR*)pIPSourceAddress,
                                                       (UCHAR*)pIPDestinationAddress,
                                                       protocol,
                                                       endpointHandle,
                                                       (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData,
                                                       pCompletionData->pInjectionData->controlDataLength,
                                                       0,
                                                       0,
                                                       interfaceIndex,
                                                       subInterfaceIndex);
      HLPR_BAIL_ON_FAILURE(status);
   }

   status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle,
                                            pCompletionData->pInjectionData->injectionContext,
                                            0,
                                            0,
                                            pCompletionData->pInjectionData->addressFamily,
                                            compartmentID,
                                            interfaceIndex,
                                            subInterfaceIndex,
                                            pNetBufferList,
                                            CompleteBasicPacketModification,
                                            pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsInjectTransportReceiveAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS ||
      bypassInjection)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

   HLPR_DELETE_ARRAY(pSourceAddress,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);

   HLPR_DELETE_ARRAY(pDestinationAddress,
                     WFPSAMPLER_CALLOUT_DRIVER_TAG);

   HLPR_DELETE(pPacketInformation,
               WFPSAMPLER_CALLOUT_DRIVER_TAG);

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtInboundTransport() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="PerformBasicPacketModificationAtOutboundTransport"
 
   Purpose:  Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and 
             injects the clone back to the stack's outbound path from the outgoing Transport 
             Layers using FwpsInjectTransportSendAsync().                                       <br>
                                                                                                <br>
   Notes:    Applies to the following outbound layers:                                          <br>
                FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}                                            <br>
                FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}                                           <br>
                FWPM_LAYER_DATAGRAM_DATA_V{4/6}        (Outbound only)                          <br>
                FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)                          <br>
                FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)                          <br>
                FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}     (Outbound, reauthorization only)         <br>
                FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only)                 <br>
                FWPM_LAYER_STREAM_PACKET_V{4/6}        (Outbound only)                          <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS PerformBasicPacketModificationAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData,
                                                           _In_ INJECTION_DATA** ppInjectionData,
                                                           _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData,
                                                           _In_ BOOLEAN isInline = FALSE)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> PerformBasicPacketModificationAtOutboundTransport()\n");

#endif /// DBG

   NT_ASSERT(ppClassifyData);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(pModificationData);
   NT_ASSERT(*ppClassifyData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                                   status          = STATUS_SUCCESS;
   FWPS_INCOMING_VALUES*                      pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues;
   FWPS_INCOMING_METADATA_VALUES*             pMetadata       = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues;
   UINT64                                     endpointHandle  = 0;
   FWPS_TRANSPORT_SEND_PARAMS*                pSendParams     = 0;
   COMPARTMENT_ID                             compartmentID   = DEFAULT_COMPARTMENT_ID;
   NET_BUFFER_LIST*                           pNetBufferList  = 0;
   BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0;
   BYTE*                                      pRemoteAddress  = 0;
   FWP_VALUE*                                 pAddressValue   = 0;

#if DBG

   KIRQL                                      irql            = KeGetCurrentIrql();

#endif /// DBG

#pragma warning(push)
#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pCompletionData,
            BASIC_PACKET_MODIFICATION_COMPLETION_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData,
                              status);

   HLPR_NEW(pSendParams,
            FWPS_TRANSPORT_SEND_PARAMS,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams,
                              status);

#pragma warning(pop)

   KeInitializeSpinLock(&(pCompletionData->spinLock));

   pCompletionData->performedInline = isInline;
   pCompletionData->pClassifyData   = *ppClassifyData;
   pCompletionData->pInjectionData  = *ppInjectionData;
   pCompletionData->pSendParams     = pSendParams;

   /// Responsibility for freeing this memory has been transferred to the pCompletionData
   *ppClassifyData = 0;

   *ppInjectionData = 0;

   pSendParams = 0;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE))
      endpointHandle = pMetadata->transportEndpointHandle;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_COMPARTMENT_ID))
      compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId;

   if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                     FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))
   {
      pCompletionData->pSendParams->controlData       = pMetadata->controlData;
      pCompletionData->pSendParams->controlDataLength = pMetadata->controlDataLength;
   }

   pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                             &FWPM_CONDITION_IP_REMOTE_ADDRESS);
   if(pAddressValue)
   {
      if(pCompletionData->pInjectionData->addressFamily == AF_INET)
      {
         UINT32 tempAddress = htonl(pAddressValue->uint32);

         HLPR_NEW_ARRAY(pRemoteAddress,
                        BYTE,
                        IPV4_ADDRESS_SIZE,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
         HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress,
                                    status);

         RtlCopyMemory(pRemoteAddress,
                       &tempAddress,
                       IPV4_ADDRESS_SIZE);
      }
      else
      {
#pragma warning(push)
#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

         HLPR_NEW_ARRAY(pRemoteAddress,
                        BYTE,
                        IPV6_ADDRESS_SIZE,
                        WFPSAMPLER_CALLOUT_DRIVER_TAG);
         HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress,
                                    status);

#pragma warning(pop)

         RtlCopyMemory(pRemoteAddress,
                       pAddressValue->byteArray16->byteArray16,
                       IPV6_ADDRESS_SIZE);

         if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                           FWPS_METADATA_FIELD_REMOTE_SCOPE_ID))
            pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId;
      }

      pCompletionData->pSendParams->remoteAddress = pRemoteAddress;
   }

   pCompletionData->pSendParams->controlData       = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData;
   pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength;

   /// Initial offset is at Transport Header (no IP Header yet), so just clone entire NET_BUFFER_LIST.
   status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket,
                                           g_pNDISPoolData->nblPoolHandle,
                                           g_pNDISPoolData->nbPoolHandle,
                                           0,
                                           &pNetBufferList);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList);

   if(pModificationData->flags)
   {
      if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER)
      {
         FWP_VALUE* pProtocolValue = 0;
         IPPROTO    protocol       = IPPROTO_MAX;

         pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                    &FWPM_CONDITION_IP_PROTOCOL);
         if(pProtocolValue &&
            pProtocolValue->type == FWP_UINT8)
            protocol = (IPPROTO)pProtocolValue->uint8;

         if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4)
            protocol = IPPROTO_ICMP;
         else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6)
            protocol = IPPROTO_ICMPV6;

#if(NTDDI_VERSION >= NTDDI_WIN7)

         else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                 pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)
            protocol = IPPROTO_TCP;

#endif

         switch(protocol)
         {
            case IPPROTO_ICMP:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;

               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;

                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);

                  status = KrnlHlprICMPv4HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;
               
                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);

                  status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               break;
            }
            case IPPROTO_TCP:
            {
               UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE;

               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  tcpHeaderSize = pMetadata->transportHeaderSize;

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;

                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;

                  status = KrnlHlprTCPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;

                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;

                  status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  tcpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               break;
            }
            case IPPROTO_UDP:
            {
               UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE;

               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  udpHeaderSize = pMetadata->transportHeaderSize;

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT)
               {
                  FWP_VALUE0 srcPort;

                  srcPort.type   = FWP_UINT16;
                  srcPort.uint16 = pModificationData->transportData.sourcePort;

                  status = KrnlHlprUDPHeaderModifySourcePort(&srcPort,
                                                             pNetBufferList,
                                                             udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT)
               {
                  FWP_VALUE0 dstPort;

                  dstPort.type   = FWP_UINT16;
                  dstPort.uint16 = pModificationData->transportData.destinationPort;

                  status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort,
                                                                  pNetBufferList,
                                                                  udpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               break;
            }
            case IPPROTO_ICMPV6:
            {
               UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE;

               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE))
                  icmpHeaderSize = pMetadata->transportHeaderSize;

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE)
               {
                  FWP_VALUE0 icmpType;

                  icmpType.type  = FWP_UINT8;
                  icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort);

                  status = KrnlHlprICMPv6HeaderModifyType(&icmpType,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE)
               {
                  FWP_VALUE0 icmpCode;

                  icmpCode.type  = FWP_UINT8;
                  icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort);

                  status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode,
                                                          pNetBufferList,
                                                          icmpHeaderSize);
                  HLPR_BAIL_ON_FAILURE(status);
               }

               break;
            }
         }
      }

      /// As there is no IP Header yet, we can only modify the destination IP address via the FWPS_TRANSPORT_SEND_PARAMS
      if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER)
      {
         if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS)
         {
            if(pCompletionData->pInjectionData->addressFamily == AF_INET)
               RtlCopyMemory(pCompletionData->pSendParams->remoteAddress,
                             pModificationData->ipData.destinationAddress.pIPv4,
                             IPV4_ADDRESS_SIZE);
            else
               RtlCopyMemory(pCompletionData->pSendParams->remoteAddress,
                             pModificationData->ipData.destinationAddress.pIPv6,
                             IPV6_ADDRESS_SIZE);
         }
      }
   }

   status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle,
                                         pCompletionData->pInjectionData->injectionContext,
                                         endpointHandle,
                                         0,
                                         pCompletionData->pSendParams,
                                         pCompletionData->pInjectionData->addressFamily,
                                         compartmentID,
                                         pNetBufferList,
                                         CompleteBasicPacketModification,
                                         pCompletionData);

   NT_ASSERT(irql == KeGetCurrentIrql());

   if(status != STATUS_SUCCESS)
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsInjectTransportSendAsync() [status: %#x]\n",
                 status);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pNetBufferList)
      {
         FwpsFreeCloneNetBufferList(pNetBufferList,
                                    0);

         pNetBufferList = 0;
      }

      if(pCompletionData)
         BasicPacketModificationCompletionDataDestroy(&pCompletionData,
                                                      TRUE);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- PerformBasicPacketModificationAtOutboundTransport() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="BasicPacketModificationDeferredProcedureCall"
 
   Purpose:  Invokes the appropriate private injection routine to perform the injection at 
             DISPATCH_LEVEL.                                                                    <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx             <br>
*/
_IRQL_requires_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Function_class_(KDEFERRED_ROUTINE)
VOID BasicPacketModificationDeferredProcedureCall(_In_ KDPC* pDPC,
                                                  _In_opt_ PVOID pContext,
                                                  _In_opt_ PVOID pArg1,
                                                  _In_opt_ PVOID pArg2)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> BasicPacketModificationDeferredProcedureCall()\n");

#endif /// DBG

   UNREFERENCED_PARAMETER(pDPC);
   UNREFERENCED_PARAMETER(pContext);
   UNREFERENCED_PARAMETER(pArg2);

   NT_ASSERT(pDPC);
   NT_ASSERT(pArg1);
   NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData);
   NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData);

   DPC_DATA* pDPCData = (DPC_DATA*)pArg1;

   if(pDPCData)
   {
      NTSTATUS                           status            = STATUS_SUCCESS;
      FWPS_INCOMING_VALUES*              pClassifyValues   = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues;
      FWPS_FILTER*                       pFilter           = (FWPS_FILTER*)pDPCData->pClassifyData->pFilter;
      PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

      if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
         status = PerformBasicPacketModificationAtInboundNetwork(&(pDPCData->pClassifyData),
                                                                 &(pDPCData->pInjectionData),
                                                                 pModificationData,
                                                                 FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6)
         status = PerformBasicPacketModificationAtOutboundNetwork(&(pDPCData->pClassifyData),
                                                                  &(pDPCData->pInjectionData),
                                                                  pModificationData,
                                                                  FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)
         status = PerformBasicPacketModificationAtForward(&(pDPCData->pClassifyData),
                                                          &(pDPCData->pInjectionData),
                                                          pModificationData,
                                                          FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
              (pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||     /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||     /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
         status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData),
                                                                   &(pDPCData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
              (pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
         status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData),
                                                                    &(pDPCData->pInjectionData),
                                                                    pModificationData,
                                                                    FALSE);

#if(NTDDI_VERSION >= NTDDI_WIN7)

      else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData),
                                                                   &(pDPCData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData),
                                                                    &(pDPCData->pInjectionData),
                                                                    pModificationData,
                                                                    FALSE);

#if(NTDDI_VERSION >= NTDDI_WIN8)

      else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE)
         status = PerformBasicPacketModificationAtInboundMACFrame(&(pDPCData->pClassifyData),
                                                                  &(pDPCData->pInjectionData),
                                                                  pModificationData,
                                                                  FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE)
         status = PerformBasicPacketModificationAtOutboundMACFrame(&(pDPCData->pClassifyData),
                                                                   &(pDPCData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET)
         status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pDPCData->pClassifyData),
                                                                         &(pDPCData->pInjectionData),
                                                                         pModificationData,
                                                                         FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET)
         status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pDPCData->pClassifyData),
                                                                        &(pDPCData->pInjectionData),
                                                                        pModificationData,
                                                                        FALSE);

#endif // (NTDDI_VERSION >= NTDDI_WIN8)
#endif // (NTDDI_VERSION >= NTDDI_WIN7)

      else
         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! BasicPacketModificationDeferredProcedureCall() [status: %#x]\n",
                    (UINT32)STATUS_NOT_SUPPORTED);

      if(status != STATUS_SUCCESS)
      {
         if(pDPCData->pClassifyData)
            KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData));

         if(pDPCData->pInjectionData)
            KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData));

         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    "  !!!! BasicPacketModificationDeferredProcedureCall: PerformBasicPacketModification() [status: %#x]\n",
                    status);
      }

      KrnlHlprDPCDataDestroy(&pDPCData);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- BasicPacketModificationDeferredProcedureCall()\n");

#endif /// DBG

   return;
}

/**
 @private_function="BasicPacketModificationWorkItemRoutine"
 
   Purpose:  Invokes the appropriate private routine to perform the modification and injection 
             at PASSIVE_LEVEL.                                                                  <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx             <br>
*/
_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
_Function_class_(IO_WORKITEM_ROUTINE)
VOID BasicPacketModificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject,
                                            _Inout_opt_ PVOID pContext)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> BasicPacketModificationWorkItemRoutine()\n");

#endif /// DBG

   UNREFERENCED_PARAMETER(pDeviceObject);

   NT_ASSERT(pContext);
   NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData);
   NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData);

   WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext;

   if(pWorkItemData)
   {
      NTSTATUS                           status            = STATUS_SUCCESS;
      FWPS_INCOMING_VALUES*              pClassifyValues   = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues;
      FWPS_FILTER*                       pFilter           = (FWPS_FILTER*)pWorkItemData->pClassifyData->pFilter;
      PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

      if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
         status = PerformBasicPacketModificationAtInboundNetwork(&(pWorkItemData->pClassifyData),
                                                                 &(pWorkItemData->pInjectionData),
                                                                 pModificationData,
                                                                 FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6)
         status = PerformBasicPacketModificationAtOutboundNetwork(&(pWorkItemData->pClassifyData),
                                                                  &(pWorkItemData->pInjectionData),
                                                                  pModificationData,
                                                                  FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)
         status = PerformBasicPacketModificationAtForward(&(pWorkItemData->pClassifyData),
                                                          &(pWorkItemData->pInjectionData),
                                                          pModificationData,
                                                          FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
              (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||     /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||     /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
         status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData),
                                                                   &(pWorkItemData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
              (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
         status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData),
                                                                    &(pWorkItemData->pInjectionData),
                                                                    pModificationData,
                                                                    FALSE);

#if(NTDDI_VERSION >= NTDDI_WIN7)

      else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData),
                                                                   &(pWorkItemData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData),
                                                                    &(pWorkItemData->pInjectionData),
                                                                    pModificationData,
                                                                    FALSE);

#if(NTDDI_VERSION >= NTDDI_WIN8)

      else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET ||
              pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE)
         status = PerformBasicPacketModificationAtInboundMACFrame(&(pWorkItemData->pClassifyData),
                                                                  &(pWorkItemData->pInjectionData),
                                                                  pModificationData,
                                                                  FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET ||
              pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE)
         status = PerformBasicPacketModificationAtOutboundMACFrame(&(pWorkItemData->pClassifyData),
                                                                   &(pWorkItemData->pInjectionData),
                                                                   pModificationData,
                                                                   FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET)
         status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pWorkItemData->pClassifyData),
                                                                         &(pWorkItemData->pInjectionData),
                                                                         pModificationData,
                                                                         FALSE);
      else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET)
         status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pWorkItemData->pClassifyData),
                                                                        &(pWorkItemData->pInjectionData),
                                                                        pModificationData,
                                                                        FALSE);

#endif // (NTDDI_VERSION >= NTDDI_WIN8)
#endif // (NTDDI_VERSION >= NTDDI_WIN7)

      else
         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! BasicPacketModificationWorkItemRoutine() [status: %#x]\n",
                    (UINT32)STATUS_NOT_SUPPORTED);

      if(status != STATUS_SUCCESS)
      {
         if(pWorkItemData->pClassifyData)
            KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData));

         if(pWorkItemData->pInjectionData)
            KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData));

         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    "  !!!! BasicPacketModificationWorkItemRoutine: PerformBasicPacketModification() [status: %#x]\n",
                    status);
      }

      KrnlHlprWorkItemDataDestroy(&pWorkItemData);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- BasicPacketModificationWorkItemRoutine()\n");

#endif /// DBG

   return;
}

/**
 @private_function="TriggerBasicPacketModificationInline"
 
   Purpose:  Makes a reference to all the classification data structures and invokes the 
             appropriate private routine to perform the modification and injection.             <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref:                                                                                    <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS TriggerBasicPacketModificationInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues,
                                              _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                              _Inout_opt_ VOID* pNetBufferList,
                                              _In_opt_ const VOID* pClassifyContext,
                                              _In_ const FWPS_FILTER* pFilter,
                                              _In_ UINT64 flowContext,
                                              _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut,
                                              _In_ INJECTION_DATA** ppInjectionData)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> TriggerBasicPacketModificationInline()\n");

#endif /// DBG

   NT_ASSERT(pClassifyValues);
   NT_ASSERT(pMetadata);
   NT_ASSERT(pNetBufferList);
   NT_ASSERT(pFilter);
   NT_ASSERT(pClassifyOut);
   NT_ASSERT(ppInjectionData);
   NT_ASSERT(*ppInjectionData);

   NTSTATUS                           status            = STATUS_SUCCESS;
   CLASSIFY_DATA*                     pClassifyData     = 0;
   PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

#pragma warning(push)
#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   HLPR_NEW(pClassifyData,
            CLASSIFY_DATA,
            WFPSAMPLER_CALLOUT_DRIVER_TAG);
   HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData,
                              status);

#pragma warning(pop)

   pClassifyData->pClassifyValues  = pClassifyValues;
   pClassifyData->pMetadataValues  = pMetadata;
   pClassifyData->pPacket          = pNetBufferList;
   pClassifyData->pClassifyContext = pClassifyContext;
   pClassifyData->pFilter          = pFilter;
   pClassifyData->flowContext      = flowContext;
   pClassifyData->pClassifyOut     = pClassifyOut;

   if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
      pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
      status = PerformBasicPacketModificationAtInboundNetwork(&pClassifyData,
                                                              ppInjectionData,
                                                              pModificationData,
                                                              TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6)
      status = PerformBasicPacketModificationAtOutboundNetwork(&pClassifyData,
                                                               ppInjectionData,
                                                               pModificationData,
                                                               TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)
      status = PerformBasicPacketModificationAtForward(&pClassifyData,
                                                       ppInjectionData,
                                                       pModificationData,
                                                       TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
           ((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND &&
           (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||     /// Policy Change Reauthorization
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||     /// Policy Change Reauthorization
           pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
      status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData,
                                                                ppInjectionData,
                                                                pModificationData,
                                                                TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
           ((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND &&
           (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
           pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6)))
      status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData,
                                                                 ppInjectionData,
                                                                 pModificationData,
                                                                 TRUE);

#if(NTDDI_VERSION >= NTDDI_WIN7)

      else if((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData,
                                                                   ppInjectionData,
                                                                   pModificationData,
                                                                   TRUE);
      else if((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND &&
              (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))
         status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData,
                                                                    ppInjectionData,
                                                                    pModificationData,
                                                                    TRUE);

#if(NTDDI_VERSION >= NTDDI_WIN8)

   else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET ||
           pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE)
      status = PerformBasicPacketModificationAtInboundMACFrame(&pClassifyData,
                                                               ppInjectionData,
                                                               pModificationData,
                                                               TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET ||
           pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE)
      status = PerformBasicPacketModificationAtOutboundMACFrame(&pClassifyData,
                                                                ppInjectionData,
                                                                pModificationData,
                                                                TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET)
      status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&pClassifyData,
                                                                      ppInjectionData,
                                                                      pModificationData,
                                                                      TRUE);
   else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET)
      status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&pClassifyData,
                                                                     ppInjectionData,
                                                                     pModificationData,
                                                                     TRUE);

#endif // (NTDDI_VERSION >= NTDDI_WIN8)
#endif // (NTDDI_VERSION >= NTDDI_WIN7)

   else
   {
      status = STATUS_NOT_SUPPORTED;

      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! TriggerBasicPacketModificationInline() [status: %#x]\n",
                 status);
   }

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- TriggerBasicPacketModificationInline() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

/**
 @private_function="TriggerBasicPacketModificationOutOfBand"
 
   Purpose:  Creates a local copy of the classification data structures and queues a WorkItem 
             to perform the modification and injection at PASSIVE_LEVEL.                        <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550679.aspx             <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
_Check_return_
NTSTATUS TriggerBasicPacketModificationOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues,
                                                 _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                                 _Inout_opt_ VOID* pNetBufferList,
                                                 _In_opt_ const VOID* pClassifyContext,
                                                 _In_ const FWPS_FILTER* pFilter,
                                                 _In_ UINT64 flowContext,
                                                 _In_ FWPS_CLASSIFY_OUT* pClassifyOut,
                                                 _In_ INJECTION_DATA* pInjectionData,
                                                 _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pPCData)
{
#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> TriggerBasicPacketModificationOutOfBand()\n");

#endif /// DBG

   NT_ASSERT(pClassifyValues);
   NT_ASSERT(pMetadata);
   NT_ASSERT(pNetBufferList);
   NT_ASSERT(pFilter);
   NT_ASSERT(pClassifyOut);
   NT_ASSERT(pInjectionData);
   NT_ASSERT(pPCData);

   NTSTATUS       status        = STATUS_SUCCESS;
   CLASSIFY_DATA* pClassifyData = 0;

#pragma warning(push)
#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

   status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData,
                                                pClassifyValues,
                                                pMetadata,
                                                pNetBufferList,
                                                pClassifyContext,
                                                pFilter,
                                                flowContext,
                                                pClassifyOut);
   HLPR_BAIL_ON_FAILURE(status);

#pragma warning(pop)

   if(pPCData->useWorkItems)
      status = KrnlHlprWorkItemQueue(g_pWDMDevice,
                                     BasicPacketModificationWorkItemRoutine,
                                     pClassifyData,
                                     pInjectionData,
                                     0);
   else if(pPCData->useThreadedDPC)
      status = KrnlHlprThreadedDPCQueue(BasicPacketModificationDeferredProcedureCall,
                                        pClassifyData,
                                        pInjectionData,
                                        0);
   else
      status = KrnlHlprDPCQueue(BasicPacketModificationDeferredProcedureCall,
                                pClassifyData,
                                pInjectionData,
                                0);

   HLPR_BAIL_LABEL:

   NT_ASSERT(status == STATUS_SUCCESS);

   if(status != STATUS_SUCCESS)
   {
      if(pClassifyData)
         KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData);
   }

#if DBG

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- TriggerBasicPacketModificationOutOfBand() [status: %#x]\n",
              status);

#endif /// DBG

   return status;
}

#if(NTDDI_VERSION >= NTDDI_WIN7)

/**
 @classify_function="ClassifyBasicPacketModification"
 
   Purpose:  Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified 
             data and injects the clone back to the stack's data path.                          <br>
                                                                                                <br>
   Notes:    Applies to the following layers:                                                   <br>
                FWPS_LAYER_INBOUND_IPPACKET_V{4/6}                                              <br>
                FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}                                             <br>
                FWPS_LAYER_IPFORWARD_V{4/6}                                                     <br>
                FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}                                             <br>
                FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}                                            <br>
                FWPS_LAYER_DATAGRAM_DATA_V{4/6}                                                 <br>
                FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}                                            <br>
                FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}                                           <br>
                FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}                                              <br>
                FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}                                          <br>
                FWPS_LAYER_STREAM_PACKET_V{4/6}                                                 <br>
                FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET                                           <br>
                FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET                                          <br>
                FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE                                             <br>
                FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE                                            <br>
                FWPS_LAYER_INGRESS_VSWITCH_ETHERNET                                             <br>
                FWPS_LAYER_EGRESS_VSWITCH_ETHERNET                                              <br>
                                                                                                <br>
             TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL                                <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues,
                                           _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                           _Inout_opt_ VOID* pNetBufferList,
                                           _In_opt_ const VOID* pClassifyContext,
                                           _In_ const FWPS_FILTER* pFilter,
                                           _In_ UINT64 flowContext,
                                           _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut)
{
      NT_ASSERT(pClassifyValues);
      NT_ASSERT(pMetadata);
      NT_ASSERT(pFilter);
      NT_ASSERT(pClassifyOut);
      NT_ASSERT(pFilter->providerContext);
      NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT);
      NT_ASSERT(pFilter->providerContext->dataBuffer);
      NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA));
   
#if(NTDDI_VERSION >= NTDDI_WIN8)
   
      NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE ||
                pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET ||
                pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET);
   
#else
   
      NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6);
      
#endif /// (NTDDI_VERSION >= NTDDI_WIN8)
   
      NT_ASSERT(pFilter->providerContext);
      NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT);
      NT_ASSERT(pFilter->providerContext->dataBuffer);
      NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA));

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->rights);

   if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE)
   {
      /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject
      if(pNetBufferList)
      {
         PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

         if(pData)
         {
            NTSTATUS        status         = STATUS_SUCCESS;
            FWP_VALUE*      pFlags         = 0;
            INJECTION_DATA* pInjectionData = 0;

            pClassifyOut->actionType = FWP_ACTION_CONTINUE;

            pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_FLAGS);
            if(pFlags &&
               pFlags->type == FWP_UINT32)
            {
               /// For IPsec interop, if  ALE classification is required, bypass the injection
               if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED &&
                  FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED))
                  HLPR_BAIL;

               /// Inject the individual fragments, but not the fragment grouping of those fragments
               if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP)
                  HLPR_BAIL;
            }

            if(pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER &&
               (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
               pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
               pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6))
            {
               UINT32  bytesRetreated = 0;
               UINT8   version        = 0;
               IPPROTO protocol       = IPPROTO_MAX;

               if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                  pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
               {
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_IP_HEADER_SIZE))
                     bytesRetreated = pMetadata->ipHeaderSize;
               }

               if(bytesRetreated)
               {
                  /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ...
                  status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                         bytesRetreated,
                                                         0,
                                                         0);
                  if(status != STATUS_SUCCESS)
                  {
                     DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                                DPFLTR_ERROR_LEVEL,
                                " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                                status);
                  
                     HLPR_BAIL;
                  }
               }

               version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList);

               protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList,
                                                           version == IPV4 ? AF_INET : AF_INET6);

               if(bytesRetreated)
               {
                  /// ... and advance the offset back to the original position.
                  NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                bytesRetreated,
                                                FALSE,
                                                0);
               }

               /// Exit if this isn't the protocol we are looking for
               if(protocol != pData->originalTransportData.protocol)
                  HLPR_BAIL;
               else
               {
                  UINT16 sourcePort      = 0;
                  UINT16 destinationPort = 0;
                  UINT32 bytesAdvanced   = 0;

                  if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                     pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                     pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                     pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)
                  {
                     if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                       FWPS_METADATA_FIELD_IP_HEADER_SIZE))
                        bytesAdvanced = pMetadata->ipHeaderSize;
                  }

                  if(bytesAdvanced)
                  {
                     /// Initial offset is at the IP Header for OUTBOUND_IPPACKET and IPFORWARD, so 
                     /// advance the size of the IP Header ...
                     NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                   bytesAdvanced,
                                                   FALSE,
                                                   0);
                  }

                  sourcePort      = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                              protocol);
                  destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                                   protocol);
                  /// Exit if the ports are not what we are looking for
                  if(pData->originalTransportData.sourcePort &&
                     sourcePort != pData->originalTransportData.sourcePort)
                      HLPR_BAIL;

                   if(pData->originalTransportData.destinationPort &&
                      destinationPort != pData->originalTransportData.destinationPort)
                      HLPR_BAIL;

                  if(bytesAdvanced)
                  {
                     /// ... and retreat the offset back to the original position.
                     status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                            bytesAdvanced,
                                                            0,
                                                            0);
                     if(status != STATUS_SUCCESS)
                     {
                        DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                                   DPFLTR_ERROR_LEVEL,
                                   " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                                   status);
                     
                        HLPR_BAIL;
                     }
                  }
               }
            }

#pragma warning(push)
#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

            status = KrnlHlprInjectionDataCreate(&pInjectionData,
                                                 pClassifyValues,
                                                 pMetadata,
                                                 (NET_BUFFER_LIST*)pNetBufferList,
                                                 pFilter);
            HLPR_BAIL_ON_FAILURE(status);

#pragma warning(pop)

            if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF &&
               pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
            {
               BOOLEAN    performOutOfBand = TRUE;
               FWP_VALUE* pProtocolValue   = 0;

               pClassifyOut->actionType  = FWP_ACTION_BLOCK;
               pClassifyOut->flags      |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
               pClassifyOut->rights     ^= FWPS_RIGHT_ACTION_WRITE;

               if(pFlags &&
                  pFlags->type == FWP_UINT32 &&
                  pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED)
                  pInjectionData->isIPsecSecured = TRUE;

               /// Override the default of performing Out of Band with the user's specified setting ...
               if(pData->performInline)
                  performOutOfBand = FALSE;

               /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ...
               pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                          &FWPM_CONDITION_IP_PROTOCOL);
               if((pProtocolValue &&
                  pProtocolValue->uint8 == IPPROTO_TCP &&
                  pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) ||
                  pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                  pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)
                  performOutOfBand = TRUE;

               /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups.
               if(!performOutOfBand &&
                  pInjectionData->direction == FWP_DIRECTION_INBOUND)
               {
                  FWP_VALUE* pRemoteAddress = 0;

                  pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                             &FWPM_CONDITION_IP_REMOTE_ADDRESS);
                  if(pRemoteAddress &&
                     ((pRemoteAddress->type == FWP_UINT32 &&
                     RtlCompareMemory(&(pRemoteAddress->uint32),
                                      IPV4_LOOPBACK_ADDRESS,
                                      IPV4_ADDRESS_SIZE)) ||
                     (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
                      RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16),
                                    IPV6_LOOPBACK_ADDRESS,
                                    IPV6_ADDRESS_SIZE))))
                     performOutOfBand = TRUE;
               }

               if(performOutOfBand)
                  status = TriggerBasicPacketModificationOutOfBand(pClassifyValues,
                                                                   pMetadata,
                                                                   pNetBufferList,
                                                                   pClassifyContext,
                                                                   pFilter,
                                                                   flowContext,
                                                                   pClassifyOut,
                                                                   pInjectionData,
                                                                   pData);
               else
                  status = TriggerBasicPacketModificationInline(pClassifyValues,
                                                                pMetadata,
                                                                pNetBufferList,
                                                                pClassifyContext,
                                                                pFilter,
                                                                flowContext,
                                                                pClassifyOut,
                                                                &pInjectionData);
            }
            else
            {
               pClassifyOut->actionType = FWP_ACTION_PERMIT;

               KrnlHlprInjectionDataDestroy(&pInjectionData);

               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_INFO_LEVEL,
                          "   -- Injection previously performed.\n");
            }

            HLPR_BAIL_LABEL:

            NT_ASSERT(status == STATUS_SUCCESS);

            if(status != STATUS_SUCCESS)
            {
               KrnlHlprInjectionDataDestroy(&pInjectionData);

               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! ClassifyBasicPacketModification() [status: %#x]\n",
                          status);
            }
         }
      }
      else
         pClassifyOut->actionType = FWP_ACTION_PERMIT;
   }

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->actionType,
              pClassifyOut->rights,
              (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE");

   return;
}

#else

/**
 @classify_function="ClassifyBasicPacketModification"
 
   Purpose:  Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified 
             data and injects the clone back to the stack's data path.                          <br>
                                                                                                <br>
   Notes:    Applies to the following layers:                                                   <br>
                FWPS_LAYER_INBOUND_IPPACKET_V{4/6}                                              <br>
                FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}                                             <br>
                FWPS_LAYER_IPFORWARD_V{4/6}                                                     <br>
                FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}                                             <br>
                FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}                                            <br>
                FWPS_LAYER_DATAGRAM_DATA_V{4/6}                                                 <br>
                FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}                                            <br>
                FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}                                           <br>
                FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}                                              <br>
                FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}                                          <br>
                                                                                                <br>
             TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL                                <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx             <br>
*/
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_
VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES* pClassifyValues,
                                           _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                           _Inout_opt_ VOID* pNetBufferList,
                                           _In_ const FWPS_FILTER* pFilter,
                                           _In_ UINT64 flowContext,
                                           _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut)
{
   NT_ASSERT(pClassifyValues);
   NT_ASSERT(pMetadata);
   NT_ASSERT(pFilter);
   NT_ASSERT(pClassifyOut);
   NT_ASSERT(pFilter->providerContext);
   NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT);
   NT_ASSERT(pFilter->providerContext->dataBuffer);
   NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA));
   NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
             pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6);
   NT_ASSERT(pFilter->providerContext);
   NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT);
   NT_ASSERT(pFilter->providerContext->dataBuffer);
   NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA));

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->rights);


   if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE)
   {
      /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject
      if(pNetBufferList)
      {
         NTSTATUS                           status         = STATUS_SUCCESS;
         FWP_VALUE*                         pFlags         = 0;
         PC_BASIC_PACKET_MODIFICATION_DATA* pData          = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;
         INJECTION_DATA*                    pInjectionData = 0;

         pClassifyOut->actionType = FWP_ACTION_CONTINUE;

         pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                            &FWPM_CONDITION_FLAGS);
         if(pFlags &&
            pFlags->type == FWP_UINT32)
         {
            /// For IPsec interop, if  ALE classification is required, bypass the injection
            if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED &&

#if(NTDDI_VERSION >= NTDDI_WIN6SP1)

               FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                              FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED))
#else

               pFlags->uint32 & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY)

#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1)

            HLPR_BAIL;

            /// Inject the individual fragments, but not the fragment grouping of those fragments
            if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP)
               HLPR_BAIL;
         }

         if(pData &&
            pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER &&
            (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
            pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
            pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
            pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6))
         {
            UINT32  bytesRetreated = 0;
            UINT8   version        = 0;
            IPPROTO protocol       = IPPROTO_MAX;

            if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
            {
               if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_IP_HEADER_SIZE))
                  bytesRetreated = pMetadata->ipHeaderSize;
            }

            if(bytesRetreated)
            {
               /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ...
               status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                      bytesRetreated,
                                                      0,
                                                      0);
               if(status != STATUS_SUCCESS)
               {
                  DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                             DPFLTR_ERROR_LEVEL,
                             " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                             status);
               
                  HLPR_BAIL;
               }
            }

            version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList);

            protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList,
                                                        version == IPV4 ? AF_INET : AF_INET6);

            if(bytesRetreated)
            {
               /// ... and advance the offset back to the original position.
               NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                             bytesRetreated,
                                             FALSE,
                                             0);
            }

            /// Exit if this isn't the protocol we are looking for
            if(protocol != pData->originalTransportData.protocol)
               HLPR_BAIL;
            else
            {
               UINT16 sourcePort      = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                                  protocol);
               UINT16 destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                                       protocol);
               /// Exit if the ports are not what we are looking for
               if((pData->originalTransportData.sourcePort &&
                   sourcePort != pData->originalTransportData.sourcePort) ||
                   (pData->originalTransportData.destinationPort &&
                   destinationPort != pData->originalTransportData.destinationPort))
                   HLPR_BAIL;
            }
         }

#pragma warning(push)
#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

         status = KrnlHlprInjectionDataCreate(&pInjectionData,
                                              pClassifyValues,
                                              pMetadata,
                                              (NET_BUFFER_LIST*)pNetBufferList,
                                              pFilter);
         HLPR_BAIL_ON_FAILURE(status);

#pragma warning(pop)
                                           
         if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF &&
            pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
         {
            BOOLEAN                            performOutOfBand = TRUE;
            FWP_VALUE*                         pProtocolValue   = 0;
            PC_BASIC_PACKET_MODIFICATION_DATA* pData            = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

            pClassifyOut->actionType  = FWP_ACTION_BLOCK;
            pClassifyOut->flags      |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
            pClassifyOut->rights     ^= FWPS_RIGHT_ACTION_WRITE;

            if(pFlags &&
               pFlags->type == FWP_UINT32 &&
               pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED)
               pInjectionData->isIPsecSecured = TRUE;

            /// Override the default of performing Out of Band with the user's specified setting ...
            if(pData->performInline)
               performOutOfBand = FALSE;

            /// Due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent
            pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                       &FWPM_CONDITION_IP_PROTOCOL);
            if(pProtocolValue &&
               pProtocolValue->uint8 == IPPROTO_TCP &&
               pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD)
               performOutOfBand = TRUE;

            if(performOutOfBand)
               status = TriggerBasicPacketModificationOutOfBand(pClassifyValues,
                                                                pMetadata,
                                                                pNetBufferList,
                                                                0,
                                                                pFilter,
                                                                flowContext,
                                                                pClassifyOut,
                                                                pInjectionData,
                                                                pData);
            else
               status = TriggerBasicPacketModificationInline(pClassifyValues,
                                                             pMetadata,
                                                             pNetBufferList,
                                                             0,
                                                             pFilter,
                                                             flowContext,
                                                             pClassifyOut,
                                                             &pInjectionData);
         }
         else
         {
            pClassifyOut->actionType = FWP_ACTION_PERMIT;

            KrnlHlprInjectionDataDestroy(&pInjectionData);

            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_INFO_LEVEL,
                       "   -- Injection previously performed.\n");
         }

         HLPR_BAIL_LABEL:

         NT_ASSERT(status == STATUS_SUCCESS);

         if(status != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! ClassifyBasicPacketModification() [status: %#x]\n",
                       status);

            KrnlHlprInjectionDataDestroy(&pInjectionData);
         }
      }
      else
         pClassifyOut->actionType = FWP_ACTION_PERMIT;
   }

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->actionType,
              pClassifyOut->rights,
              (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE");

   return;
}

#endif // (NTDDI_VERSION >= NTDDI_WIN7)


<|start_filename|>audio/sysvad/EndpointsCommon/usbhsspeakerwavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsspeakerwavtable.h

Abstract:

    Declaration of wave miniport tables for the USB Headset speaker (external).

--*/

#ifndef _SYSVAD_USBHSSPEAKERWAVTABLE_H_
#define _SYSVAD_USBHSSPEAKERWAVTABLE_H_

//
// Function prototypes.
//
NTSTATUS PropertyHandler_UsbHsWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest);



#define USBHSSPEAKER_DEVICE_MAX_CHANNELS                 2       // Max Channels.
                                                         
#define USBHSSPEAKER_HOST_MAX_CHANNELS                   2       // Max Channels.
#define USBHSSPEAKER_HOST_MIN_BITS_PER_SAMPLE            16      // Min Bits Per Sample
#define USBHSSPEAKER_HOST_MAX_BITS_PER_SAMPLE            24      // Max Bits Per Sample
#define USBHSSPEAKER_HOST_MIN_SAMPLE_RATE                24000   // Min Sample Rate
#define USBHSSPEAKER_HOST_MAX_SAMPLE_RATE                96000   // Max Sample Rate
                                                         
#define USBHSSPEAKER_OFFLOAD_MAX_CHANNELS                2       // Max Channels.
#define USBHSSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE         16      // Min Bits Per Sample
#define USBHSSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE         16      // Max Bits Per Sample
#define USBHSSPEAKER_OFFLOAD_MIN_SAMPLE_RATE             44100   // Min Sample Rate
#define USBHSSPEAKER_OFFLOAD_MAX_SAMPLE_RATE             48000   // Max Sample Rate
                                                         
#define USBHSSPEAKER_LOOPBACK_MAX_CHANNELS               USBHSSPEAKER_HOST_MAX_CHANNELS          // Must be equal to host pin's Max Channels.
#define USBHSSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE        USBHSSPEAKER_HOST_MIN_BITS_PER_SAMPLE   // Must be equal to host pin's Min Bits Per Sample
#define USBHSSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE        USBHSSPEAKER_HOST_MAX_BITS_PER_SAMPLE   // Must be equal to host pin's Max Bits Per Sample
#define USBHSSPEAKER_LOOPBACK_MIN_SAMPLE_RATE            USBHSSPEAKER_HOST_MIN_SAMPLE_RATE       // Must be equal to host pin's Min Sample Rate
#define USBHSSPEAKER_LOOPBACK_MAX_SAMPLE_RATE            USBHSSPEAKER_HOST_MAX_SAMPLE_RATE       // Must be equal to host pin's Max Sample Rate

#define USBHSSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS          2       // Max Channels.
#define USBHSSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE   16      // Min Bits Per Sample
#define USBHSSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE   16      // Max Bits Per Sample
#define USBHSSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE       44100   // Min Sample Rate
#define USBHSSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE       44100   // Max Sample Rate

//
// Max # of pin instances.
//
#define USBHSSPEAKER_MAX_INPUT_SYSTEM_STREAMS             6
#define USBHSSPEAKER_MAX_INPUT_OFFLOAD_STREAMS            MAX_INPUT_OFFLOAD_STREAMS
#define USBHSSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS          MAX_OUTPUT_LOOPBACK_STREAMS

//=============================================================================
static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE UsbHsSpeakerAudioEngineSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                24000,
                96000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                288000,
                6,
                24,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            24,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                88200,
                352800,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 5
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                96000,
                384000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE UsbHsSpeakerHostPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                24000,
                96000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                32000,
                128000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                288000,
                6,
                24,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            24,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 5
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                88200,
                352800,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 6
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                96000,
                384000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE UsbHsSpeakerOffloadPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

//
// Supported modes (only on streaming pins).
//
static
MODE_AND_DEFAULT_FORMAT UsbHsSpeakerHostPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[4].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[0].DataFormat // 24KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION,
        &UsbHsSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    }
};

static
MODE_AND_DEFAULT_FORMAT UsbHsSpeakerOffloadPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &UsbHsSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA,
        &UsbHsSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz
    }
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
//
static 
PIN_DEVICE_FORMATS_AND_MODES UsbHsSpeakerPinDeviceFormatsAndModes[] = 
{
    {
        SystemRenderPin,
        UsbHsSpeakerHostPinSupportedDeviceFormats,
        SIZEOF_ARRAY(UsbHsSpeakerHostPinSupportedDeviceFormats),
        UsbHsSpeakerHostPinSupportedDeviceModes,
        SIZEOF_ARRAY(UsbHsSpeakerHostPinSupportedDeviceModes)
    },
    {
        OffloadRenderPin,
        UsbHsSpeakerOffloadPinSupportedDeviceFormats,
        SIZEOF_ARRAY(UsbHsSpeakerOffloadPinSupportedDeviceFormats),
        UsbHsSpeakerOffloadPinSupportedDeviceModes,
        SIZEOF_ARRAY(UsbHsSpeakerOffloadPinSupportedDeviceModes),
    },
    {
        RenderLoopbackPin,
        UsbHsSpeakerHostPinSupportedDeviceFormats,   // Must support all the formats supported by host pin
        SIZEOF_ARRAY(UsbHsSpeakerHostPinSupportedDeviceFormats),
        NULL,   // loopback doesn't support modes.
        0
    },
    {
        BridgePin,
        NULL,
        0,
        NULL,
        0
    },
    {
        NoPin,      // For convenience, offload engine device formats appended here
        UsbHsSpeakerAudioEngineSupportedDeviceFormats,
        SIZEOF_ARRAY(UsbHsSpeakerAudioEngineSupportedDeviceFormats),
        NULL,       // no modes for this entry.
        0
    }
};

//=============================================================================
static
KSDATARANGE_AUDIO UsbHsSpeakerPinDataRangesStream[] =
{
    { // 0
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        USBHSSPEAKER_HOST_MAX_CHANNELS,           
        USBHSSPEAKER_HOST_MIN_BITS_PER_SAMPLE,    
        USBHSSPEAKER_HOST_MAX_BITS_PER_SAMPLE,    
        USBHSSPEAKER_HOST_MIN_SAMPLE_RATE,            
        USBHSSPEAKER_HOST_MAX_SAMPLE_RATE             
    },
    { // 1
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        USBHSSPEAKER_OFFLOAD_MAX_CHANNELS,           
        USBHSSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE,    
        USBHSSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE,    
        USBHSSPEAKER_OFFLOAD_MIN_SAMPLE_RATE,
        USBHSSPEAKER_OFFLOAD_MAX_SAMPLE_RATE
    },
    { // 2
        {
            sizeof(KSDATARANGE_AUDIO),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        USBHSSPEAKER_LOOPBACK_MAX_CHANNELS,           
        USBHSSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE,    
        USBHSSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE,    
        USBHSSPEAKER_LOOPBACK_MIN_SAMPLE_RATE,
        USBHSSPEAKER_LOOPBACK_MAX_SAMPLE_RATE
    }
};

static
PKSDATARANGE UsbHsSpeakerPinDataRangePointersStream[] =
{
    PKSDATARANGE(&UsbHsSpeakerPinDataRangesStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList)
};

static
PKSDATARANGE UsbHsSpeakerPinDataRangePointersOffloadStream[] =
{
    PKSDATARANGE(&UsbHsSpeakerPinDataRangesStream[1]),
    PKSDATARANGE(&PinDataRangeAttributeList)
};

static
PKSDATARANGE UsbHsSpeakerPinDataRangePointersLoopbackStream[] =
{
    PKSDATARANGE(&UsbHsSpeakerPinDataRangesStream[2])
};

//=============================================================================
static
KSDATARANGE UsbHsSpeakerPinDataRangesBridge[] =
{
    {
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE UsbHsSpeakerPinDataRangePointersBridge[] =
{
    &UsbHsSpeakerPinDataRangesBridge[0]
};

//=============================================================================

static
PCPROPERTY_ITEM PropertiesUsbHsSpeakerOffloadPin[] =
{
    {
        &KSPROPSETID_OffloadPin,  // define new property set
        KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_OffloadPin
    },
    {
        &KSPROPSETID_OffloadPin,  // define new property set
        KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties
        KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_OffloadPin
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationUsbHsSpeakerOffloadPin, PropertiesUsbHsSpeakerOffloadPin);

//=============================================================================
static
PCPIN_DESCRIPTOR UsbHsSpeakerWaveMiniportPins[] =
{
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM
    {
        USBHSSPEAKER_MAX_INPUT_SYSTEM_STREAMS,
        USBHSSPEAKER_MAX_INPUT_SYSTEM_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(UsbHsSpeakerPinDataRangePointersStream),
            UsbHsSpeakerPinDataRangePointersStream,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD
    {
        USBHSSPEAKER_MAX_INPUT_OFFLOAD_STREAMS,
        USBHSSPEAKER_MAX_INPUT_OFFLOAD_STREAMS, 
        0,
        &AutomationUsbHsSpeakerOffloadPin,     // AutomationTable
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(UsbHsSpeakerPinDataRangePointersOffloadStream),
            UsbHsSpeakerPinDataRangePointersOffloadStream,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK
    {
        USBHSSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS,
        USBHSSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(UsbHsSpeakerPinDataRangePointersLoopbackStream),
            UsbHsSpeakerPinDataRangePointersLoopbackStream,
            KSPIN_DATAFLOW_OUT,              
            KSPIN_COMMUNICATION_SINK,
            &KSNODETYPE_AUDIO_LOOPBACK,
            NULL,
            0
        }
    },
    // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(UsbHsSpeakerPinDataRangePointersBridge),
            UsbHsSpeakerPinDataRangePointersBridge,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
};

//=============================================================================
static
PCNODE_DESCRIPTOR UsbHsSpeakerWaveMiniportNodes[] =
{
    // KSNODE_WAVE_AUDIO_ENGINE
    {
        0,                          // Flags
        NULL,                       // AutomationTable
        &KSNODETYPE_AUDIO_ENGINE,   // Type  KSNODETYPE_AUDIO_ENGINE
        NULL                        // Name
    }
};
//=============================================================================
//
//                   ----------------------------      
//                   |                          |      
//  System Pin   0-->|                          |--> 2 Loopback Pin
//                   |   HW Audio Engine node   |      
//  Offload Pin  1-->|                          |--> 3 KSPIN_WAVE_RENDER_SOURCE
//                   |                          |      
//                   ----------------------------      
static
PCCONNECTION_DESCRIPTOR UsbHsSpeakerWaveMiniportConnections[] =
{
    { PCFILTER_NODE,            KSPIN_WAVE_RENDER_SINK_SYSTEM,     KSNODE_WAVE_AUDIO_ENGINE,   1 },
    { PCFILTER_NODE,            KSPIN_WAVE_RENDER_SINK_OFFLOAD,    KSNODE_WAVE_AUDIO_ENGINE,   2 },
    { KSNODE_WAVE_AUDIO_ENGINE, 3,                                 PCFILTER_NODE,              KSPIN_WAVE_RENDER_SINK_LOOPBACK },
    { KSNODE_WAVE_AUDIO_ENGINE, 0,                                 PCFILTER_NODE,              KSPIN_WAVE_RENDER_SOURCE },
};

//=============================================================================
static
PCPROPERTY_ITEM PropertiesUsbHsSpeakerWaveFilter[] =
{
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsWaveFilter
    },
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT2,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsWaveFilter
    },
    {
        &KSPROPSETID_AudioEffectsDiscovery,
        KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsWaveFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationUsbHsSpeakerWaveFilter, PropertiesUsbHsSpeakerWaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR UsbHsSpeakerWaveMiniportFilterDescriptor =
{
    0,                                              // Version
    &AutomationUsbHsSpeakerWaveFilter,             // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                       // PinSize
    SIZEOF_ARRAY(UsbHsSpeakerWaveMiniportPins),    // PinCount
    UsbHsSpeakerWaveMiniportPins,                  // Pins
    sizeof(PCNODE_DESCRIPTOR),                      // NodeSize
    SIZEOF_ARRAY(UsbHsSpeakerWaveMiniportNodes),   // NodeCount
    UsbHsSpeakerWaveMiniportNodes,                 // Nodes
    SIZEOF_ARRAY(UsbHsSpeakerWaveMiniportConnections),// ConnectionCount
    UsbHsSpeakerWaveMiniportConnections,           // Connections
    0,                                              // CategoryCount
    NULL                                            // Categories  - use defaults (audio, render, capture)
};

#endif // _SYSVAD_USBHSSPEAKERWAVTABLE_H_



<|start_filename|>gnss/gnssUmdf/precomp.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    precomp.h

Abstract:

    This module contains the common type definitions and includes.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

//
// Include the type specific headers.
//
#include <windows.h>
#include <wdf.h>
#include <initguid.h>
#include <sstream>
#include <vector>
#include <gnssdriver.h>


<|start_filename|>audio/tests/KSPosTst/locallimits.h<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
//  locallimits.h
//
// Abstract:
//
//  defines the tolerances for WHQL failures for this test app
//
// -------------------------------------------------------------------------------

// max latency for rendering or capturing is 20 ms
#define LATENCY_THRESHOLD           20.

//  Real time sampling rates must be within 1% of the theoretical rate,
//  per the format, as required by the PC98/PC99 HW Design Guide.
#define SAMPLERATE_THRESHOLD        1.0

//  audio devices are required to be SAMPLEACCURATE (PC9x HW Design Guide)..  
//  but for some reason we are making them ms accurate??
#define JITTER_STD_THRESHOLD        1.0       // 1 ms max allowed for std-deviation
#define JITTER_MAX_THRESHOLD        4.0       // 4 ms max allowed for max deviation


<|start_filename|>hid/vhidmini2/inc/common.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    common.h

Environment:

    User mode

--*/

#ifndef __VHIDMINI_COMMON_H__
#define __VHIDMINI_COMMON_H__

//
// Custom control codes are defined here. They are to be used for sideband 
// communication with the hid minidriver. These control codes are sent to 
// the hid minidriver using Hid_SetFeature() API to a custom collection 
// defined especially to handle such requests.
//
#define  HIDMINI_CONTROL_CODE_SET_ATTRIBUTES              0x00
#define  HIDMINI_CONTROL_CODE_DUMMY1                      0x01
#define  HIDMINI_CONTROL_CODE_DUMMY2                      0x02

//
// This is the report id of the collection to which the control codes are sent
//
#define CONTROL_COLLECTION_REPORT_ID                      0x01
#define TEST_COLLECTION_REPORT_ID                         0x02

#define MAXIMUM_STRING_LENGTH           (126 * sizeof(WCHAR))
#define VHIDMINI_MANUFACTURER_STRING    L"UMDF Virtual hidmini device Manufacturer string"  
#define VHIDMINI_PRODUCT_STRING         L"UMDF Virtual hidmini device Product string"  
#define VHIDMINI_SERIAL_NUMBER_STRING   L"UMDF Virtual hidmini device Serial Number string"  
#define VHIDMINI_DEVICE_STRING          L"UMDF Virtual hidmini device"  
#define VHIDMINI_DEVICE_STRING_INDEX    5
#include <pshpack1.h>

typedef struct _MY_DEVICE_ATTRIBUTES {

    USHORT          VendorID;
    USHORT          ProductID;
    USHORT          VersionNumber;

} MY_DEVICE_ATTRIBUTES, *PMY_DEVICE_ATTRIBUTES;

typedef struct _HIDMINI_CONTROL_INFO {

    //
    //report ID of the collection to which the control request is sent
    //
    UCHAR    ReportId;   

    //
    // One byte control code (user-defined) for communication with hid 
    // mini driver
    //
    UCHAR   ControlCode;

    //
    // This union contains input data for the control request.
    //
    union {
        MY_DEVICE_ATTRIBUTES Attributes;
        struct {
            ULONG Dummy1;
            ULONG Dummy2;
        } Dummy;
    } u;
    
} HIDMINI_CONTROL_INFO, * PHIDMINI_CONTROL_INFO;

//
// input from device to system
//
typedef struct _HIDMINI_INPUT_REPORT {
    
    UCHAR ReportId;   

    UCHAR Data; 

} HIDMINI_INPUT_REPORT, *PHIDMINI_INPUT_REPORT;

//
// output to device from system
//
typedef struct _HIDMINI_OUTPUT_REPORT {
    
    UCHAR ReportId;   

    UCHAR Data; 

    USHORT Pad1;

    ULONG Pad2;

} HIDMINI_OUTPUT_REPORT, *PHIDMINI_OUTPUT_REPORT;

#include <poppack.h>

//
// SetFeature request requires that the feature report buffer size be exactly 
// same as the size of report described in the hid report descriptor (
// excluding the report ID). Since HIDMINI_CONTROL_INFO includes report ID,
// we subtract one from the size.
//
#define FEATURE_REPORT_SIZE_CB      ((USHORT)(sizeof(HIDMINI_CONTROL_INFO) - 1))
#define INPUT_REPORT_SIZE_CB        ((USHORT)(sizeof(HIDMINI_INPUT_REPORT) - 1))
#define OUTPUT_REPORT_SIZE_CB       ((USHORT)(sizeof(HIDMINI_OUTPUT_REPORT) - 1))

#endif //__VHIDMINI_COMMON_H__


<|start_filename|>avstream/avscamera/sys/formats.cpp<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2001, Microsoft Corporation.

    File:

        Formats.cpp

    Abstract:

        A list of available Image and Video formats.  

    History:

        created 2/12/2016

**************************************************************************/

#include "Common.h"
#include "ntintsafe.h"

#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA

//  Define 720p resolution.
#define D720P_W 1280
#define D720P_H 720

//  Add 20% for overscan.
#define D720P_W_OS (1280+(1280/5))
#define D720P_H_OS (720+(720/5))


/**************************************************************************

    DISPATCH AND DESCRIPTOR LAYOUT

**************************************************************************/

DEFINE_DATARANGE_VIDEO(
    FormatNV12_CaptureVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_NV12),
    DMAX_X, DMAX_Y,
    30,
    1,
    12,
    FOURCC_NV12)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureQVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    D_X, D_Y,
    30,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureQVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    D_X_OS, D_Y_OS,
    30,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X, DMAX_Y,
    30,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X_OS, DMAX_Y_OS,
    30,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureQVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    D_X, D_Y,
    30,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureQVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    D_X_OS, D_Y_OS,
    30,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X, DMAX_Y,
    30,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X_OS, DMAX_Y_OS,
    30,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_60fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X, DMAX_Y,
    60,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_60fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X_OS, DMAX_Y_OS,
    60,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_60fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X, DMAX_Y,
    60,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_60fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X_OS, DMAX_Y_OS,
    60,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_90fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X, DMAX_Y,
    90,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_90fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X_OS, DMAX_Y_OS,
    90,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_90fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X, DMAX_Y,
    90,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_90fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X_OS, DMAX_Y_OS,
    90,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_120fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X, DMAX_Y,
    120,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatYUY2_CaptureVGA_120fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_YUY2),
    DMAX_X_OS, DMAX_Y_OS,
    120,
    1,
    16,
    FOURCC_YUY2)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_120fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X, DMAX_Y,
    120,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB24Bpp_CaptureVGA_120fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB24),
    DMAX_X_OS, DMAX_Y_OS,
    120,
    1,
    24,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X, DMAX_Y,
    30,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X_OS, DMAX_Y_OS,
    30,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_60fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X, DMAX_Y,
    60,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_60fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X_OS, DMAX_Y_OS,
    60,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_90fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X, DMAX_Y,
    90,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_90fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X_OS, DMAX_Y_OS,
    90,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_120fps,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X, DMAX_Y,
    120,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureVGA_120fpsOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    DMAX_X_OS, DMAX_Y_OS,
    120,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureQVGA,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    D_X, D_Y,
    30,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatRGB32Bpp_CaptureQVGAOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_RGB32),
    D_X_OS, D_Y_OS,
    30,
    1,
    32,
    KS_BI_RGB)

DEFINE_DATARANGE_VIDEO(
    FormatNV12_720p,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_NV12),
    D720P_W, D720P_H,
    30,
    1,
    12,
    FOURCC_NV12)

DEFINE_DATARANGE_VIDEO(
    FormatNV12_720pOverscan,
    STATICGUIDOF(KSDATAFORMAT_SUBTYPE_NV12),
    D720P_W_OS, D720P_H_OS,
    30,
    1,
    12,
    FOURCC_NV12)

//
// CapturePinDataRanges:
//
// This is the list of data ranges supported on the capture pin.  We support
// two: one RGB24, and one YUY2.
//
const
PKSDATARANGE
ImageCapturePinDataRanges[ IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT ] =
{
    (PKSDATARANGE) &FormatYUY2_CaptureVGA,
    (PKSDATARANGE) &FormatNV12_CaptureVGA 
};

//
// CapturePinDataRanges:
//
// This is the list of data ranges supported on the capture pin.  We support
// RGB24, YUY2, and RGB32
//
const
PKSDATARANGE
VideoCapturePinDataRanges [VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT] =
{
    (PKSDATARANGE) &FormatYUY2_CaptureQVGA,
    (PKSDATARANGE) &FormatYUY2_CaptureQVGAOverscan,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA,
    (PKSDATARANGE) &FormatYUY2_CaptureVGAOverscan,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGAOverscan,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGAOverscan,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fpsOverscan,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fpsOverscan,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fpsOverscan,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fpsOverscan,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fpsOverscan,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fpsOverscan,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGAOverscan,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fpsOverscan,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fpsOverscan,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fpsOverscan,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGAOverscan,
    (PKSDATARANGE) &FormatNV12_720p,
    (PKSDATARANGE) &FormatNV12_720pOverscan
};

const
PKSDATARANGE
VideoPreviewPinDataRanges[VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT] =
{
    (PKSDATARANGE) &FormatYUY2_CaptureQVGA,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps,
    (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA,
    (PKSDATARANGE) &FormatNV12_720p
};


<|start_filename|>audio/simpleaudiosample/Source/Main/adapter.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    adapter.cpp

Abstract:

    Setup and miniport installation.  No resources are used by simple audio sample.
    This sample is to demonstrate how to develop a full featured audio miniport driver.
--*/

#pragma warning (disable : 4127)

//
// All the GUIDS for all the miniports end up in this object.
//
#define PUT_GUIDS_HERE

#include "definitions.h"
#include "endpoints.h"
#include "minipairs.h"

typedef void (*fnPcDriverUnload) (PDRIVER_OBJECT);
fnPcDriverUnload gPCDriverUnloadRoutine = NULL;
extern "C" DRIVER_UNLOAD DriverUnload;

//-----------------------------------------------------------------------------
// Referenced forward.
//-----------------------------------------------------------------------------

DRIVER_ADD_DEVICE AddDevice;

NTSTATUS
StartDevice
( 
    _In_  PDEVICE_OBJECT,      
    _In_  PIRP,                
    _In_  PRESOURCELIST        
); 

_Dispatch_type_(IRP_MJ_PNP)
DRIVER_DISPATCH PnpHandler;

//
// Rendering streams are saved to a file by default. Use the registry value 
// DoNotCreateDataFiles (DWORD) > 0 to override this default.
//
DWORD g_DoNotCreateDataFiles = 0;  // default is off.
DWORD g_DisableToneGenerator = 0;  // default is to generate tones.
UNICODE_STRING g_RegistryPath;      // This is used to store the registry settings path for the driver

//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------

#pragma code_seg("PAGE")
void ReleaseRegistryStringBuffer()
{
    if (g_RegistryPath.Buffer != NULL)
    {
        ExFreePool(g_RegistryPath.Buffer);
        g_RegistryPath.Buffer = NULL;
        g_RegistryPath.Length = 0;
        g_RegistryPath.MaximumLength = 0;
    }
}

//=============================================================================
#pragma code_seg("PAGE")
extern "C"
void DriverUnload 
(
    _In_ PDRIVER_OBJECT DriverObject
)
/*++

Routine Description:

  Our driver unload routine. This just frees the WDF driver object.

Arguments:

  DriverObject - pointer to the driver object

Environment:

    PASSIVE_LEVEL

--*/
{
    PAGED_CODE(); 

    DPF(D_TERSE, ("[DriverUnload]"));

    ReleaseRegistryStringBuffer();

    if (DriverObject == NULL)
    {
        goto Done;
    }
    
    //
    // Invoke first the port unload.
    //
    if (gPCDriverUnloadRoutine != NULL)
    {
        gPCDriverUnloadRoutine(DriverObject);
    }

    //
    // Unload WDF driver object. 
    //
    if (WdfGetDriver() != NULL)
    {
        WdfDriverMiniportUnload(WdfGetDriver());
    }
Done:
    return;
}

//=============================================================================
#pragma code_seg("INIT")
__drv_requiresIRQL(PASSIVE_LEVEL)
NTSTATUS
CopyRegistrySettingsPath(
    _In_ PUNICODE_STRING RegistryPath
)
/*++

Routine Description:

Copies the following registry path to a global variable.

\REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters

Arguments:

RegistryPath - Registry path passed to DriverEntry

Returns:

NTSTATUS - SUCCESS if able to configure the framework

--*/

{
    // Initializing the unicode string, so that if it is not allocated it will not be deallocated too.
    RtlInitUnicodeString(&g_RegistryPath, NULL);

    g_RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);

    g_RegistryPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, g_RegistryPath.MaximumLength, MINADAPTER_POOLTAG);

    if (g_RegistryPath.Buffer == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlAppendUnicodeToString(&g_RegistryPath, RegistryPath->Buffer);

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("INIT")
__drv_requiresIRQL(PASSIVE_LEVEL)
NTSTATUS
GetRegistrySettings(
    _In_ PUNICODE_STRING RegistryPath
   )
/*++

Routine Description:

    Initialize Driver Framework settings from the driver
    specific registry settings under

    \REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\<driver>\Parameters

Arguments:

    RegistryPath - Registry path passed to DriverEntry

Returns:

    NTSTATUS - SUCCESS if able to configure the framework

--*/

{
    NTSTATUS                    ntStatus;
    UNICODE_STRING              parametersPath;
    RTL_QUERY_REGISTRY_TABLE    paramTable[] = {
    // QueryRoutine     Flags                                               Name                     EntryContext             DefaultType                                                    DefaultData              DefaultLength
        { NULL,   RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DoNotCreateDataFiles", &g_DoNotCreateDataFiles, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DoNotCreateDataFiles, sizeof(ULONG)},
        { NULL,   RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"DisableToneGenerator", &g_DisableToneGenerator, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &g_DisableToneGenerator, sizeof(ULONG)},
        { NULL,   0,                                                        NULL,                    NULL,                    0,                                                             NULL,                    0}
    };

    DPF(D_TERSE, ("[GetRegistrySettings]"));

    PAGED_CODE(); 

    RtlInitUnicodeString(&parametersPath, NULL);

    parametersPath.MaximumLength =
        RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR);

    parametersPath.Buffer = (PWCH) ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINADAPTER_POOLTAG);
    if (parametersPath.Buffer == NULL) 
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlAppendUnicodeToString(&parametersPath, RegistryPath->Buffer);
    RtlAppendUnicodeToString(&parametersPath, L"\\Parameters");

    ntStatus = RtlQueryRegistryValues(
                 RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
                 parametersPath.Buffer,
                 &paramTable[0],
                 NULL,
                 NULL
                );

    if (!NT_SUCCESS(ntStatus)) 
    {
        DPF(D_VERBOSE, ("RtlQueryRegistryValues failed, using default values, 0x%x", ntStatus));
        //
        // Don't return error because we will operate with default values.
        //
    }

    //
    // Dump settings.
    //
    DPF(D_VERBOSE, ("DoNotCreateDataFiles: %u", g_DoNotCreateDataFiles));
    DPF(D_VERBOSE, ("DisableToneGenerator: %u", g_DisableToneGenerator));

    //
    // Cleanup.
    //
    ExFreePool(parametersPath.Buffer);

    return STATUS_SUCCESS;
}

#pragma code_seg("INIT")
extern "C" DRIVER_INITIALIZE DriverEntry;
extern "C" NTSTATUS
DriverEntry
( 
    _In_  PDRIVER_OBJECT          DriverObject,
    _In_  PUNICODE_STRING         RegistryPathName
)
{
/*++

Routine Description:

  Installable driver initialization entry point.
  This entry point is called directly by the I/O system.

  All audio adapter drivers can use this code without change.

Arguments:

  DriverObject - pointer to the driver object

  RegistryPath - pointer to a unicode string representing the path,
                   to driver-specific key in the registry.

Return Value:

  STATUS_SUCCESS if successful,
  STATUS_UNSUCCESSFUL otherwise.

--*/
    NTSTATUS                    ntStatus;
    WDF_DRIVER_CONFIG           config;

    DPF(D_TERSE, ("[DriverEntry]"));

    // Copy registry Path name in a global variable to be used by modules inside driver.
    // !! NOTE !! Inside this function we are initializing the registrypath, so we MUST NOT add any failing calls
    // before the following call.
    ntStatus = CopyRegistrySettingsPath(RegistryPathName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Registry path copy error 0x%x", ntStatus)),
        Done);

    //
    // Get registry configuration.
    //
    ntStatus = GetRegistrySettings(RegistryPathName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)),
        Done);
    
    WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
    //
    // Set WdfDriverInitNoDispatchOverride flag to tell the framework
    // not to provide dispatch routines for the driver. In other words,
    // the framework must not intercept IRPs that the I/O manager has
    // directed to the driver. In this case, they will be handled by Audio
    // port driver.
    //
    config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
    config.DriverPoolTag    = MINADAPTER_POOLTAG;

    ntStatus = WdfDriverCreate(DriverObject,
                               RegistryPathName,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &config,
                               WDF_NO_HANDLE);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Tell the class driver to initialize the driver.
    //
    ntStatus =  PcInitializeAdapterDriver(DriverObject,
                                          RegistryPathName,
                                          (PDRIVER_ADD_DEVICE)AddDevice);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)),
        Done);

    //
    // To intercept stop/remove/surprise-remove.
    //
    DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler;

    //
    // Hook the port class unload function
    //
    gPCDriverUnloadRoutine = DriverObject->DriverUnload;
    DriverObject->DriverUnload = DriverUnload;

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;
    
Done:

    if (!NT_SUCCESS(ntStatus))
    {
        if (WdfGetDriver() != NULL)
        {
            WdfDriverMiniportUnload(WdfGetDriver());
        }

        ReleaseRegistryStringBuffer();
    }
    
    return ntStatus;
} // DriverEntry

#pragma code_seg()
// disable prefast warning 28152 because 
// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice
#pragma warning(disable:28152)
#pragma code_seg("PAGE")
//=============================================================================
NTSTATUS AddDevice
( 
    _In_  PDRIVER_OBJECT    DriverObject,
    _In_  PDEVICE_OBJECT    PhysicalDeviceObject 
)
/*++

Routine Description:

  The Plug & Play subsystem is handing us a brand new PDO, for which we
  (by means of INF registration) have been asked to provide a driver.

  We need to determine if we need to be in the driver stack for the device.
  Create a function device object to attach to the stack
  Initialize that device object
  Return status success.

  All audio adapter drivers can use this code without change.

Arguments:

  DriverObject - pointer to a driver object

  PhysicalDeviceObject -  pointer to a device object created by the
                            underlying bus driver.

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    NTSTATUS        ntStatus;
    ULONG           maxObjects;

    DPF(D_TERSE, ("[AddDevice]"));

    maxObjects = g_MaxMiniports;

    // Tell the class driver to add the device.
    //
    ntStatus = 
        PcAddAdapterDevice
        ( 
            DriverObject,
            PhysicalDeviceObject,
            PCPFNSTARTDEVICE(StartDevice),
            maxObjects,
            0
        );

    return ntStatus;
} // AddDevice

#pragma code_seg()
NTSTATUS
_IRQL_requires_max_(DISPATCH_LEVEL)
PowerControlCallback
(
    _In_        LPCGUID PowerControlCode,
    _In_opt_    PVOID   InBuffer,
    _In_        SIZE_T  InBufferSize,
    _Out_writes_bytes_to_(OutBufferSize, *BytesReturned) PVOID OutBuffer,
    _In_        SIZE_T  OutBufferSize,
    _Out_opt_   PSIZE_T BytesReturned,
    _In_opt_    PVOID   Context
)
{
    UNREFERENCED_PARAMETER(PowerControlCode);
    UNREFERENCED_PARAMETER(InBuffer);
    UNREFERENCED_PARAMETER(InBufferSize);
    UNREFERENCED_PARAMETER(OutBuffer);
    UNREFERENCED_PARAMETER(OutBufferSize);
    UNREFERENCED_PARAMETER(BytesReturned);
    UNREFERENCED_PARAMETER(Context);
    
    return STATUS_NOT_IMPLEMENTED;
}

#pragma code_seg("PAGE")
NTSTATUS 
InstallEndpointRenderFilters(
    _In_ PDEVICE_OBJECT     _pDeviceObject, 
    _In_ PIRP               _pIrp, 
    _In_ PADAPTERCOMMON     _pAdapterCommon,
    _In_ PENDPOINT_MINIPAIR _pAeMiniports
    )
{
    NTSTATUS                    ntStatus                = STATUS_SUCCESS;
    PUNKNOWN                    unknownTopology         = NULL;
    PUNKNOWN                    unknownWave             = NULL;
    PPORTCLSETWHELPER           pPortClsEtwHelper       = NULL;
#ifdef _USE_IPortClsRuntimePower
    PPORTCLSRUNTIMEPOWER        pPortClsRuntimePower    = NULL;
#endif // _USE_IPortClsRuntimePower
    PPORTCLSStreamResourceManager pPortClsResMgr        = NULL;
    PPORTCLSStreamResourceManager2 pPortClsResMgr2      = NULL;

    PAGED_CODE();
    
    UNREFERENCED_PARAMETER(_pDeviceObject);

    ntStatus = _pAdapterCommon->InstallEndpointFilters(
        _pIrp,
        _pAeMiniports,
        NULL,
        &unknownTopology,
        &unknownWave,
        NULL, NULL);

    if (unknownWave) // IID_IPortClsEtwHelper and IID_IPortClsRuntimePower interfaces are only exposed on the WaveRT port.
    {
        ntStatus = unknownWave->QueryInterface (IID_IPortClsEtwHelper, (PVOID *)&pPortClsEtwHelper);
        if (NT_SUCCESS(ntStatus))
        {
            _pAdapterCommon->SetEtwHelper(pPortClsEtwHelper);
            ASSERT(pPortClsEtwHelper != NULL);
            pPortClsEtwHelper->Release();
        }

#ifdef _USE_IPortClsRuntimePower
        // Let's get the runtime power interface on PortCls.  
        ntStatus = unknownWave->QueryInterface(IID_IPortClsRuntimePower, (PVOID *)&pPortClsRuntimePower);
        if (NT_SUCCESS(ntStatus))
        {
            // This interface would typically be stashed away for later use.  Instead,
            // let's just send an empty control with GUID_NULL.
            NTSTATUS ntStatusTest =
                pPortClsRuntimePower->SendPowerControl
                (
                    _pDeviceObject,
                    &GUID_NULL,
                    NULL,
                    0,
                    NULL,
                    0,
                    NULL
                );

            if (NT_SUCCESS(ntStatusTest) || STATUS_NOT_IMPLEMENTED == ntStatusTest || STATUS_NOT_SUPPORTED == ntStatusTest)
            {
                ntStatus = pPortClsRuntimePower->RegisterPowerControlCallback(_pDeviceObject, &PowerControlCallback, NULL);
                if (NT_SUCCESS(ntStatus))
                {
                    ntStatus = pPortClsRuntimePower->UnregisterPowerControlCallback(_pDeviceObject);
                }
            }
            else
            {
                ntStatus = ntStatusTest;
            }

            pPortClsRuntimePower->Release();
        }
#endif // _USE_IPortClsRuntimePower

        //
        // Test: add and remove current thread as streaming audio resource.  
        // In a real driver you should only add interrupts and driver-owned threads 
        // (i.e., do NOT add the current thread as streaming resource).
        //
        // testing IPortClsStreamResourceManager:
        ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager, (PVOID *)&pPortClsResMgr);
        if (NT_SUCCESS(ntStatus))
        {
            PCSTREAMRESOURCE_DESCRIPTOR res;
            PCSTREAMRESOURCE hRes = NULL;
            PDEVICE_OBJECT pdo = NULL;

            PcGetPhysicalDeviceObject(_pDeviceObject, &pdo);
            PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res);
            res.Pdo = pdo;
            res.Type = ePcStreamResourceThread;
            res.Resource.Thread = PsGetCurrentThread();
            
            NTSTATUS ntStatusTest = pPortClsResMgr->AddStreamResource(NULL, &res, &hRes);
            if (NT_SUCCESS(ntStatusTest))
            {
                pPortClsResMgr->RemoveStreamResource(hRes);
                hRes = NULL;
            }

            pPortClsResMgr->Release();
            pPortClsResMgr = NULL;
        }
        
        // testing IPortClsStreamResourceManager2:
        ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager2, (PVOID *)&pPortClsResMgr2);
        if (NT_SUCCESS(ntStatus))
        {
            PCSTREAMRESOURCE_DESCRIPTOR res;
            PCSTREAMRESOURCE hRes = NULL;
            PDEVICE_OBJECT pdo = NULL;

            PcGetPhysicalDeviceObject(_pDeviceObject, &pdo);
            PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res);
            res.Pdo = pdo;
            res.Type = ePcStreamResourceThread;
            res.Resource.Thread = PsGetCurrentThread();
            
            NTSTATUS ntStatusTest = pPortClsResMgr2->AddStreamResource2(pdo, NULL, &res, &hRes);
            if (NT_SUCCESS(ntStatusTest))
            {
                pPortClsResMgr2->RemoveStreamResource(hRes);
                hRes = NULL;
            }

            pPortClsResMgr2->Release();
            pPortClsResMgr2 = NULL;
        }
    }

    SAFE_RELEASE(unknownTopology);
    SAFE_RELEASE(unknownWave);

    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS 
InstallAllRenderFilters(
    _In_ PDEVICE_OBJECT _pDeviceObject, 
    _In_ PIRP           _pIrp, 
    _In_ PADAPTERCOMMON _pAdapterCommon
    )
{
    NTSTATUS            ntStatus;
    PENDPOINT_MINIPAIR* ppAeMiniports   = g_RenderEndpoints;
    
    PAGED_CODE();

    for(ULONG i = 0; i < g_cRenderEndpoints; ++i, ++ppAeMiniports)
    {
        ntStatus = InstallEndpointRenderFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports);
        IF_FAILED_JUMP(ntStatus, Exit);
    }
    
    ntStatus = STATUS_SUCCESS;

Exit:
    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS
InstallEndpointCaptureFilters(
    _In_ PDEVICE_OBJECT     _pDeviceObject,
    _In_ PIRP               _pIrp,
    _In_ PADAPTERCOMMON     _pAdapterCommon,
    _In_ PENDPOINT_MINIPAIR _pAeMiniports
)
{
    NTSTATUS    ntStatus = STATUS_SUCCESS;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(_pDeviceObject);

    ntStatus = _pAdapterCommon->InstallEndpointFilters(
        _pIrp,
        _pAeMiniports,
        NULL,
        NULL,
        NULL,
        NULL, NULL);

    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS
InstallAllCaptureFilters(
    _In_ PDEVICE_OBJECT _pDeviceObject,
    _In_ PIRP           _pIrp,
    _In_ PADAPTERCOMMON _pAdapterCommon
)
{
    NTSTATUS            ntStatus;
    PENDPOINT_MINIPAIR* ppAeMiniports = g_CaptureEndpoints;

    PAGED_CODE();

    for (ULONG i = 0; i < g_cCaptureEndpoints; ++i, ++ppAeMiniports)
    {
        ntStatus = InstallEndpointCaptureFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports);
        IF_FAILED_JUMP(ntStatus, Exit);
    }

    ntStatus = STATUS_SUCCESS;

Exit:
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
StartDevice
( 
    _In_  PDEVICE_OBJECT          DeviceObject,     
    _In_  PIRP                    Irp,              
    _In_  PRESOURCELIST           ResourceList      
)  
{
/*++

Routine Description:

  This function is called by the operating system when the device is 
  started.
  It is responsible for starting the miniports.  This code is specific to    
  the adapter because it calls out miniports for functions that are specific 
  to the adapter.                                                            

Arguments:

  DeviceObject - pointer to the driver object

  Irp - pointer to the irp 

  ResourceList - pointer to the resource list assigned by PnP manager

Return Value:

  NT status code.

--*/
    UNREFERENCED_PARAMETER(ResourceList);

    PAGED_CODE();

    ASSERT(DeviceObject);
    ASSERT(Irp);
    ASSERT(ResourceList);

    NTSTATUS                    ntStatus        = STATUS_SUCCESS;

    PADAPTERCOMMON              pAdapterCommon  = NULL;
    PUNKNOWN                    pUnknownCommon  = NULL;
    PortClassDeviceContext*     pExtension      = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension);

    DPF_ENTER(("[StartDevice]"));

    //
    // create a new adapter common object
    //
    ntStatus = NewAdapterCommon( 
                                &pUnknownCommon,
                                IID_IAdapterCommon,
                                NULL,
                                POOL_FLAG_NON_PAGED 
                                );
    IF_FAILED_JUMP(ntStatus, Exit);

    ntStatus = pUnknownCommon->QueryInterface( IID_IAdapterCommon,(PVOID *) &pAdapterCommon);
    IF_FAILED_JUMP(ntStatus, Exit);

    ntStatus = pAdapterCommon->Init(DeviceObject);
    IF_FAILED_JUMP(ntStatus, Exit);

    //
    // register with PortCls for power-management services
    ntStatus = PcRegisterAdapterPowerManagement( PUNKNOWN(pAdapterCommon), DeviceObject);
    IF_FAILED_JUMP(ntStatus, Exit);

    //
    // Install wave+topology filters for render devices
    //
    ntStatus = InstallAllRenderFilters(DeviceObject, Irp, pAdapterCommon);
    IF_FAILED_JUMP(ntStatus, Exit);

    //
    // Install wave+topology filters for capture devices
    //
    ntStatus = InstallAllCaptureFilters(DeviceObject, Irp, pAdapterCommon);
    IF_FAILED_JUMP(ntStatus, Exit);

Exit:

    //
    // Stash the adapter common object in the device extension so
    // we can access it for cleanup on stop/removal.
    //
    if (pAdapterCommon)
    {
        ASSERT(pExtension != NULL);
        pExtension->m_pCommon = pAdapterCommon;
    }

    //
    // Release the adapter IUnknown interface.
    //
    SAFE_RELEASE(pUnknownCommon);
    
    return ntStatus;
} // StartDevice

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS 
PnpHandler
(
    _In_ DEVICE_OBJECT *_DeviceObject, 
    _Inout_ IRP *_Irp
)
/*++

Routine Description:

  Handles PnP IRPs                                                           

Arguments:

  _DeviceObject - Functional Device object pointer.

  _Irp - The Irp being passed

Return Value:

  NT status code.

--*/
{
    NTSTATUS                ntStatus = STATUS_UNSUCCESSFUL;
    IO_STACK_LOCATION      *stack;
    PortClassDeviceContext *ext;

    // Documented https://msdn.microsoft.com/en-us/library/windows/hardware/ff544039(v=vs.85).aspx
    // This method will be called in IRQL PASSIVE_LEVEL
#pragma warning(suppress: 28118)
    PAGED_CODE(); 

    ASSERT(_DeviceObject);
    ASSERT(_Irp);

    //
    // Check for the REMOVE_DEVICE irp.  If we're being unloaded, 
    // uninstantiate our devices and release the adapter common
    // object.
    //
    stack = IoGetCurrentIrpStackLocation(_Irp);

    switch (stack->MinorFunction)
    {
    case IRP_MN_REMOVE_DEVICE:
    case IRP_MN_SURPRISE_REMOVAL:
    case IRP_MN_STOP_DEVICE:
        ext = static_cast<PortClassDeviceContext*>(_DeviceObject->DeviceExtension);

        if (ext->m_pCommon != NULL)
        {
            ext->m_pCommon->Cleanup();
            
            ext->m_pCommon->Release();
            ext->m_pCommon = NULL;
        }
        break;

    default:
        break;
    }
    
    ntStatus = PcDispatchIrp(_DeviceObject, _Irp);

    return ntStatus;
}

#pragma code_seg()



<|start_filename|>avstream/avscamera/sys/PreviewHwSim.h<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2014, Microsoft Corporation.

    File:

        PreviewHwSim.h

    Abstract:

        This file contains the definition of the CPreviewHardwareSimulation class.

        This is a specialization of CHardwareSimulation that provides preview-
        specific metadata.

    History:

        created 5/28/2014

**************************************************************************/

#pragma once

//  forward references
struct ISP_FRAME_SETTINGS;

class CPreviewHardwareSimulation :
    public CHardwareSimulation
{
public:
    CPreviewHardwareSimulation(
        _Inout_ CSensor *Sensor,
        _In_    LONG PinID
    );

    virtual ~CPreviewHardwareSimulation();

    virtual
    void
    FakeHardware();

protected:
    METADATA_PREVIEWAGGREGATION
        GetMetadata();

    VOID GetCaptureStats(
        _In_ PKSCAMERA_METADATA_CAPTURESTATS pCaptureStats
    );

    //  Inject preview pin-specific metadata.
    virtual
    void
    EmitMetadata(
        _Inout_ PKSSTREAM_HEADER   pStreamHeader
    );

private:
    BOOLEAN m_Illuminated;

};



<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/stdafx.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

--*/

#pragma once

#include <Windows.h>
#include <strsafe.h>

//
// Device context
//
#include <cfgmgr32.h>

#include <initguid.h>

#include "ServiceWin32API.h"
#include "DeviceContext.h"
#include "DeviceControl.h"


<|start_filename|>audio/tests/PinResourceHelpers/TestResourceHelper.h<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
//  TestResourceHelpers.h
//
// Abstract:
//
//  Header for BuildResourceList helpers.
//
// -------------------------------------------------------------------------------
#pragma once

#include <HalfApp.h>

// ----------------------------------------------------------------------
HRESULT GetProcessingModesForConnector
(
    IMMDevice*              pDevice,
    UINT                    uConnectorId,
    EndpointConnectorType   eConnectorType,
    ULONG*                  pCount,
    AUDIO_SIGNALPROCESSINGMODE** ppModes
);

// ----------------------------------------------------------------------
HRESULT GetDefaultFormatForConnector
(
    IMMDevice*              pDevice,
    EndpointConnectorType   eConnectorType,
    WAVEFORMATEX**          ppDefaultFormat
);

// ----------------------------------------------------------------------
HRESULT GetPreferredFormatForConnector
(
    IMMDevice*                  pDevice,
    UINT                        uConnectorId,
    EndpointConnectorType       eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    WAVEFORMATEX**              ppPreferredFormat
);

// ----------------------------------------------------------------------
HRESULT GetPreferredFormatPeriodicityCharacteristicsForConnector
(
    IMMDevice*                  pDevice,
    EndpointConnectorType       eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    STACKWISE_DATAFLOW          dataFlow,
    WAVEFORMATEX*               pPreferredFormat,
    ULONG                       cFormatRecords,
    PFORMAT_RECORD              pFormatRecords,
    UINT32*                     pDefaultPeriodicityInFrames,
    UINT32*                     pFundamentalPeriodicityInFrames,
    UINT32*                     pMinPeriodicityInFrames,
    UINT32*                     pMaxPeriodicityInFrames,
    UINT32*                     pMaxPeriodicityInFramesExtended
);

// ----------------------------------------------------------------------
HRESULT GetSupportedFormatRecordsForConnector
(
    IMMDevice*              pDevice,
    UINT                    uConnectorId,
    EndpointConnectorType   eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE mode,
    STACKWISE_DATAFLOW      dataFlow,
    ULONG*                  pCount,
    FORMAT_RECORD**         ppFormatRecords
);

// ----------------------------------------------------------------------
void VerifyAllEndpointsPluggedIn();

// ----------------------------------------------------------------------
HRESULT AddDevices
(
    WEX::TestExecution::ResourceList& resourceList
);


<|start_filename|>avstream/avscamera/sys/filter.h<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2001, Microsoft Corporation.

    File:

        filter.h

    Abstract:

        This file contains the filter level header for the base capture filter
        class.  It provides a wrapper on KSFILTER.  
        
        CCaptureFilter holds state information for the filter and contains
        implementations for standard camera controls.  These implementations
        validate arguments and capabilities of the control according to DDI
        definitions.  Once validated, control is passes to a CSensor object
        to manipulate the state of camera.

        CCaptureFilter holds a reference to any active CCapturePins that may 
        have been created on this filter.  The reference is used to obtain 
        the state of the pin and potentially modify the pin's allocator 
        framing.

        To add a new control, derive from CCaptureFilter and use one of the 
        DECLARE_PROPERTY_XXX() macros below to declare the property handlers.
        You can also use the DEFINE_PROP_ITEM_XXX() and DEFINE_STD_EVENT_ITEM()
        macros as aids in constructing a filter's automation table.

    History:

        created 3/12/2001

**************************************************************************/

extern KSPROPERTY_VALUES    ZoomRelativeValues;
extern KSPROPERTY_VALUES    ZoomValues;
extern KSPROPERTY_VALUES    PanValues;
extern KSPROPERTY_VALUES    RollValues;
extern KSPROPERTY_VALUES    TiltValues;
extern KSPROPERTY_VALUES    BacklightCompensationValues;
extern KSPROPERTY_VALUES    BrightnessValues;
extern KSPROPERTY_VALUES    ContrastValues;
extern KSPROPERTY_VALUES    HueValues;
extern KSPROPERTY_VALUES    PLFValues;
extern KSPROPERTY_VALUES    WhiteBalanceValues;
extern KSPROPERTY_VALUES    ExposureValues;
extern KSPROPERTY_VALUES    FocusValues;

class CCapturePin;
class CCaptureFilter
{
private:

    //
    // The AVStream filter object associated with this CCaptureFilter.
    //
    CCapturePin **m_pinArray;
    ULONG        *m_pMinimumRequestedFrames;

protected:
    //
    //  Setup, memory allocation, etc.
    //
    NTSTATUS
    Initialize();

    //
    //  Cleanup
    //
    void
    Cleanup();

    NTSTATUS
    GetExtrinsic(
        _In_opt_    KS_CAMERA_EXTRINSICS *Data,
        _In_        ULONG   PinId,
        _Inout_     ULONG   *Length);

    NTSTATUS
    GetIntrinsic(
        _In_opt_    KS_CAMERA_INTRINSICS *Data,
        _In_        ULONG   PinId,
        _Inout_     ULONG   *Length);

public:

    //
    //  setPin()
    //
    //  Attach a CCapturePin.
    //
    void
    setPin(
        _In_    CCapturePin *pPin,
        _In_    unsigned Id
    ) ;

    //
    //  getPin()
    //
    //  Query an attached CCapturePin.
    //
    CCapturePin *
    getPin(
        _In_    unsigned Id
    ) ;

    //
    //  CCaptureFilter()
    //
    //  Constructor.
    //
    CCaptureFilter (
        _In_    PKSFILTER Filter
    );

    //
    // ~CCaptureFilter():
    //
    // The capture filter destructor.
    //
    virtual
    ~CCaptureFilter ();

    //
    // DispatchCreate():
    //
    // This is the filter creation dispatch for the capture filter.  It
    // creates the CCaptureFilter object, associates it with the AVStream
    // object, and bags it for easy cleanup later.
    //
    static
    NTSTATUS
    DispatchCreate (
        _In_    PKSFILTER Filter,
        _In_    PIRP Irp
    );

    //
    // Close(): Called in response to IRP_MJ_CLOSE.
    //
    static
    NTSTATUS
    DispatchClose(
        _In_  PKSFILTER Filter,
        _In_  PIRP Irp
    );

    LARGE_INTEGER   m_StartTime;
    CCaptureDevice *m_pCaptureDevice;
    CSensor        *m_Sensor;
    PKSFILTER       m_pKSFilter;
    PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings;
    ULONG m_PFSSize;

    CNotifier   m_PhotoModeNotifier;
    CNotifier   m_PhotoMaxFrameRateNotifier;
    CNotifier   m_FocusNotifier;
    CNotifier   m_FocusRectNotifier;
    CNotifier   m_IsoNotifier;
    CNotifier   m_IsoAdvancedNotifier;
    CNotifier   m_EvCompensationNotifier;
    CNotifier   m_WhiteBalanceNotifier;
    CNotifier   m_ExposureNotifier;
    CNotifier   m_SceneModeNotifier;
    CNotifier   m_ThumbnailNotifier;
    CNotifier   m_WarmStartNotifier;
    CNotifier   m_RoiNotifier;
    CNotifier   m_ProfileNotifier;

    //
    //  The following static member template function is used to validate
    //  most KSPROPERTYs and thunk that call down to a handler that is a non-
    //  static member of CCaptureFilter (or a derived class).
    //
    //  Note: A separate instance of this thunking function is instantiated for 
    //        each control, so code space is not conserved.  However, performing 
    //        this logic in only one place ensures consistant behavior across 
    //        controls and reduces the chances for mistakes.  
    //
    template
    <typename OUTPUT,                       // type of data consumed by the handler.
     typename FILTER,
     NTSTATUS (FILTER::*pFun)(      // The local handling function.
         _Inout_ OUTPUT     *pOutput )>
    __declspec(code_seg("PAGE"))                // Put this function into paged code.
    static
    NTSTATUS
    Prop(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData)
    {
        PAGED_CODE();
        UNREFERENCED_PARAMETER(pProperty);

        NTSTATUS Status = STATUS_SUCCESS;
        NT_ASSERT(pIrp);

        FILTER *pFilter = reinterpret_cast <FILTER *>(KsGetFilterFromIrp(pIrp)->Context);
        PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
        ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
        OUTPUT *pOutput = reinterpret_cast<OUTPUT *>(pData);

        if (ulOutputBufferLength == 0)
        {
            pIrp->IoStatus.Information = sizeof(OUTPUT);
            Status = STATUS_BUFFER_OVERFLOW;
        }
        else if (ulOutputBufferLength < sizeof(OUTPUT))
        {
            Status = STATUS_BUFFER_TOO_SMALL;
        }
        else if (pData && ulOutputBufferLength >= sizeof(OUTPUT))
        {
            Status = (pFilter->*pFun)( pOutput );
            if( NT_SUCCESS(Status) )
            {
                pIrp->IoStatus.Information = sizeof(OUTPUT);
            }
        }
        else
        {
            Status = STATUS_INVALID_PARAMETER;
        }

        return Status;
    }

    //  Get Per Frame Setting Caps
    static
    NTSTATUS
    GetPFSCaps(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData
    );

    //  Get current Per Frame Settings
    static
    NTSTATUS
    GetPerFrameSettings(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData
    );

    //  Set new Per Frame Settings
    static
    NTSTATUS
    SetPerFrameSettings(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData
    );

    //  Clear Per Frame Settings
    static
    NTSTATUS
    ClearPerFrameSettings(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData
    );

//
//  DECLARE_PROPERTY_XXX() macros.
//
//  The follow macros declare a property handler using a common naming 
//  convention.  This convention is unique to this sample code.  The 
//  convention is used again in the DEFINE_PROP_ITEM_XXX() macros during
//  construction of the automation tables.
//
//  Note: These definitions are non-static members of the filter.
//

//  Declare a property's GET handler.
#define DECLARE_PROPERTY_GET_HANDLER( type, name )      \
    NTSTATUS                                            \
    Get##name(                                          \
        _Inout_ type        *pOutput                    \
        );

//  Declare a property's SET handler.
#define DECLARE_PROPERTY_SET_HANDLER( type, name )      \
    NTSTATUS                                            \
    Set##name(                                          \
        _In_    type        *pOutput                    \
        );

//  Declare a property's GET and SET handlers.
#define DECLARE_PROPERTY_HANDLERS( type, name )         \
DECLARE_PROPERTY_GET_HANDLER( type, name )              \
DECLARE_PROPERTY_SET_HANDLER( type, name )

    //
    //  Declaration of all the standard control handlers that use fixed structures.
    //
    DECLARE_PROPERTY_HANDLERS( CExtendedPhotoMode, PhotoMode );
    DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, PhotoFrameRate );
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoMaxFrameRate );
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, WarmStart );
    DECLARE_PROPERTY_HANDLERS( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes );
    DECLARE_PROPERTY_GET_HANDLER( CExtendedFieldOfView, FieldOfView );
    DECLARE_PROPERTY_GET_HANDLER( CExtendedCameraAngleOffset, CameraAngleOffset );
    DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect );
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Focus );
    DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, FocusState );
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, FocusPriority );
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, ExtendedFlash )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Iso )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, IsoAdvanced )
    DECLARE_PROPERTY_HANDLERS( CExtendedEvCompensation, EvCompensation )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, WhiteBalance )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Exposure )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, FaceDetection )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoConfirmation )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, SceneMode )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VFR )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoHDR )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Zoom )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoStabilization)
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Histogram )
    DECLARE_PROPERTY_HANDLERS( CExtendedMetadata, Metadata )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OpticalImageStabilization )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OptimizationHint )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, AdvancedPhoto )
    DECLARE_PROPERTY_HANDLERS( CExtendedProfile, CameraProfile )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Thumbnail )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TriggerTime )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TorchMode )
    DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, IRTorch )
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoTemporalDenoising)
    DECLARE_PROPERTY_HANDLERS( CExtendedProperty, RelativePanel)

    DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode )
    DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash )
    DECLARE_PROPERTY_GET_HANDLER( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence )

    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Exposure);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Focus);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Zoom);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, ZoomRelative);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Pan);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Roll);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Tilt);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength);

    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Brightness);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Contrast);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Hue);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance);
    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq);

    DECLARE_PROPERTY_GET_HANDLER( CRoiConfig, RoiConfigCaps );

    DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode);
    DECLARE_PROPERTY_GET_HANDLER(KSPROPERTY_VIDEOCONTROL_CAPS_S, VideoControlCaps);

    //
    //  The following are property handlers that use an atypical variable 
    //  length (not known at compile-time) parameter list.
    //
    //  Since we only have a few, declare them explicitly.
    //

    //  KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler
    static
    NTSTATUS
    GetRoiIspControl(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData
    );

    //  KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler
    static
    NTSTATUS
    SetRoiIspControl(
        _In_    PIRP pIrp,
        _In_    PKSPROPERTY pProperty,
        _Inout_ PVOID pData);

    //  Make sure the PERFRAMESETTINGs passed to us are valid.
    _Success_(return == 0)
    NTSTATUS
    ParsePFSBuffer(
        _In_reads_bytes_(BufferLimit)
        PKSCAMERA_PERFRAMESETTING_HEADER    pPFS,
        _In_    ULONG                               BufferLimit,
        _Outptr_opt_result_maybenull_
        ISP_FRAME_SETTINGS                **ppSettings );

    static
    NTSTATUS
    GetExtrinsics(
        _In_    PIRP            pIrp,
        _In_    PKSP_PIN        pProperty,
        _Inout_ PVOID           pData
    );

    static
    NTSTATUS
    GetIntrinsics(
        _In_    PIRP            pIrp,
        _In_    PKSP_PIN        pProperty,
        _Inout_ PVOID           pData
    );

    //  Helper function for finding our filter object.
    static
    CCaptureFilter *
    GetFilter(
        _In_    PKSPIN  Pin
    )
    {
        return reinterpret_cast <CCaptureFilter *>
               (KsPinGetParentFilter(Pin)->Context);
    }

    //  Accessor to convert a reference to a CCaptureFilter to a PKSFILTER.
    operator PKSFILTER()
    {
        return m_pKSFilter;
    }

    //  Get the name of the filter from the context structure passed in at initialization.
    LPCWSTR GetFilterName()
    {
        return m_pCaptureDevice->GetFilterName(m_pCaptureDevice->GetFilterIndex(m_pKSFilter));
    }

    //  Update the pin's allocator to a specific frame count.
    NTSTATUS
    UpdateAllocatorFraming( 
        _In_    ULONG PinId,
        _In_    ULONG DesiredFrames
    );

private:
    //  An arbitrary limit on Per-Frame settings.
    static
    const ULONG     MAX_FRAME_COUNT=512;

    // Filter level state information.
    KSCAMERA_EXTENDEDPROP_PROFILE   m_Profile;
};

//
//  Helper definitions for creating custom property sets
//
//  Note: Property automation tables require a statically defined accessor 
//        functions, however our properties functions are (mostly) implemented
//        as non-static member functions.  The following macros provide a
//        static member thunking function by invoking a member template 
//        function called Prop<>.  The Prop<> performs limited validation on
//        the buffer, provides back the size if the buffer size was 0, picks 
//        up a pointer to the filter through the IRP and calls the member
//        function that handles the property.
//

#define DEFINE_PROP_ITEM_NO_SET( T, ctrl, type, name )              \
    DEFINE_KSPROPERTY_ITEM(                                         \
        ctrl,                                                       \
        (&T::Prop<type, T, &T::Get##name>),                         \
        sizeof(KSPROPERTY),                                         \
        0,                                                          \
        NULL,                                                       \
        NULL, 0, NULL, NULL, 0                                      \
        )

#define DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, values )     \
    DEFINE_KSPROPERTY_ITEM(                                             \
        ctrl,                                                           \
        (&T::Prop<type, T, &T::Get##name>),                             \
        sizeof(KSPROPERTY),                                             \
        0,                                                              \
        (&T::Prop<type, T, &T::Set##name>),                             \
        values, 0, NULL, NULL, 0                                        \
        )

#define DEFINE_PROP_ITEM( T, ctrl, type, name )                         \
    DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, NULL )

//
//  A simplification for defining a KSEVENT.  We only need to specify one parameter.
//
#define DEFINE_STD_EVENT_ITEM(EVENTID)      \
    DEFINE_KSEVENT_ITEM                     \
    (                                       \
        EVENTID,                            \
        sizeof(KSEVENTDATA),                \
        0,                                  \
        NULL,                               \
        NULL,                               \
        NULL                                \
    )

//
//  A simplification for defining a KSPROPERTY_SET.  We only need to specify two parameters.
//
#define DEFINE_STD_PROPERTY_SET(SET, PROPERTIES)    \
    DEFINE_KSPROPERTY_SET(                          \
        &SET,                                       \
        SIZEOF_ARRAY(PROPERTIES),                   \
        PROPERTIES,                                 \
        0,                                          \
        NULL)

const LONGLONG MAX_EXPOSURE_TIME = 10000000ULL * 60 * 60;    // 1 min
const LONG MIN_EXPOSURE_TIME=10000;            // 1 ms
const LONG DEF_EXPOSURE_TIME=10000 * 30;       // 30 ms.


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/DeviceContext.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    DeviceContext.cpp

Abstract:

    Implements the functions to create/destroy device context by
    registering/unregistering for device notifications.

Environment:

    User mode

--*/

#include "stdafx.h"

extern HANDLE            SvcStopRequestEvent;
extern HCMNOTIFICATION   InterfaceNotificationHandle;
extern DEVICE_LIST_ENTRY DeviceList;
extern SRWLOCK           DeviceListLock;

//
// Keep track of running theads for unregistering device notifications
//
DWORD              RunningUnregistrations     = 0;
SRWLOCK            RunningUnregistrationsLock = SRWLOCK_INIT;
CONDITION_VARIABLE UnregistrationComplete     = CONDITION_VARIABLE_INIT;

/*++

Routine Description:

    Setup device interface context by registering for device interface
    notifications

Arguments:

    NULL

Return Value:

    A Win32 error code.

--*/
DWORD
SetupDeviceInterfaceContext(
    VOID
    )
{
    DWORD     Err                     = ERROR_SUCCESS;
    CONFIGRET Cr                      = CR_SUCCESS;
    PCWSTR    DeviceInterface         = NULL;
    PWSTR     DeviceInterfaceList     = NULL;
    DWORD     DeviceInterfaceListSize = 0;

    //
    // Register for device interface events to open and close the handle to
    // the interface.
    //
    Err = RegisterInterfaceNotifications(&InterfaceNotificationHandle);

    if (Err != ERROR_SUCCESS)
    {
        goto cleanup;
    }

    //
    // The interface may already be present on the system. Retrieve a list of
    // existing interfaces
    //
    do
    {
        Cr = CM_Get_Device_Interface_List_Size(&DeviceInterfaceListSize,
                                               (LPGUID)&GUID_DEVINTERFACE_OSRUSBFX2,
                                               NULL,
                                               CM_GET_DEVICE_INTERFACE_LIST_PRESENT);

        if (Cr != CR_SUCCESS)
        {
            break;
        }

        if (DeviceInterfaceList != NULL)
        {
            free(DeviceInterfaceList);
        }

        DeviceInterfaceList = (PWSTR)malloc(DeviceInterfaceListSize * sizeof(WCHAR));

        if (DeviceInterfaceList == NULL)
        {
            Cr = CR_OUT_OF_MEMORY;
            break;
        }

        Cr = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_OSRUSBFX2,
                                          NULL,
                                          DeviceInterfaceList,
                                          DeviceInterfaceListSize,
                                          CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    } while (Cr == CR_BUFFER_SMALL);

    if (Cr != CR_SUCCESS)
    {
        Err = CM_MapCrToWin32Err(Cr, ERROR_INVALID_DATA);
        goto cleanup;
    }

    //
    // Register for notifications on existing device interfaces
    //
    for (DeviceInterface = DeviceInterfaceList;
         *DeviceInterface != L'\0';
         DeviceInterface += wcslen(DeviceInterface) + 1)
    {
        RegisterDeviceNotifications(DeviceInterface);
    }

cleanup:

    if (DeviceInterfaceList != NULL)
    {
        free(DeviceInterfaceList);
    }

    return Err;
}


/*++

Routine Description:

    Clean up device interface context

Arguments:

    NULL

Return Value:

    NULL

--*/
VOID
CleanupDeviceInterfaceContext(
    VOID
    )
{
    PDEVICE_CONTEXT Context         = NULL;
    BOOL            NeedsUnregister = FALSE;

    //
    // Unregister from the interface first, so that re-appearance of the
    // interface does not cause us to register device events again.
    //
    if (InterfaceNotificationHandle != NULL)
    {
        CM_Unregister_Notification(InterfaceNotificationHandle);
        InterfaceNotificationHandle = NULL;
    }

    //
    // Unregister notifications for device handles.
    //
    while (TRUE)
    {
        NeedsUnregister = FALSE;

        AcquireSRWLockShared(&DeviceListLock);

        if (IsDeviceListEmpty(&DeviceList))
        {
            ReleaseSRWLockShared(&DeviceListLock);
            break;
        }

        //
        // Get next device context
        //
        Context = CONTAINING_DEVICE_RECORD(DeviceList.Flink);

        //
        // The device context can be unregistered either within notification
        // callbacks or cleanup routines. Synchronize these operations to only
        // unregister once.
        //
        EnterCriticalSection(&Context->ContextLock);

        if (!Context->Unregistered)
        {
            Context->Unregistered = TRUE;
            NeedsUnregister = TRUE;
        }

        LeaveCriticalSection(&Context->ContextLock);

        ReleaseSRWLockShared(&DeviceListLock);

        if (NeedsUnregister)
        {
            //
            // Remove next device from device list
            //
            AcquireSRWLockExclusive(&DeviceListLock);
            RemoveDeviceListEntry(DeviceList.Flink);
            ReleaseSRWLockExclusive(&DeviceListLock);

            UnregisterDeviceNotifications(Context);
        }
    }

    //
    // Wait for all other unregistrations for device handle that is not in
    // device list.
    //
    AcquireSRWLockShared(&RunningUnregistrationsLock);

    while (RunningUnregistrations != 0)
    {
        SleepConditionVariableSRW(&UnregistrationComplete,
                                  &RunningUnregistrationsLock,
                                  INFINITE,
                                  CONDITION_VARIABLE_LOCKMODE_SHARED);
    }

    ReleaseSRWLockShared(&RunningUnregistrationsLock);
}


/*++

Routine Description:

    Handles a device query remove notification.

Arguments:

    Context - The callback context

Return Value:

    NULL

--*/
VOID
DeviceQueryRemoveAction(
    _In_ PDEVICE_CONTEXT Context
    )
{
    if (Context->DeviceHandle != INVALID_HANDLE_VALUE)
    {
        //
        // Close open handles to allow the device to be query removed
        //
        CloseHandle(Context->DeviceHandle);

        Context->DeviceHandle = INVALID_HANDLE_VALUE;
    }
}


/*++

Routine Description:

    Handles a device query remove failed notification.

Arguments:

    Context - The callback context

Return Value:

    NULL

--*/
VOID
DeviceQueryRemoveFailedAction(
    _In_ PDEVICE_CONTEXT Context
    )
{
    PWSTR DeviceInterfacePath = NULL;
    BOOL  NeedsUnregister     = FALSE;

    //
    // Keep a record of device symbolic link since the callback context will
    // be freed up during unregistration
    //
    DeviceInterfacePath = Context->SymbolicLink;
    Context->SymbolicLink = NULL;

    //
    // The device context can be unregistered either within notification
    // callbacks or cleanup routines. Synchronize these operations to only
    // unregister once.
    //
    EnterCriticalSection(&Context->ContextLock);

    if (!Context->Unregistered)
    {
        Context->Unregistered = TRUE;
        NeedsUnregister = TRUE;
    }

    LeaveCriticalSection(&Context->ContextLock);

    //
    // Remove device from device list if needed
    //
    if (NeedsUnregister)
    {
        AcquireSRWLockExclusive(&DeviceListLock);
        RemoveDeviceListEntry(&Context->ListEntry);
        ReleaseSRWLockExclusive(&DeviceListLock);

        //
        // Unregister notifications for the old device handle from a deferred
        // routine since CM_Unregister_Notification can not be called from a
        // notification callback
        //
        AcquireSRWLockExclusive(&RunningUnregistrationsLock);
        RunningUnregistrations++;
        ReleaseSRWLockExclusive(&RunningUnregistrationsLock);

        QueueUserWorkItem(UnregisterDeviceNotificationsWorkerThread,
                          Context,
                          WT_EXECUTEDEFAULT);
    }

    //
    // Re-register for device notifications on a new device handle
    //
    RegisterDeviceNotifications(DeviceInterfacePath);

    //
    // Free memory that the callback context used to point at but was not freed
    // during unregistration
    //
    free(DeviceInterfacePath);
}

/*++

Routine Description:

    Handles a device remove complete notification.

Arguments:

    Context - The callback context

Return Value:

    NULL

--*/
VOID
DeviceRemoveCompleteAction(
    _In_ PDEVICE_CONTEXT Context
    )
{
    BOOL NeedsUnregister = FALSE;

    //
    // The device context can be unregistered either within notification
    // callbacks or cleanup routines. Synchronize these operations to only
    // unregister once.
    //
    EnterCriticalSection(&Context->ContextLock);

    if (!Context->Unregistered)
    {
        Context->Unregistered = TRUE;
        NeedsUnregister = TRUE;
    }

    LeaveCriticalSection(&Context->ContextLock);

    //
    // Remove device from device list if needed
    //
    if (NeedsUnregister)
    {
        AcquireSRWLockExclusive(&DeviceListLock);
        RemoveDeviceListEntry(&Context->ListEntry);
        ReleaseSRWLockExclusive(&DeviceListLock);

        //
        // Unregister notifications for the old device handle from a deferred
        // routine since CM_Unregister_Notification can not be called from a
        // notification callback
        //
        AcquireSRWLockExclusive(&RunningUnregistrationsLock);
        RunningUnregistrations++;
        ReleaseSRWLockExclusive(&RunningUnregistrationsLock);

        QueueUserWorkItem(UnregisterDeviceNotificationsWorkerThread,
                          Context,
                          WT_EXECUTEDEFAULT);
    }
}

/*++

Routine Description:

    Handles device notifications.

Arguments:

    hNotify       - The notification that spurred this callback

    hContext      - The callback context

    Action        - The type of callback

    EventData     - Additional information about this callback

    EventDataSize - The size of EventData

Return Value:

    A Win32 error code.

--*/
DWORD
WINAPI
DeviceCallback(
    _In_ HCMNOTIFICATION       hNotify,
    _In_ PVOID                 hContext,
    _In_ CM_NOTIFY_ACTION      Action,
    _In_ PCM_NOTIFY_EVENT_DATA EventData,
    _In_ DWORD                 EventDataSize
    )
{
    PDEVICE_CONTEXT Context = (PDEVICE_CONTEXT)hContext;

    UNREFERENCED_PARAMETER(EventData);
    UNREFERENCED_PARAMETER(EventDataSize);

    //
    // In case this callback fires before the registration call returns, make
    // sure the notification handle is properly set
    //
    Context->DeviceNotificationHandle = hNotify;

    switch (Action)
    {
    case CM_NOTIFY_ACTION_DEVICEQUERYREMOVE:
        DeviceQueryRemoveAction(Context);
        break;

    case CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED:
        DeviceQueryRemoveFailedAction(Context);
        break;

    case CM_NOTIFY_ACTION_DEVICEREMOVEPENDING:
    case CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE:
        DeviceRemoveCompleteAction(Context);
        break;

    default:
        break;
    }

    return ERROR_SUCCESS;
}


/*++

Routine Description:

    Register for device notifications.

Arguments:

    DeviceInterfacePath - The symbolic link path of the device interface

Return Value:

    A Win32 error code.

--*/
DWORD
RegisterDeviceNotifications(
    _In_ PCWSTR DeviceInterfacePath
    )
{
    DWORD              Err          = ERROR_SUCCESS;
    CONFIGRET          Cr           = CR_SUCCESS;
    PDEVICE_CONTEXT    Context      = NULL;
    PDEVICE_LIST_ENTRY Link         = NULL;
    DWORD              BufferSize   = 0;
    CM_NOTIFY_FILTER   NotifyFilter = {0};

    //
    // Check whether the device interface has already been registered for device
    // handle notifications since it can be done either in interface arrival
    // callback routine or initial device interface setup routine
    //
    AcquireSRWLockExclusive(&DeviceListLock);

    for (Link = DeviceList.Flink; Link != &DeviceList; Link = Link->Flink)
    {
        Context = CONTAINING_DEVICE_RECORD(Link);

        if (_wcsicmp(DeviceInterfacePath, Context->SymbolicLink) == 0)
        {
            break;
        }
    }

    if (Link != &DeviceList)
    {
        Err = ERROR_SUCCESS;
        goto cleanup;
    }

    //
    // Create a new device context
    //
    Context = (PDEVICE_CONTEXT)malloc(sizeof(DEVICE_CONTEXT));

    if (Context == NULL)
    {
        Err = ERROR_OUTOFMEMORY;
        goto cleanup;
    }

    //
    // Initialize device context
    //
    ZeroMemory(Context, sizeof(DEVICE_CONTEXT));
    Context->DeviceHandle = INVALID_HANDLE_VALUE;
    InitializeCriticalSection(&Context->ContextLock);

    //
    // Fill out the context and register device handle notifications
    //
    BufferSize = (DWORD)wcslen(DeviceInterfacePath) + 1;
    Context->SymbolicLink = (PWSTR)malloc(BufferSize * sizeof(WCHAR));

    if ((Context->SymbolicLink == NULL) ||
        (FAILED(StringCchCopy(Context->SymbolicLink,
                              BufferSize,
                              DeviceInterfacePath))))
    {
        Err = ERROR_OUTOFMEMORY;
        goto cleanup;
    }

    Context->DeviceHandle = CreateFile(Context->SymbolicLink,
                                       GENERIC_WRITE | GENERIC_READ,
                                       FILE_SHARE_WRITE | FILE_SHARE_READ,
                                       NULL, // default security
                                       OPEN_EXISTING,
                                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                       NULL);

    if (Context->DeviceHandle == INVALID_HANDLE_VALUE)
    {
        Err = GetLastError();
        goto cleanup;
    }

    NotifyFilter.cbSize = sizeof(NotifyFilter);
    NotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
    NotifyFilter.u.DeviceHandle.hTarget = Context->DeviceHandle;

    Cr = CM_Register_Notification(&NotifyFilter,
                                  (PVOID)Context,
                                  (PCM_NOTIFY_CALLBACK)DeviceCallback,
                                  &Context->DeviceNotificationHandle);

    if (Cr != CR_SUCCESS)
    {
        Err = CM_MapCrToWin32Err(Cr, ERROR_INVALID_DATA);
        goto cleanup;
    }

    //
    // Add the device entry to device list
    //
    InsertTailDeviceListEntry(&DeviceList, &Context->ListEntry);
    Context = NULL;

cleanup:

    ReleaseSRWLockExclusive(&DeviceListLock);

    //
    // Clean up device context if an error occurred.
    //
    if (Context != NULL)
    {
        if (Context->SymbolicLink != NULL)
        {
            free(Context->SymbolicLink);
        }

        if (Context->DeviceHandle != INVALID_HANDLE_VALUE)
        {
            CloseHandle(Context->DeviceHandle);
        }

        free(Context);
    }

    return Err;
}


VOID
UnregisterDeviceNotifications(
    _Inout_ PDEVICE_CONTEXT Context
    )
{
    if (Context->DeviceNotificationHandle != NULL)
    {
        CM_Unregister_Notification(Context->DeviceNotificationHandle);
    }

    if (Context->DeviceHandle != INVALID_HANDLE_VALUE)
    {
        CloseHandle(Context->DeviceHandle);
    }

    if (Context->SymbolicLink != NULL)
    {
        free(Context->SymbolicLink);
    }

    DeleteCriticalSection(&Context->ContextLock);

    free(Context);
}


/*++

Routine Description:

    This worker thread avoids a deadlock when unregistering device notifications.
    Rather than calling CM_Unregister_Notification from the callback, the
    callback gives that work to a separate thread to avoid deadlock.

Arguments:

    lpThreadParameter - The thread data passed to the function

Return Value:

    Zero

--*/
DWORD
WINAPI
UnregisterDeviceNotificationsWorkerThread(
    _In_ PVOID lpThreadParameter
    )
{
    PDEVICE_CONTEXT Context = (PDEVICE_CONTEXT)lpThreadParameter;

    UnregisterDeviceNotifications(Context);

    //
    // Decrease running unregistrations
    //
    AcquireSRWLockExclusive(&RunningUnregistrationsLock);
    RunningUnregistrations--;
    ReleaseSRWLockExclusive(&RunningUnregistrationsLock);

    WakeAllConditionVariable(&UnregistrationComplete);

    return 0;
}


/*++

Routine Description:

    Handles an interface arrival notification.

Arguments:

    hNotify       - The notification that fired the callback

    hContext      - The callback context

    Action        - The type of notification

    EventData     - Additional information about the callback

    EventDataSize - The size of EventData

Return Value:

    Notification callback should return ERROR_SUCCESS.

--*/
DWORD
WINAPI
InterfaceCallback(
    _In_ HCMNOTIFICATION       hNotify,
    _In_ PVOID                 hContext,
    _In_ CM_NOTIFY_ACTION      Action,
    _In_ PCM_NOTIFY_EVENT_DATA EventData,
    _In_ DWORD                 EventDataSize
    )
{
    PDEVICE_CONTEXT    Context         = NULL;
    PDEVICE_LIST_ENTRY Link            = NULL;
    BOOL               NeedsUnregister = FALSE;

    UNREFERENCED_PARAMETER(hNotify);
    UNREFERENCED_PARAMETER(hContext);
    UNREFERENCED_PARAMETER(EventDataSize);

    switch (Action)
    {
    case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:
        //
        // Register for device notifications
        //
        RegisterDeviceNotifications(EventData->u.DeviceInterface.SymbolicLink);
        break;

    case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL:
        //
        // The device may still be running in the case where the driver disables
        // its interfaces. Find whether it is still on the device list and clean
        // it up.
        //
        AcquireSRWLockShared(&DeviceListLock);

        for (Link = DeviceList.Flink; Link != &DeviceList; Link = Link->Flink)
        {
            Context = CONTAINING_DEVICE_RECORD(Link);

            if (_wcsicmp(EventData->u.DeviceInterface.SymbolicLink,
                         Context->SymbolicLink) == 0)
            {
                //
                // The device context can be unregistered either within
                // notification callbacks or cleanup routines. Synchronize these
                // operations to only unregister once.
                //
                EnterCriticalSection(&Context->ContextLock);

                if (!Context->Unregistered)
                {
                    Context->Unregistered = TRUE;
                    NeedsUnregister = TRUE;
                }

                LeaveCriticalSection(&Context->ContextLock);

                break;
            }
        }

        ReleaseSRWLockShared(&DeviceListLock);

        if (NeedsUnregister)
        {
            //
            // Remove the device from device list
            //
            AcquireSRWLockExclusive(&DeviceListLock);
            RemoveDeviceListEntry(&Context->ListEntry);
            ReleaseSRWLockExclusive(&DeviceListLock);

            UnregisterDeviceNotifications(Context);
        }

        //
        // Notify service to stop if this is the last device interface
        //
        AcquireSRWLockShared(&DeviceListLock);

        if (IsDeviceListEmpty(&DeviceList))
        {
            SetEvent(SvcStopRequestEvent);
        }

        ReleaseSRWLockShared(&DeviceListLock);
        break;

    default:
        break;
    }

    return ERROR_SUCCESS;
}


/*++

Routine Description:

    Registers the service for interface notifications

Arguments:

    pInterfaceNotificationHandle - Pointer to receive the HCMNOTIFICATION handle

Return Value:

    A Win32 error code.

--*/
DWORD
RegisterInterfaceNotifications(
    _Out_ PHCMNOTIFICATION pInterfaceNotificationHandle
    )
{
    DWORD            Err          = ERROR_SUCCESS;
    CONFIGRET        Cr           = CR_SUCCESS;
    CM_NOTIFY_FILTER NotifyFilter = {0};

    NotifyFilter.cbSize = sizeof(NotifyFilter);
    NotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
    NotifyFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_OSRUSBFX2;

    //
    // Register for device interface events to open and close the handle to
    // the interface.
    //
    Cr = CM_Register_Notification(&NotifyFilter,
                                  NULL,
                                  (PCM_NOTIFY_CALLBACK)InterfaceCallback,
                                  pInterfaceNotificationHandle);

    if (Cr != CR_SUCCESS)
    {
        Err = CM_MapCrToWin32Err(Cr, ERROR_INVALID_DATA);
        goto cleanup;
    }

cleanup:

    return Err;
}


<|start_filename|>audio/simpleaudiosample/Source/Main/minwavert.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    minwavert.cpp

Abstract:

    Implementation of wavert miniport.
--*/

#pragma warning (disable : 4127)

#include "definitions.h"
#include <limits.h>
#include "endpoints.h"
#include "minwavert.h"
#include "minwavertstream.h"
#include "micarraywavtable.h"

#define EFFECTS_LIST_COUNT 2

//=============================================================================
// CMiniportWaveRT
//=============================================================================

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CreateMiniportWaveRTSimpleAudioSample
( 
    _Out_           PUNKNOWN                              * Unknown,
    _In_            REFCLSID,
    _In_opt_        PUNKNOWN                                UnknownOuter,
    _In_            POOL_FLAGS                              PoolFlags,
    _In_            PUNKNOWN                                UnknownAdapter,
    _In_opt_        PVOID                                   DeviceContext,
    _In_            PENDPOINT_MINIPAIR                      MiniportPair
)
/*++

Routine Description:

  Create the wavert miniport.

Arguments:

  Unknown - 

  RefClsId -

  UnknownOuter -

  PoolType -

  UnkownAdapter -

  DeviceContext -

  MiniportPair -

Return Value:

  NT status code.

--*/
{
    UNREFERENCED_PARAMETER(UnknownOuter);

    PAGED_CODE();

    ASSERT(Unknown);
    ASSERT(MiniportPair);

    CMiniportWaveRT *obj = new (PoolFlags, MINWAVERT_POOLTAG) CMiniportWaveRT
                                                             (
                                                                UnknownAdapter,
                                                                MiniportPair,
                                                                DeviceContext
                                                             );
    if (NULL == obj)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    obj->AddRef();
    *Unknown = reinterpret_cast<IUnknown*>(obj);

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
CMiniportWaveRT::~CMiniportWaveRT
( 
    void 
)
/*++

Routine Description:

  Destructor for wavert miniport

Arguments:

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::~CMiniportWaveRT]"));

    if (m_pDeviceFormat)
    {
        ExFreePoolWithTag( m_pDeviceFormat, MINWAVERT_POOLTAG );
        m_pDeviceFormat = NULL;
    }

    if (m_pMixFormat)
    {
        ExFreePoolWithTag( m_pMixFormat, MINWAVERT_POOLTAG );
        m_pMixFormat = NULL;
    }

    if (m_pbMuted)
    {
        ExFreePoolWithTag(m_pbMuted, MINWAVERT_POOLTAG);
        m_pbMuted = NULL;
    }

    if (m_plVolumeLevel)
    {
        ExFreePoolWithTag(m_plVolumeLevel, MINWAVERT_POOLTAG);
        m_plVolumeLevel = NULL;
    }

    if (m_pDrmPort)
    {
        m_pDrmPort->Release();
        m_pDrmPort = NULL;
    }

    if (m_pPortEvents)
    {
        m_pPortEvents->Release();
        m_pPortEvents = NULL;
    }
    
    if (m_SystemStreams)
    {
        ExFreePoolWithTag( m_SystemStreams, MINWAVERT_POOLTAG );
        m_SystemStreams = NULL;
    }

} // ~CMiniportWaveRT

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CMiniportWaveRT::DataRangeIntersection
( 
    _In_        ULONG                       PinId,
    _In_        PKSDATARANGE                ClientDataRange,
    _In_        PKSDATARANGE                MyDataRange,
    _In_        ULONG                       OutputBufferLength,
    _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength)
                PVOID                       ResultantFormat,
    _Out_       PULONG                      ResultantFormatLength 
)
/*++

Routine Description:

  The DataRangeIntersection function determines the highest quality 
  intersection of two data ranges.

  This sample just sets the ResultantFormat to be the only supported
  format for the MicArray endpoint.

Arguments:

  PinId -           Pin for which data intersection is being determined. 

  ClientDataRange - Pointer to KSDATARANGE structure which contains the data 
                    range submitted by client in the data range intersection 
                    property request. 

  MyDataRange -         Pin's data range to be compared with client's data 
                        range. In this case we actually ignore our own data 
                        range, because we know that we only support one range.

  OutputBufferLength -  Size of the buffer pointed to by the resultant format 
                        parameter. 

  ResultantFormat -     Pointer to value where the resultant format should be 
                        returned. 

  ResultantFormatLength -   Actual length of the resultant format placed in 
                            ResultantFormat. This should be less than or equal 
                            to OutputBufferLength. 

  Return Value:

    NT status code.

--*/
{
    UNREFERENCED_PARAMETER(PinId);

    ULONG                   requiredSize;

    PAGED_CODE();

    if (!IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
    {
        return STATUS_NOT_IMPLEMENTED;
    }

    //If called for the mic array pin, set ResultantFormat to be the endpoint's only supported format.
    //Otherwise, allow the class handler to set ResultantFormat.  
    if ((this->m_DeviceType) == eMicArrayDevice1)
    {
        requiredSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE);

        //
        // Validate return buffer size, if the request is only for the
        // size of the resultant structure, return it now before
        // returning other types of errors.
        //
        if (!OutputBufferLength)
        {
            *ResultantFormatLength = requiredSize;
            return STATUS_BUFFER_OVERFLOW;
        }
        else if (OutputBufferLength < requiredSize)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        //Set ResultantFormat to be the only supported format for the MicArray endpoint. 
        PKSDATAFORMAT_WAVEFORMATEXTENSIBLE resultantFormat;
        resultantFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ResultantFormat;
        *resultantFormat = *MicArrayPinSupportedDeviceFormats;
        *ResultantFormatLength = requiredSize;

        return STATUS_SUCCESS;
    }
    else
    {
        requiredSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);

        //
        // Validate return buffer size, if the request is only for the
        // size of the resultant structure, return it now before
        // returning other types of errors.
        //
        if (!OutputBufferLength)
        {
            *ResultantFormatLength = requiredSize;
            return STATUS_BUFFER_OVERFLOW;
        }
        else if (OutputBufferLength < requiredSize)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        // Verify channel count is supported. This routine assumes a separate data
        // range for each supported channel count.
        if (((PKSDATARANGE_AUDIO)MyDataRange)->MaximumChannels != ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels)
        {
            return STATUS_NO_MATCH;
        }

        //
        // Ok, let the class handler do the rest.
        //
        return STATUS_NOT_IMPLEMENTED;
    }

} // DataRangeIntersection

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CMiniportWaveRT::GetDescription
( 
    _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor 
)
/*++

Routine Description:

  The GetDescription function gets a pointer to a filter description. 
  It provides a location to deposit a pointer in miniport's description 
  structure.

Arguments:

  OutFilterDescriptor - Pointer to the filter description. 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(OutFilterDescriptor);

    *OutFilterDescriptor = &m_FilterDesc;

    return STATUS_SUCCESS;
} // GetDescription

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CMiniportWaveRT::Init
( 
    _In_  PUNKNOWN                UnknownAdapter_,
    _In_  PRESOURCELIST           ResourceList_,
    _In_  PPORTWAVERT             Port_ 
)
/*++

Routine Description:

  The Init function initializes the miniport. Callers of this function 
  should run at IRQL PASSIVE_LEVEL

Arguments:

  UnknownAdapter - A pointer to the Iuknown interface of the adapter object. 

  ResourceList - Pointer to the resource list to be supplied to the miniport 
                 during initialization. The port driver is free to examine the 
                 contents of the ResourceList. The port driver will not be 
                 modify the ResourceList contents. 

  Port - Pointer to the topology port object that is linked with this miniport. 

Return Value:

  NT status code.

--*/
{
    UNREFERENCED_PARAMETER(UnknownAdapter_);
    UNREFERENCED_PARAMETER(ResourceList_);
    UNREFERENCED_PARAMETER(Port_);
    PAGED_CODE();

    ASSERT(UnknownAdapter_);
    ASSERT(Port_);

    DPF_ENTER(("[CMiniportWaveRT::Init]"));

    NTSTATUS ntStatus = STATUS_SUCCESS;
    size_t   size;

    //
    // Init class data members
    //
    m_ulSystemAllocated                 = 0;
    m_SystemStreams                     = NULL;
    m_bGfxEnabled                       = FALSE;
    m_pMixFormat                        = NULL;
    m_pDeviceFormat                     = NULL;
    m_ulMixDrmContentId                 = 0;
    m_pbMuted = NULL;
    m_plVolumeLevel = NULL;
    RtlZeroMemory(&m_MixDrmRights, sizeof(m_MixDrmRights));

    //
    // Init the audio-engine used by the render devices.
    //
    if (IsRenderDevice())
    {
        if (m_ulMaxSystemStreams == 0 )
        {
            return STATUS_INVALID_DEVICE_STATE;
        }
            
        // System streams.
        size = sizeof(PCMiniportWaveRTStream) * m_ulMaxSystemStreams;
        m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePool2(POOL_FLAG_NON_PAGED, size, MINWAVERT_POOLTAG);
        if (m_SystemStreams == NULL)
        {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        
        // 
        // For DRM support.
        //
        if (!NT_SUCCESS(Port_->QueryInterface(IID_IDrmPort2, (PVOID *)&m_pDrmPort)))
        {
            m_pDrmPort = NULL;
        }
    }
    
    // 
    // For KS event support.
    //
    if (!NT_SUCCESS(Port_->QueryInterface(IID_IPortEvents, (PVOID *)&m_pPortEvents)))
    {
        m_pPortEvents = NULL;
    }
    
    return ntStatus;
} // Init

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CMiniportWaveRT::NewStream
( 
    _Out_ PMINIPORTWAVERTSTREAM * OutStream,
    _In_  PPORTWAVERTSTREAM       OuterUnknown,
    _In_  ULONG                   Pin,
    _In_  BOOLEAN                 Capture,
    _In_  PKSDATAFORMAT           DataFormat
)
/*++

Routine Description:

  The NewStream function creates a new instance of a logical stream 
  associated with a specified physical channel. Callers of NewStream should 
  run at IRQL PASSIVE_LEVEL.

Arguments:

  OutStream -

  OuterUnknown -

  Pin - 

  Capture - 

  DataFormat -

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(OutStream);
    ASSERT(DataFormat);

    DPF_ENTER(("[CMiniportWaveRT::NewStream]"));

    NTSTATUS                    ntStatus = STATUS_SUCCESS;
    PCMiniportWaveRTStream      stream = NULL;
    GUID                        signalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT;
    
    *OutStream = NULL;

     //
    // If the data format attributes were specified, extract them.
    //
    if ( DataFormat->Flags & KSDATAFORMAT_ATTRIBUTES )
    {
        // The attributes are aligned (QWORD alignment) after the data format
        PKSMULTIPLE_ITEM attributes = (PKSMULTIPLE_ITEM) (((PBYTE)DataFormat) + ((DataFormat->FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT));
        ntStatus = GetAttributesFromAttributeList(attributes, attributes->Size, &signalProcessingMode);
    }

    // Check if we have enough streams.
    //
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = ValidateStreamCreate(Pin, Capture);
    }

    // Determine if the format is valid.
    //
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = IsFormatSupported(Pin, Capture, DataFormat);
    }

    // Instantiate a stream. Stream must be in
    // NonPagedPool(Nx) because of file saving.
    //
    if (NT_SUCCESS(ntStatus))
    {
        stream = new (POOL_FLAG_NON_PAGED, MINWAVERT_POOLTAG) 
            CMiniportWaveRTStream(NULL);

        if (stream)
        {
            stream->AddRef();

            ntStatus = 
                stream->Init
                ( 
                    this,
                    OuterUnknown,
                    Pin,
                    Capture,
                    DataFormat,
                    signalProcessingMode
                );
        }
        else
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(ntStatus))
    {
        *OutStream = PMINIPORTWAVERTSTREAM(stream);
        (*OutStream)->AddRef();

        // The stream has references now for the caller.  The caller expects these
        // references to be there.
    }

    // This is our private reference to the stream.  The caller has
    // its own, so we can release in any case.
    //
    if (stream)
    {
        stream->Release();
    }
    
    return ntStatus;
} // NewStream

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CMiniportWaveRT::NonDelegatingQueryInterface
( 
    _In_ REFIID  Interface,
    _COM_Outptr_ PVOID * Object 
)
/*++

Routine Description:

  QueryInterface

Arguments:

  Interface - GUID

  Object - interface pointer to be returned.

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(Object);

    if (IsEqualGUIDAligned(Interface, IID_IUnknown))
    {
        *Object = PVOID(PUNKNOWN(PMINIPORTWAVERT(this)));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IMiniport))
    {
        *Object = PVOID(PMINIPORT(this));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRT))
    {
        *Object = PVOID(PMINIPORTWAVERT(this));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IMiniportAudioSignalProcessing))
    {
        *Object = PVOID(PMINIPORTAudioSignalProcessing(this));
    }
    else
    {
        *Object = NULL;
    }

    if (*Object)
    {
        // We reference the interface for the caller.

        PUNKNOWN(*Object)->AddRef();
        return STATUS_SUCCESS;
    }

    return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceDescription(_Out_ PDEVICE_DESCRIPTION DmaDeviceDescription)
{
    PAGED_CODE ();

    ASSERT (DmaDeviceDescription);

    DPF_ENTER(("[CMiniportWaveRT::GetDeviceDescription]"));

    RtlZeroMemory (DmaDeviceDescription, sizeof (DEVICE_DESCRIPTION));

    //
    // Init device description. This sample is using the same info for all m_DeviceType(s).
    // 
    
    DmaDeviceDescription->Master = TRUE;
    DmaDeviceDescription->ScatterGather = TRUE;
    DmaDeviceDescription->Dma32BitAddresses = TRUE;
    DmaDeviceDescription->InterfaceType = PCIBus;
    DmaDeviceDescription->MaximumLength = 0xFFFFFFFF;

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::GetModes
(
    _In_                                        ULONG     Pin,
    _Out_writes_opt_(*NumSignalProcessingModes) GUID*     SignalProcessingModes,
    _Inout_                                     ULONG*    NumSignalProcessingModes
)
/*

1.	If Pin is not a valid pin number, 
        return STATUS_INVALID_PARAMETER.
        
2.	If Pin is a valid pin number and it supports n modes (n>0), 
        init out-parameters and return STATUS_SUCCESS.
    
3.  Else this pin doesn't support any mode,
        return STATUS_NOT_SUPPORTED. 
        example: bridge pins or another mode-not-aware pins.

*/
{
    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::GetModes]"));

    NTSTATUS                ntStatus    = STATUS_INVALID_PARAMETER;
    ULONG                   numModes    = 0;
    MODE_AND_DEFAULT_FORMAT *modeInfo   = NULL;

    if (Pin >= m_pMiniportPair->WaveDescriptor->PinCount)
    {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // This method is valid only on the following pins:
    //  render is offload capable:
    //      sink (#0) and offload (#1) pins.
    //  render is NOT offload capable:
    //      sink (#0) pin.
    //  capture device: 
    //      source (#1) pin.
    //
    numModes = GetPinSupportedDeviceModes(Pin, &modeInfo);
    if (numModes == 0)
    {
        return STATUS_NOT_SUPPORTED;
    }
   
    // If caller requests the modes, verify sufficient buffer size then return the modes
    if (SignalProcessingModes != NULL)
    {
        if (*NumSignalProcessingModes < numModes)
        {
            *NumSignalProcessingModes = numModes;
            ntStatus = STATUS_BUFFER_TOO_SMALL;
            goto Done;
        }

        for (ULONG i=0; i<numModes; ++i)
        {
            SignalProcessingModes[i] = modeInfo[i].Mode;
        }
    }

    ASSERT(numModes > 0);
    *NumSignalProcessingModes = numModes;
    ntStatus = STATUS_SUCCESS;

Done:   
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::ValidateStreamCreate
(
    _In_    ULONG   _Pin,
    _In_    BOOLEAN _Capture
)
{
    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::ValidateStreamCreate]"));

    NTSTATUS ntStatus = STATUS_NOT_SUPPORTED;

    if (_Capture)
    {

        if (IsSystemCapturePin(_Pin))
        {
            VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(ntStatus, m_ulSystemAllocated, m_ulMaxSystemStreams);
        }

    }
    else
    {

        if (IsSystemRenderPin(_Pin))
        {
            VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(ntStatus, m_ulSystemAllocated, m_ulMaxSystemStreams);
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg()
_Use_decl_annotations_
VOID CMiniportWaveRT::AcquireFormatsAndModesLock()
{
    KeAcquireSpinLock(&m_DeviceFormatsAndModesLock, &m_DeviceFormatsAndModesIrql);
}

#pragma code_seg()
_Use_decl_annotations_
VOID CMiniportWaveRT::ReleaseFormatsAndModesLock()
{
    KeReleaseSpinLock(&m_DeviceFormatsAndModesLock, m_DeviceFormatsAndModesIrql);
}

//---------------------------------------------------------------------------
// GetPinSupportedDeviceFormats 
//
//  Return supported formats for a given pin.
//
//  Return value
//      The number of KSDATAFORMAT_WAVEFORMATEXTENSIBLE items.
//
//  Remarks
//      Supported formats index array follows same order as filter's pin
//      descriptor list.
//
#pragma code_seg()
_Use_decl_annotations_
ULONG CMiniportWaveRT::GetPinSupportedDeviceFormats(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats)
{
    PPIN_DEVICE_FORMATS_AND_MODES pDeviceFormatsAndModes = NULL;

    AcquireFormatsAndModesLock();

    pDeviceFormatsAndModes = m_DeviceFormatsAndModes;
    ASSERT(m_DeviceFormatsAndModesCount > PinId);
    ASSERT(pDeviceFormatsAndModes[PinId].WaveFormats != NULL);
    ASSERT(pDeviceFormatsAndModes[PinId].WaveFormatsCount > 0);

    if (ppFormats != NULL)
    {
        *ppFormats = pDeviceFormatsAndModes[PinId].WaveFormats;
    }

    ReleaseFormatsAndModesLock();

    return pDeviceFormatsAndModes[PinId].WaveFormatsCount;
}

//---------------------------------------------------------------------------
// GetPinSupportedDeviceModes 
//
//  Return mode information for a given pin.
//
//  Return value
//      The number of MODE_AND_DEFAULT_FORMAT items or 0 if none.
//
//  Remarks
//      Supported formats index array follows same order as filter's pin
//      descriptor list.
//
#pragma code_seg()
_Use_decl_annotations_
ULONG CMiniportWaveRT::GetPinSupportedDeviceModes(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) _On_failure_(_Deref_post_null_) MODE_AND_DEFAULT_FORMAT **ppModes)
{
    PMODE_AND_DEFAULT_FORMAT modes;
    ULONG numModes;

    AcquireFormatsAndModesLock();

    ASSERT(m_DeviceFormatsAndModesCount > PinId);
    ASSERT((m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount == 0) == (m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat == NULL));

    modes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat;
    numModes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount;

    if (ppModes != NULL)
    {
        if (numModes > 0)
        {
            *ppModes = modes;
        }
        else
        {
            // ensure that the returned pointer is NULL
            // in the event of failure (SAL annotation above
            // indicates that it must be NULL, and OACR sees a possibility
            // that it might not be).
            *ppModes = NULL;
        }
    }

    ReleaseFormatsAndModesLock();
    return numModes;
}

#pragma code_seg()
BOOL CMiniportWaveRT::IsSystemCapturePin(ULONG nPinId)
{
    AcquireFormatsAndModesLock();

    PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType;

    ReleaseFormatsAndModesLock();
    return (pinType == SystemCapturePin);
}

#pragma code_seg()
BOOL CMiniportWaveRT::IsSystemRenderPin(ULONG nPinId)
{
    AcquireFormatsAndModesLock();

    PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType;

    ReleaseFormatsAndModesLock();
    return (pinType == SystemRenderPin);
}

#pragma code_seg()
BOOL CMiniportWaveRT::IsBridgePin(ULONG nPinId)
{
    AcquireFormatsAndModesLock();

    PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType;

    ReleaseFormatsAndModesLock();
    return (pinType == BridgePin);
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::StreamCreated
(
    _In_ ULONG                  _Pin,
    _In_ PCMiniportWaveRTStream _Stream
)
{
    PAGED_CODE();

    PCMiniportWaveRTStream * streams        = NULL;
    ULONG                    count          = 0;
    
    DPF_ENTER(("[CMiniportWaveRT::StreamCreated]"));

    if (IsSystemCapturePin(_Pin))
    {
        ALLOCATE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated);
        return STATUS_SUCCESS;
    }
    else if (IsSystemRenderPin(_Pin))
    {

        ALLOCATE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated);
        streams = m_SystemStreams;
        count = m_ulMaxSystemStreams;

    }
    
    //
    // Cache this stream's ptr.
    //
    if (streams != NULL)
    {
        ULONG i = 0;
        for (; i<count; ++i)
        {
            if (streams[i] == NULL)
            {
                streams[i] = _Stream;
                break;
            }
        }
        ASSERT(i != count);
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::StreamClosed
(
    _In_ ULONG                  _Pin,
    _In_ PCMiniportWaveRTStream _Stream
)
{
    PAGED_CODE();

    bool                      updateDrmRights = false;
    PCMiniportWaveRTStream  * streams         = NULL;
    ULONG                     count           = 0;

    DPF_ENTER(("[CMiniportWaveRT::StreamClosed]"));

    if (IsSystemCapturePin(_Pin))
    {
        FREE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated);
        return STATUS_SUCCESS;
    }
    else if (IsSystemRenderPin(_Pin))
    {

        FREE_PIN_INSTANCE_RESOURCES(m_ulSystemAllocated);
        streams = m_SystemStreams;
        count = m_ulMaxSystemStreams;
        updateDrmRights = true;

    }

    //
    // Cleanup.
    //
    if (streams != NULL)
    {
        ULONG i = 0;
        for (; i<count; ++i)
        {
            if (streams[i] == _Stream)
            {
                streams[i] = NULL;
                break;
            }
        }
        ASSERT(i != count);
    }
    
    //
    // Update mixed drm rights.
    //
    if (updateDrmRights)
    {
        UpdateDrmRights();
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::GetAttributesFromAttributeList
(
    _In_ const KSMULTIPLE_ITEM *_pAttributes,
    _In_ size_t _Size,
    _Out_ GUID* _pSignalProcessingMode
)
/*++

Routine Description:

  Processes attributes list and return known attributes.

Arguments:

  _pAttributes - pointer to KSMULTIPLE_ITEM at head of an attributes list.

  _Size - count of bytes in the buffer pointed to by _pAttributes. The routine
    verifies sufficient buffer size while processing the attributes.

  _pSignalProcessingMode - returns the signal processing mode extracted from
    the attribute list, or AUDIO_SIGNALPROCESSINGMODE_DEFAULT if the attribute
    is not present in the list.

Return Value:

  NT status code.

Remarks

    This function is currently written for a single supported attribute
    (KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE). As additional attributes are defined in the future,
    this function should be rewritten to be data driven through tables, etc.

--*/
{
    PAGED_CODE();
    
    DPF_ENTER(("[CMiniportWaveRT::GetAttributesFromAttributeList]"));

    size_t cbRemaining = _Size;

    *_pSignalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT;

    if (cbRemaining < sizeof(KSMULTIPLE_ITEM))
    {
        return STATUS_INVALID_PARAMETER;
    }
    cbRemaining -= sizeof(KSMULTIPLE_ITEM);

    //
    // Extract attributes.
    //
    PKSATTRIBUTE attributeHeader = (PKSATTRIBUTE)(_pAttributes + 1);

    for (ULONG i = 0; i < _pAttributes->Count; i++)
    {
        if (cbRemaining < sizeof(KSATTRIBUTE))
        {
            return STATUS_INVALID_PARAMETER;
        }

        if (attributeHeader->Attribute == KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE)
        {
            KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE* signalProcessingModeAttribute;

            if (cbRemaining < sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE))
            {
                return STATUS_INVALID_PARAMETER;
            }

            if (attributeHeader->Size != sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE))
            {
                return STATUS_INVALID_PARAMETER;
            }

            signalProcessingModeAttribute = (KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE*)attributeHeader;

            // Return mode to caller.
            *_pSignalProcessingMode = signalProcessingModeAttribute->SignalProcessingMode;
        }
        else
        {
            return STATUS_NOT_SUPPORTED;
        }

        // Adjust pointer and buffer size to next attribute (QWORD aligned)
        ULONG cbAttribute = ((attributeHeader->Size + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT);

        attributeHeader = (PKSATTRIBUTE) (((PBYTE)attributeHeader) + cbAttribute);
        cbRemaining -= cbAttribute;
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::IsFormatSupported
(
    _In_ ULONG          _ulPin,
    _In_ BOOLEAN        _bCapture,
    _In_ PKSDATAFORMAT  _pDataFormat
)
{
    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::IsFormatSupported]"));

    NTSTATUS                            ntStatus = STATUS_NO_MATCH;
    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE  pPinFormats = NULL;
    ULONG                               cPinFormats = 0;

    UNREFERENCED_PARAMETER(_bCapture);

    if (_ulPin >= m_pMiniportPair->WaveDescriptor->PinCount)
    {
        return STATUS_INVALID_PARAMETER;
    }

    cPinFormats = GetPinSupportedDeviceFormats(_ulPin, &pPinFormats);

    for (UINT iFormat = 0; iFormat < cPinFormats; iFormat++)
    {
        PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pFormat = &pPinFormats[iFormat];
        // KSDATAFORMAT VALIDATION
        if (!IsEqualGUIDAligned(pFormat->DataFormat.MajorFormat, _pDataFormat->MajorFormat)) { continue; }
        if (!IsEqualGUIDAligned(pFormat->DataFormat.SubFormat, _pDataFormat->SubFormat)) { continue; }
        if (!IsEqualGUIDAligned(pFormat->DataFormat.Specifier, _pDataFormat->Specifier)) { continue; }
        if (pFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEX)) { continue; }

        // WAVEFORMATEX VALIDATION
        PWAVEFORMATEX pWaveFormat = reinterpret_cast<PWAVEFORMATEX>(_pDataFormat + 1);
        
        if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE)
        {
            if (pWaveFormat->wFormatTag != EXTRACT_WAVEFORMATEX_ID(&(pFormat->WaveFormatExt.SubFormat))) { continue; }
        }
        if (pWaveFormat->nChannels  != pFormat->WaveFormatExt.Format.nChannels) { continue; }
        if (pWaveFormat->nSamplesPerSec != pFormat->WaveFormatExt.Format.nSamplesPerSec) { continue; }
        if (pWaveFormat->nBlockAlign != pFormat->WaveFormatExt.Format.nBlockAlign) { continue; }
        if (pWaveFormat->wBitsPerSample != pFormat->WaveFormatExt.Format.wBitsPerSample) { continue; }
        if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE)
        {
            ntStatus = STATUS_SUCCESS;
            break;
        }

        // WAVEFORMATEXTENSIBLE VALIDATION
        if (pWaveFormat->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { continue; }

        PWAVEFORMATEXTENSIBLE pWaveFormatExt = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pWaveFormat);
        if (pWaveFormatExt->Samples.wValidBitsPerSample != pFormat->WaveFormatExt.Samples.wValidBitsPerSample) { continue; }
        if (pWaveFormatExt->dwChannelMask != pFormat->WaveFormatExt.dwChannelMask) { continue; }
        if (!IsEqualGUIDAligned(pWaveFormatExt->SubFormat, pFormat->WaveFormatExt.SubFormat)) { continue; }

        ntStatus = STATUS_SUCCESS;
        break;
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::PropertyHandlerProposedFormat
(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
)
{
    PKSP_PIN                kspPin                  = NULL;
    PKSDATAFORMAT           pKsFormat               = NULL;
    ULONG                   cbMinSize               = 0;
    NTSTATUS                ntStatus                = STATUS_INVALID_PARAMETER;

    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat]"));
    
    // All properties handled by this handler require at least a KSP_PIN descriptor.

    // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY.
    if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property)))
    {
        return STATUS_INVALID_PARAMETER;
    }

    // Extract property descriptor from property request instance data
    kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId);

    //
    // This method is valid only on streaming pins.
    //
    if (IsSystemRenderPin(kspPin->PinId) ||
        IsSystemCapturePin(kspPin->PinId))
    {
        ntStatus = STATUS_SUCCESS;
    }
    else if (IsBridgePin(kspPin->PinId))
    {
        ntStatus = STATUS_NOT_SUPPORTED;
    }
    else 
    {
        ntStatus = STATUS_INVALID_PARAMETER;
    }

    if (!NT_SUCCESS(ntStatus))
    {
        return ntStatus;
    }

    cbMinSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE);
   
    // Handle KSPROPERTY_TYPE_BASICSUPPORT query
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ULONG flags = PropertyRequest->PropertyItem->Flags;
        
        return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL);
    }

    // Verify value size
    if (PropertyRequest->ValueSize == 0)
    {
        PropertyRequest->ValueSize = cbMinSize;
        return STATUS_BUFFER_OVERFLOW;
    }
    if (PropertyRequest->ValueSize < cbMinSize)
    {
        return STATUS_BUFFER_TOO_SMALL;
    }

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
    {
        pKsFormat = (PKSDATAFORMAT)PropertyRequest->Value;
        ntStatus = IsFormatSupported(kspPin->PinId, IsSystemCapturePin(kspPin->PinId) || FALSE, pKsFormat);
        if (!NT_SUCCESS(ntStatus))
        {
            return ntStatus;
        }
    }

    return ntStatus;
} // PropertyHandlerProposedFormat

//=============================================================================
#pragma code_seg()
NTSTATUS
CMiniportWaveRT_EventHandler_PinCapsChange
(
_In_  PPCEVENT_REQUEST EventRequest
)
{
    CMiniportWaveRT* miniport = reinterpret_cast<CMiniportWaveRT*>(EventRequest->MajorTarget);
    return miniport->EventHandler_PinCapsChange(EventRequest);
}

//=============================================================================
#pragma code_seg()
NTSTATUS
CMiniportWaveRT::EventHandler_PinCapsChange
(
_In_  PPCEVENT_REQUEST EventRequest
)
{
    if (*EventRequest->EventItem->Set != KSEVENTSETID_PinCapsChange)
    {
        return STATUS_INVALID_PARAMETER;
    }

    switch (EventRequest->Verb)
    {
        // Do we support event handling?!?
    case PCEVENT_VERB_SUPPORT:
        break;
        // We should add the event now!
    case PCEVENT_VERB_ADD:
        // If we have the interface and EventEntry is defined ...
        if (EventRequest->EventEntry)
        {
            switch (EventRequest->EventItem->Id)
            {
                // Add pincaps format change event to support the force sample rate feature
                case KSEVENT_PINCAPS_FORMATCHANGE:
                    m_pPortEvents->AddEventToEventList(EventRequest->EventEntry);
                    break;
                default:
                    return STATUS_INVALID_PARAMETER;
                    break;
            }
        }
        else
        {
            return STATUS_UNSUCCESSFUL;
        }
        break;

    case PCEVENT_VERB_REMOVE:
        // We cannot remove the event but we can stop generating the
        // events. However, it also doesn't hurt to always generate them ...
        break;

    default:
        return STATUS_INVALID_PARAMETER;
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::PropertyHandlerProposedFormat2
(
    _In_ PPCPROPERTY_REQUEST      PropertyRequest
)
{
    PKSP_PIN                kspPin                  = NULL;
    ULONG                   cbMinSize               = 0;
    NTSTATUS                ntStatus                = STATUS_INVALID_PARAMETER;
    ULONG                   numModes                = 0;
    MODE_AND_DEFAULT_FORMAT *modeInfo               = NULL;
    MODE_AND_DEFAULT_FORMAT *modeTemp               = NULL;
    PKSMULTIPLE_ITEM        pKsItemsHeader          = NULL;
    PKSMULTIPLE_ITEM        pKsItemsHeaderOut       = NULL;
    size_t                  cbItemsList             = 0;
    GUID                    signalProcessingMode    = {0};
    BOOLEAN                 bFound                  = FALSE;
    ULONG                   i;

    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat2]"));
    
    // All properties handled by this handler require at least a KSP_PIN descriptor.

    // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY.
    if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property)))
    {
        return STATUS_INVALID_PARAMETER;
    }

    // Extract property descriptor from property request instance data
    kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId);

    if (kspPin->PinId >= m_pMiniportPair->WaveDescriptor->PinCount)
    {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // This property is supported only on some streaming pins.
    //
    numModes = GetPinSupportedDeviceModes(kspPin->PinId, &modeInfo);

    ASSERT((modeInfo != NULL && numModes > 0) || (modeInfo == NULL && numModes == 0));

    if (modeInfo == NULL)
    {
        return STATUS_NOT_SUPPORTED;
    }

    //
    // Even for pins that support modes, the pin might not support proposed formats
    //
    bFound = FALSE;
    for (i=0, modeTemp=modeInfo; i<numModes; ++i, ++modeTemp)
    {
        if (modeTemp->DefaultFormat != NULL)
        {
            bFound = TRUE;
            break;
        }
    }

    if (!bFound)
    {
        return STATUS_NOT_SUPPORTED;
    }

    //
    // The property is generally supported on this pin. Handle basic support request.
    //
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL);
    }

    //
    // Get the mode if specified.
    //
    pKsItemsHeader = (PKSMULTIPLE_ITEM)(kspPin + 1);
    cbItemsList = (((PBYTE)PropertyRequest->Instance) + PropertyRequest->InstanceSize) - (PBYTE)pKsItemsHeader;

    ntStatus = GetAttributesFromAttributeList(pKsItemsHeader, cbItemsList, &signalProcessingMode);
    if (!NT_SUCCESS(ntStatus))
    {
        return ntStatus;
    }

    //
    // Get the info associated with this mode.
    //
    bFound = FALSE;
    for (i=0; i<numModes; ++i, ++modeInfo)
    {
        if (modeInfo->Mode == signalProcessingMode)
        {
            bFound = TRUE;
            break;
        }
    }

    // Either the mode isn't supported, or the driver doesn't support a
    // proprosed format for this specific mode.
    if (!bFound || modeInfo->DefaultFormat == NULL)
    {
        return STATUS_NOT_SUPPORTED;
    }

    //
    // Compute output data buffer.
    //
    cbMinSize = modeInfo->DefaultFormat->FormatSize;
    cbMinSize = (cbMinSize + 7) & ~7;

    pKsItemsHeaderOut = (PKSMULTIPLE_ITEM)((PBYTE)PropertyRequest->Value + cbMinSize);

    if (cbItemsList > MAXULONG)
    {
        return STATUS_INVALID_PARAMETER;
    }

    // Total # of bytes.
    ntStatus = RtlULongAdd(cbMinSize, (ULONG)cbItemsList, &cbMinSize);
    if (!NT_SUCCESS(ntStatus))
    {
        return STATUS_INVALID_PARAMETER;
    }
        
    // Property not supported.
    if (cbMinSize == 0)
    {
        return STATUS_NOT_SUPPORTED;
    }

    // Verify value size
    if (PropertyRequest->ValueSize == 0)
    {
        PropertyRequest->ValueSize = cbMinSize;
        return STATUS_BUFFER_OVERFLOW;
    }
    if (PropertyRequest->ValueSize < cbMinSize)
    {
        return STATUS_BUFFER_TOO_SMALL;
    }

    // Only GET is supported for this property
    if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) == 0)
    {
        return STATUS_INVALID_DEVICE_REQUEST;
    }

    // Copy the proposed default format.
    RtlCopyMemory(PropertyRequest->Value, modeInfo->DefaultFormat, modeInfo->DefaultFormat->FormatSize);

    // Copy back the attribute list.
    ASSERT(cbItemsList > 0);
    ((KSDATAFORMAT*)PropertyRequest->Value)->Flags = KSDATAFORMAT_ATTRIBUTES;
    RtlCopyMemory(pKsItemsHeaderOut, pKsItemsHeader, cbItemsList);
    
    PropertyRequest->ValueSize = cbMinSize;

    return STATUS_SUCCESS;
} // PropertyHandlerProposedFormat

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CMiniportWaveRT::UpdateDrmRights
(
    void
)
/*++

Routine Description:

  Updates the mixed DrmRights. This is done by creating an array of existing
  content ids and asking DrmPort to create a new contend id with a mixed
  DrmRights structure.
  The new DrmRights structure should be enforced, if everything goes well.

Arguments:

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    DPF_ENTER(("[CMiniportWaveRT::UpdateDrmRights]"));
    
    NTSTATUS        ntStatus                = STATUS_UNSUCCESSFUL;
    ULONG           ulMixDrmContentId       = 0;
    BOOL            fCreatedContentId       = FALSE;
    DRMRIGHTS       MixDrmRights            = {FALSE, 0, FALSE};
    ULONG           ulContentIndex          = 0;
    ULONG*          ulContentIds            = NULL;

    //
    // This function only runs if IID_DrmPort is implemented in Wave port.
    //
    if (!m_pDrmPort)
    {
        return STATUS_UNSUCCESSFUL;
    }

    ulContentIds = new (POOL_FLAG_NON_PAGED, MINWAVERT_POOLTAG) ULONG[m_ulMaxSystemStreams + m_ulMaxOffloadStreams];
    if (!ulContentIds)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Create an array of all StreamIds.
    //
    for (ULONG i = 0; i < m_ulMaxSystemStreams; i++)
    {
        if (m_SystemStreams[i])
        {
            ulContentIds[ulContentIndex] = m_SystemStreams[i]->m_ulContentId;
            ulContentIndex++;
        }
    }

    //
    // Create the new contentId.
    //
    if (ulContentIndex)
    {
        ntStatus = 
            m_pDrmPort->CreateContentMixed
            (
                ulContentIds,
                ulContentIndex,
                &ulMixDrmContentId
            );
        
        if (NT_SUCCESS(ntStatus))
        {
            fCreatedContentId = TRUE;
            ntStatus = 
                m_pDrmPort->GetContentRights
                (
                    ulMixDrmContentId, 
                    &MixDrmRights
                );
        }
    }

    //
    // If successful, destroy the old ContentId and update global rights.
    //
    if (NT_SUCCESS(ntStatus))
    {
        m_pDrmPort->DestroyContent(m_ulMixDrmContentId);
        m_ulMixDrmContentId = ulMixDrmContentId;
        RtlCopyMemory(&m_MixDrmRights, &MixDrmRights, sizeof(m_MixDrmRights));

        //
        // At this point the driver should enforce the new DrmRights.
        // The sample driver handles DrmRights per stream basis, and 
        // stops writing the stream to disk, if CopyProtect = TRUE.
        //
    } 

    //
    // Cleanup if failed
    // 
    if (!NT_SUCCESS(ntStatus) && fCreatedContentId)
    {
        m_pDrmPort->DestroyContent(ulMixDrmContentId);
    }

    //
    // Free allocated memory.
    //
    ASSERT(ulContentIds);
    delete [] ulContentIds;
    ulContentIds = NULL;

    return ntStatus;
} // UpdateDrmRights

#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_WaveFilter
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Redirects general property request to miniport object

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    NTSTATUS            ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    CMiniportWaveRT*    pWaveHelper = reinterpret_cast<CMiniportWaveRT*>(PropertyRequest->MajorTarget);

    if (pWaveHelper == NULL)
    {
        return STATUS_INVALID_PARAMETER;
    }

    pWaveHelper->AddRef();


    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin))
    {
        switch (PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_PIN_PROPOSEDATAFORMAT:
                ntStatus = pWaveHelper->PropertyHandlerProposedFormat(PropertyRequest);
                break;
            
            case KSPROPERTY_PIN_PROPOSEDATAFORMAT2:
                ntStatus = pWaveHelper->PropertyHandlerProposedFormat2(PropertyRequest);
                break;

            default:
                DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]"));
        }
    }

    pWaveHelper->Release();

    return ntStatus;
} // PropertyHandler_WaveFilter

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_GenericPin
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
{
    NTSTATUS                ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    CMiniportWaveRT*        pWave = NULL;
    CMiniportWaveRTStream * pStream = NULL;

    PAGED_CODE();

    if (PropertyRequest->MajorTarget == NULL ||
        PropertyRequest->MinorTarget == NULL)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        goto exit;
    }
    
    //
    // Get a ref to the miniport.
    //
    pWave = MajorTarget_to_Obj(PropertyRequest->MajorTarget);
    pWave->AddRef();

    //
    // Get a ref to the stream.
    //
    pStream = MinorTarget_to_Obj(PropertyRequest->MinorTarget);
    pStream->AddRef();

exit:

    SAFE_RELEASE(pStream);
    SAFE_RELEASE(pWave);
    
    return ntStatus;
}

#pragma code_seg()



<|start_filename|>storage/miniports/storahci/src/hbastat.c<|end_filename|>
﻿/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    hbastat.c

Abstract:
    This file contains the core logic for managing AHCI controller state transitions.
    Primarily it focuses on starting and stopping the channel and performing resets.

Notes:

Revision History: 

--*/

#if _MSC_VER >= 1200
#pragma warning(push)
#endif

#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union

#include "generic.h"


__inline
VOID
ForceGenxSpeed (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ ULONG GenNumber
    )
{
    AHCI_SERIAL_ATA_CONTROL sctl;
    AHCI_SERIAL_ATA_ERROR   serr;
    UCHAR                   spd;

    if (GenNumber == 0) {
        return;
    }

    spd = (UCHAR)min(GenNumber, ChannelExtension->AdapterExtension->CAP.ISS);

    sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong);
    if (sctl.SPD == spd) {
        return;
    }

    //Set speed limitation.
    sctl.SPD = spd;
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong);
    //Reset the port
    AhciCOMRESET(ChannelExtension, ChannelExtension->Px);
    //and clear out SERR
    serr.AsUlong = (ULONG)~0;
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong, serr.AsUlong);

    return;
}


BOOLEAN
AhciAdapterReset(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
/*
This function brings the HBA and all ports to the H:Idle and P:Idle states by way of HBA Reset and P:Reset

Called By:
    AhciHwFindAdapter if needed.

It assumes:
    AdapterExtension->ABAR_Address is valid.

It performs:
    //10.4.3    HBA Reset
    //5.3.2.1    P:Reset
    //5.3.2.2    P:Init

Return value:
    BOOLEAN - success or not.  note the many controller registers that hold reset values.
*/
{
    AHCI_Global_HBA_CONTROL ghc;
    PAHCI_MEMORY_REGISTERS  abar = AdapterExtension->ABAR_Address;
    int i;

    if (abar == NULL) {
        return FALSE;
    }

//10.4.3    HBA Reset
  //If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the problem, software may reset the entire HBA by setting GHC.HR to ‘1’.  When software sets the GHC.HR bit to ‘1’, the HBA shall perform an internal reset action.
    ghc.HR = 1;
    StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);

  //The bit shall be cleared to ‘0’ by the HBA when the reset is complete.  A software write of ‘0’ to GHC.HR shall have no effect.  To perform the HBA reset, software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that the HBA reset has completed.
    ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong);
    //5.2.2.1    H:Init
    //5.2.2.2    H:WaitForAhciEnable
    for (i = 0;(i < 50) && (ghc.HR == 1); i++) {
        StorPortStallExecution(20000);  //20 milliseconds
        ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong);
    }

    //If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in a hung or locked state.
    if(i == 50) {
        AdapterExtension->ErrorFlags = (1 << 29);
        return FALSE;
    }

    //When GHC.HR is set to ‘1’, GHC.AE, GHC.IE, the IS register, and all port register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the HBA’s register memory space are reset.  The HBA’s configuration space and all other global registers/bits are not affected by setting GHC.HR to ‘1’.  Any HwInit bits in the port specific registers are not affected by setting GHC.HR to ‘1’.  The port specific registers PxFB, PxFBU, PxCLB, and PxCLBU are not affected by setting GHC.HR to ‘1’.  If the HBA supports staggered spin-up, the PxCMD.SUD bit will be reset to ‘0’; software is responsible for setting the PxCMD.SUD and PxSCTL.DET fields appropriately such that communication can be established on the Serial ATA link.  If the HBA does not support staggered spin-up, the HBA reset shall cause a COMRESET to be sent on the port

    return TRUE;
}


VOID
AhciCOMRESET(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PAHCI_PORT Px
    )
/*
PHY Reset:COMRESET
    SCTL.DET Controls the HBA’s device detection and interface initialization.
    DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized.  While this field is 1h, COMRESET is transmitted on the interface.
    Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface.
    since we are in 5.3.2.3    P:NotRunning and PxCMD.SUD = ‘0’ does this still take us to P:StartComm?
Called By:
    AhciPortReset,AhciNonQueuedErrorRecovery,P_Running_WaitWhileDET1,AhciHwControlIdeStart
It assumes:
    nothing
It performs:
    5.3.2.11    P:StartComm
    (overview)
    1 Prepare for COMRSET
    2 Perform COMRESET
    3 Clean up after COMRESET
    (details)
    1.1 make sure ST is 0.  DET cannot be altered while ST == 1 as per AHCI 1.1 section 5.3.2.3.
    1.2 Don't allow a comm init to trigger a hotplug
    1.3 Ignore Hotplug events until the channel is started again
    2.1 Perform COMRESET
    2.2 Wait for DET to be set
    3.1 Clear SERR

Affected Variables,Registers:
    CMD.ST, SCTL.DET, CI, SACT, SERR

Return Values:
    none
*/
{
    AHCI_SERIAL_ATA_CONTROL sctl;
    AHCI_COMMAND            cmd;
    AHCI_INTERRUPT_ENABLE   ieOrig;
    AHCI_INTERRUPT_ENABLE   ieTemp;
    AHCI_SERIAL_ATA_STATUS  ssts;
    UCHAR                   i;

    RecordExecutionHistory(ChannelExtension, 0x00000010);//AhciCOMRESET

  //1.1 make sure ST is 0.  DET cannot be altered while ST == 1 as per AHCI 1.1 section 5.3.2.3.
    cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->CMD.AsUlong);
    if(cmd.ST == 1) {
        RecordExecutionHistory(ChannelExtension, 0x10fa0010);   //AhciCOMRESET, PxCMD.ST is 1. Abort
        return;
    }

  //1.2 Don't allow a COMINIT to trigger a hotplug
    ieTemp.AsUlong = ieOrig.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->IE.AsUlong);
    ieTemp.PRCE = 0;        // "PhyRdy Change Interrupt Enable", no generate interrupt with PxIS.PRCS
    ieTemp.PCE = 0;         // "Port Change Interrupt Enable", no generate interrupt with PxIS.PCS
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->IE.AsUlong, ieTemp.AsUlong);

  //1.3 Ignore Hotplug events until the channel is started again
    ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE;

  //2.1 Perform COMRESET
    sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong);
    sctl.DET = 1;
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong, sctl.AsUlong);
    //DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized.  While this field is 1h, COMRESET is transmitted on the interface.
    //Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface.
    StorPortStallExecution(1000);

    sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong);
    sctl.DET = 0;
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong, sctl.AsUlong);

  //2.2 Wait for DET to be set
    // AHCI 10.4.2 After clearing PxSCTL.DET to 0h, software should wait for communication to be re-established as indicated by bit 0 of PxSSTS.DET being set to ‘1’.
    // typically, it will be done in 10ms. max wait 30ms to be safe
    StorPortStallExecution(50);
    ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SSTS.AsUlong);

    if (ssts.DET == 0) {
        for (i = 0; i < (AHCI_PORT_WAIT_ON_DET_COUNT * 10); i++) {
            StorPortStallExecution(1000);
            ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SSTS.AsUlong);
            if (ssts.DET != 0) {
                break;
            }
        }
    }

  //2.3 Enable Hotplug again if it was enabled before
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->IE.AsUlong, ieOrig.AsUlong);

  //3.1 Clear SERR
    // AHCI 10.4.2 software should write all 1s to the PxSERR register to clear any bits that were set as part of the port reset.
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->SERR.AsUlong, (ULONG)~0);

  //3.2 record the count of reset (both internal or asked by port driver)
    ChannelExtension->DeviceExtension[0].IoRecord.TotalResetCount++;

    RecordExecutionHistory(ChannelExtension, 0x10000010);//Exit AhciCOMRESET
}


BOOLEAN
P_NotRunning(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PAHCI_PORT Px
    )
/*
Called By:

It assumes:
    nothing

It performs:
    5.3.2.3    P:NotRunning
    (overview)
    1 Clear CMD.ST
    2 Clear CMD.FRE
    3 Update the Channel Start State

    (details)
    1.1 Clear CMD.ST
    1.2 Verify CR cleared
    1.3 Verify CI cleared
    2.1 Clear CMD.FRE
    2.2 Verify FR cleared
    3.1 Update the Channel Start State

Affected Variables,Registers:
    cmd.ST cmd.CR cmd.FRE cmd.FR are 0 and the controller is in P:NotRunning

Return value:
    FALSE if the ST,CR,FRE, and FRE could not be cleared.  TRUE if success.
    If FALSE is returned, the caller may issue RESET to recover
*/
{
    PAHCI_ADAPTER_EXTENSION adapterExtension;
    BOOLEAN                 supportsCLO;
    AHCI_COMMAND            cmd;
    AHCI_TASK_FILE_DATA     tfd;
    ULONG                   ci;
    UCHAR                   i;

    adapterExtension = ChannelExtension->AdapterExtension;
    supportsCLO = CloResetEnabled(adapterExtension);     //3.1.1 make sure HBA supports Command List Override.

    if (ChannelExtension->StartState.ChannelNextStartState == Stopped) {
        RecordExecutionHistory(ChannelExtension, 0x00030011);   //P_NotRunning, already stopped, nothing to do.
        return TRUE;
    }

    // record this function is called.
    if (supportsCLO) {
        RecordExecutionHistory(ChannelExtension, 0x10810011);   //P_NotRunning, CLO is enabled.
    } else {
        RecordExecutionHistory(ChannelExtension, 0x00000011);   //P_NotRunning, CLO isn't enabled.
    }

  //1.1 Clear CMD.ST
    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
    //AHCI 10.3.2 on FRE it says:
    //Software shall not clear this [FRE] bit while PxCMD.ST remains set to ‘1’."
    //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return ‘0’ when read.
    cmd.ST = 0;
    StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong);

  //1.1.1 Update the Channel Start State after we ask the channel to stop
    ChannelExtension->StartState.ChannelNextStartState = Stopped;

    StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Port Stopped\n", ChannelExtension->PortNumber);

    if (supportsCLO) {
        // AHCI 3.3.7 make sure PxCMD.ST is 0.
        for (i = 1; i < 101; i++) {
            cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
            if (cmd.ST == 0) {
                break;
            }
            StorPortStallExecution(5000);  //5 milliseconds
        }
        if (i == 101) {
            RecordExecutionHistory(ChannelExtension, 0x10820011); //P_NotRunning, After 500ms of writing 0 to PxCMD.ST, it's still 1.
        }

        tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong);
        if (tfd.STS.BSY) {
          // AHCI 3.3.7 Command List Override (CLO):  Setting this bit to ‘1’ causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to ‘0’.
          // This allows a software reset to be transmitted to the device regardless of whether the BSY and DRQ bits are still set in the PxTFD.STS register.

          // Do this to make sure the port can stop
            cmd.CLO = 1;
            StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong);
        }
    }

  //1.2 Verify CR cleared
    //AHCI 10.1.2 - 3: wait cmd.CR to be 0. Software should wait at least 500 milliseconds for this to occur.
    for (i = 1; i < 101; i++) {
        cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
        if ( (cmd.CR == 0) && (cmd.ST == 0) ) {
            break;
        }
        StorPortStallExecution(5000);  //5 milliseconds
    }

    //AHCI 10.4.2 If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover.
    if (i == 101) {
        RecordExecutionHistory(ChannelExtension, 0x10350011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly
        return FALSE;
    }

  //1.3 Verify CI cleared
    //This must have the effect of clearing CI as per AHCI section 3.3.14
    for (i = 1; i < 101; i++) {
        ci = StorPortReadRegisterUlong(adapterExtension, &Px->CI);
        if (ci == 0) {
            break;
        }
        StorPortStallExecution(50);  //50 microseconds
    }

    //If CI does not clear to ‘0’ correctly abort the stop
    if (i == 101) {
        RecordExecutionHistory(ChannelExtension, 0x10360011); // P_NotRunning, CI does not clear to ‘0’
        return FALSE;
    }

  //2.1 Clear CMD.FRE
    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
    if( (cmd.FRE | cmd.FR) != 0 ) {
        //If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least 500 milliseconds for PxCMD.FR to return ‘0’ when read.
        //AHCI 10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to ‘1’.
        cmd.FRE = 0;
        StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong);

        if (supportsCLO) {
            tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong);
            if (tfd.STS.BSY) {
                NT_ASSERT(FALSE);     //don't expect BSY is set again
                cmd.CLO = 1;
                StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong);
            }
        }

        //Software should wait at least 500 milliseconds for this to occur.
        for (i = 1; i < 101; i++) {
            cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
            if( (cmd.CR == 0) && (cmd.FR == 0) && (cmd.ST == 0) && (cmd.FRE == 0) ) {
                break;
            }
            StorPortStallExecution(5000);    //  5 milliseconds
        }

        if ( i == 101 ) {  //If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover.
            if (supportsCLO) {
                RecordExecutionHistory(ChannelExtension, 0x10380011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO enabled
            } else {
                RecordExecutionHistory(ChannelExtension, 0x10370011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO not enabled
            }
            return FALSE;
        }
    }

  //2.2 wait for CLO to clear
    // AHCI 3.3.7 Software must wait for CLO to be cleared to ‘0’ before setting PxCMD.ST to ‘1’.
    // register bit CLO might be set in this function, and it should have been cleared before function exit.
    if ( supportsCLO && (cmd.CLO == 0) ) {
        for (i = 1; i < 101; i++) {
            cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong);
            if( (cmd.CLO == 0) ) {
                break;
            }
            StorPortStallExecution(5000);    //  5 milliseconds
        }

        if ( i == 101 ) {
            RecordExecutionHistory(ChannelExtension, 0x10390011); // P_NotRunning, PxCMD.CLO not clear to ‘0’, CLO enabled
            return FALSE;
        }
    }

    RecordExecutionHistory(ChannelExtension, 0x10000011); //Exit P_NotRunning, port stopped

    return TRUE;
}


VOID
AhciAdapterRunAllPorts(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
{
    ULONG i;

    //only start with the first one. callback routine will go over all ports.
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if (AdapterExtension->PortExtension[i] != NULL) {
            AdapterExtension->InRunningPortsProcess = TRUE;
            P_Running_StartAttempt(AdapterExtension->PortExtension[i], TRUE);
            return;
        }
    }

    return;
}

VOID
RunNextPort(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    )
{
    PAHCI_ADAPTER_EXTENSION adapterExtension;
    PAHCI_CHANNEL_EXTENSION nextPortExtension;
    ULONG i;

    adapterExtension = ChannelExtension->AdapterExtension;
    nextPortExtension = NULL;

    //starting all ports process either not started or already completed.
    if (!adapterExtension->InRunningPortsProcess) {
        return;
    }

    //get next port extension
    for (i = (ChannelExtension->PortNumber + 1); i <= adapterExtension->HighestPort; i++) {
        if (adapterExtension->PortExtension[i] != NULL) {
            nextPortExtension = adapterExtension->PortExtension[i];
            break;
        }
    }

    // run next port or declare the ports running process completed.
    if (nextPortExtension != NULL) {
        if ((nextPortExtension->StartState.ChannelNextStartState == 0) ||
            (nextPortExtension->StartState.ChannelNextStartState == Stopped)) {
            P_Running_StartAttempt(nextPortExtension, AtDIRQL);
        } else {
            // return, do nothing here as the Port Start effort has been made.
            // RunNextPort() is only used in AhciAdapterRunAllPorts() which is called by AhciHwFindAdapter.
            // In other cases, P_Running_StartAttempt() will be called directly for each port.
            return;
        }
    } else {
        //starting all ports process is completed.
        adapterExtension->InRunningPortsProcess = FALSE;

        //
        // Note: If we are at DIRQL when get here, it implies previous port start finishes quickly since no
        //       timer scheduled during port start. Then there is no need to log the port start time.
        //
        if (!AtDIRQL) {
            AhciPortStartTelemetry(adapterExtension);
        }
    }

    return;
}

VOID
P_Running_StartAttempt(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    )
/*

Called By:
It assumes:
    Channel Start may already be in progress (not function reentrant)
It performs:
    1 Initializes the Channel Start state machine
    2 Starts the Channel Start state machine

Affected Variables/Registers:
    none
*/
{
    STOR_LOCK_HANDLE    lockhandle = {InterruptLock, 0};

    if (!AtDIRQL) {
        AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    }

  //1 Initializes the Channel Start state machine
    ChannelExtension->StartState.ChannelNextStartState = WaitOnDET;
    ChannelExtension->StartState.ChannelStateDETCount = 0;
    ChannelExtension->StartState.ChannelStateDET1Count = 0;
    ChannelExtension->StartState.ChannelStateDET3Count = 0;
    ChannelExtension->StartState.ChannelStateFRECount = 0;
    ChannelExtension->StartState.ChannelStateBSYDRQCount = 0;
    ChannelExtension->StartState.AtDIRQL = AtDIRQL ? 1 : 0;
    ChannelExtension->StartState.DirectStartInProcess = 1;

    if (!AtDIRQL) {
        AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    }

  //2 Starts the Channel Start state machine
    P_Running(ChannelExtension, FALSE);
    return;
}

VOID
P_Running_Callback(
    _In_ PVOID AdapterExtension,
    _In_opt_ PVOID ChannelExtension
    )
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)ChannelExtension;
    ULONG                   callbackIndex = 0;

    if (channelExtension == NULL) {
        NT_ASSERT(FALSE);
        return;
    }

    NT_ASSERT(AdapterExtension == (PVOID)(channelExtension->AdapterExtension));

    UNREFERENCED_PARAMETER(AdapterExtension);

    callbackIndex = channelExtension->StartState.ChannelStateDETCount +
                    channelExtension->StartState.ChannelStateDET1Count +
                    channelExtension->StartState.ChannelStateDET3Count +
                    channelExtension->StartState.ChannelStateFRECount +
                    channelExtension->StartState.ChannelStateBSYDRQCount;

    // only clear the bit if this is the first timer callback in Port Start process
    if (callbackIndex == 1) {
        STOR_LOCK_HANDLE    lockhandle = {InterruptLock, 0};

        AhciInterruptSpinlockAcquire(AdapterExtension, channelExtension->PortNumber, &lockhandle);

        if (channelExtension->StartState.AtDIRQL == 1) {
            channelExtension->StartState.AtDIRQL = 0;
        }
        if (channelExtension->StartState.DirectStartInProcess == 1) {
            channelExtension->StartState.DirectStartInProcess = 0;
        }

        AhciInterruptSpinlockRelease(AdapterExtension, channelExtension->PortNumber, &lockhandle);
    }

    P_Running(channelExtension, TRUE);

    return;
}


BOOLEAN
P_Running(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    The purpose of this function is to verify and drive the Start Channel state machine

Called By:
    P_Running_StartAttempt

    Through StorPortRequestTimer which is set up by each of the Start Channel state functions:
            P_Running_WaitOnDET
            P_Running_WaitWhileDET1
            P_Running_WaitOnDET3
            P_Running_WaitOnFRE
            P_Running_WaitOnBSYDRQ
It assumes:
    Nothing

It performs:
    (overview)
    1 Verify Start can/needs to be done
    2 Run the Start Channel state machine

    (details)
    1.1 First, is the channel initialized.  Would turning start on blow up the machine?
    1.2 Next, is the port somehow already running?
    1.3 Make sure the device knows it is supposed to be spun up
    1.4 Check to make sure that FR and CR are both 0.  Attempt to bring the controller into a consistent state by stopping the controller.
    1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’

    2.1 Dispatch to the current state (states are responsible for selecting the next state)

Affected Variables/Registers:
    CMD

Return Values:
    TRUE if the state machine can be run
    FALSE if the state machine can't
*/
{

    AHCI_COMMAND            cmd;
    AHCI_SERIAL_ATA_STATUS  ssts;
    PAHCI_PORT              px = ChannelExtension->Px;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000015);//P_Running
    }

  //1.1 First, is the channel initialized.  Would turning start on blow up the machine?
    if( !IsPortStartCapable(ChannelExtension) ) {
        RecordExecutionHistory(ChannelExtension, 0x10120015);//No Channel Resources
        RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));
        return FALSE;
    }

    if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) {
        RecordExecutionHistory(ChannelExtension, 0x10130015);//This is timer callback and a direct port start process has been started separately, bail out this one.
        RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));
        return FALSE;
    }

  //1.2 Next, is the port somehow already running?
    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &px->CMD.AsUlong);
    if( (cmd.ST == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.FR == 1) ) {
        ChannelExtension->StartState.ChannelNextStartState = StartComplete;
        RecordExecutionHistory(ChannelExtension, 0x30000015);//Channel Already Running
        RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));
        return TRUE;
    }

  //1.3 Make sure the device knows it is supposed to be spun up
    cmd.SUD = 1;
    StorPortWriteRegisterUlong(adapterExtension, &px->CMD.AsUlong, cmd.AsUlong);

  //1.4 Check to make sure that FR and CR are both 0.  If not then ST and/or FRE are 0 which a bad scenario.
    if ( ( (cmd.FR == 1) && (cmd.FRE == 0) ) ||
         ( (cmd.CR == 1) && (cmd.ST  == 0) ) ) {
        //Attempt to bring the controller into a consistent state by stopping the controller.
        if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) {
            RecordExecutionHistory(ChannelExtension, 0x10880015);   //CR or FR 1 when ST or FRE 0
            AhciCOMRESET(ChannelExtension, ChannelExtension->Px);   //Issue COMRESET to recover

            if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
                ((cmd.CR == 1) && (cmd.ST == 0))) {
                //In case the error cannot be recovered, fail the port start process.
                P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
                RecordExecutionHistory(ChannelExtension, 0x10150014);   //cmd.FR or cmd.CR cannot be stopped.
                return FALSE;
            } else {
                //Reset StartState to start the port again.
                ChannelExtension->StartState.ChannelNextStartState = WaitOnDET;
            }
        } else {
            //Reset StartState to start the port again.
            ChannelExtension->StartState.ChannelNextStartState = WaitOnDET;
        }
    }

  //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’
    ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &px->SSTS.AsUlong);
    if( ssts.DET == 0x4) {
        P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
        RecordExecutionHistory(ChannelExtension, 0x10150015);   //Channel Disabled
        return FALSE;
    }

  //2.1 Ok, run the Start Channel State Machine
    switch(ChannelExtension->StartState.ChannelNextStartState) {

        case WaitOnDET:
            P_Running_WaitOnDET(ChannelExtension, TimerCallbackProcess);
            break;
        case WaitWhileDET1:
            P_Running_WaitWhileDET1(ChannelExtension, TimerCallbackProcess);
            break;
        case WaitOnDET3:
            P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess);
            break;
        case WaitOnFRE:
            P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
            break;
        case WaitOnBSYDRQ:
            P_Running_WaitOnBSYDRQ(ChannelExtension, TimerCallbackProcess);
            break;
        default:
            // Above cases cover all intermediate states. If a port start process has been completed, just exit.
            break;
    }

    // record this function is called.
    RecordExecutionHistory(ChannelExtension, 0x10000015);   //Exit P_Running
    return TRUE;
}

VOID
P_Running_WaitOnDET(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    Search for Device Activity phase.  Use DET and IPM for any signs of life as defined in AHCI 1.2 section 10.1.2 and 10.3.1.
    Polled 3 times in 30ms

Called By:
    P_Running
It assumes:
    Controller is start capable
It performs:
    (overview)
    1 Look for signs of life while DET == 0
    2 Look for signs of life while DET == 1
    3 Look for signs of life which is DET == 3
    (details)
    1.1 When looking for device presence, seeing link level power management shall count as device present as per 10.3.1
    1.2 Wait for 1 second for DET to show signs of life
    1.3 After 30ms give up
    2.1 When looking for device presence, seeing link level power management shall count as device present as per 10.3.1
    2.2 Otherwise continue on to WaitOnDET1
    3.1 If DET==3 we are ready for WaitOnFRE already.

Affected Variables/Registers:
    none
*/
{
    AHCI_SERIAL_ATA_STATUS ssts;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;
    UCHAR                   waitMaxCount = AHCI_PORT_WAIT_ON_DET_COUNT;

    if (IsPortD3ColdEnabled(ChannelExtension)) {
        // if D3 Cold is enabled, a device was there; Add additional wait time to make sure device is able to get ready after a possible power on.
        waitMaxCount += 30;     // wait timer is 10ms, add 300ms for safe
    } else if (IsDumpMode(adapterExtension)) {
        //
        // If we're on the dump stack we may have crashed while the device was
        // in an indeterminant state so give it some more time to get ready.
        //
        waitMaxCount += 30;
    }

WaitOnDET_Start:
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000016);//P_Running_WaitOnDET
    }

  //1 Look for signs of life while DET == 0
    ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->SSTS.AsUlong);

    if (ssts.DET == 0)  { //When a COMRESET is sent to the device the PxSSTS.DET field shall be cleared to 0h.

      //1.1 When looking for device presence, seeing link level power management shall count as device present as per 10.3.1
        if ((ssts.IPM == 0x2) || (ssts.IPM == 0x6) || (ssts.IPM == 0x8)) {
            ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE;
            RecordExecutionHistory(ChannelExtension, 0x10800016);//P_Running_WaitOnDET is 0 w/ LPM activity, goto FRE
            P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
            return;
      //1.3 After 30ms give up (spec requires 10ms, use 30ms for safety). There is no device connected.
        //from SATA-IO spec 3.0 - 172.16.31.10:
        //If a device is present, the Phy shall detect device presence within 10 ms of a power-on reset (i.e. COMINIT shall be returned within 10 ms of an issued COMRESET).
        } else if (ChannelExtension->StartState.ChannelStateDETCount > waitMaxCount) {
            P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
            RecordExecutionHistory(ChannelExtension, 0x10ff0016);//P_Running_WaitOnDET Timed out, No Device
            return;
      //1.2 Wait for 30ms for DET to show signs of life
        } else {
            ChannelExtension->StartState.ChannelStateDETCount++;
            RecordExecutionHistory(ChannelExtension, 0x10810016);//P_Running_WaitOnDET is 0, still waiting

            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(10000); //10 milliseconds
                goto WaitOnDET_Start;
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 10000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(10000); //10 milliseconds
                    goto WaitOnDET_Start;
                }
            }
            return;
        }

    } else {
      //2 Look for signs of life while DET == 1
        if (ssts.DET == 1)  {
          //2.1 When looking for device presence, seeing link level power management shall count as device present as per 10.3.1
            if ((ssts.IPM == 0x2) || (ssts.IPM == 0x6) || (ssts.IPM == 0x8)) {
                ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE;
                RecordExecutionHistory(ChannelExtension, 0x10a00016);//P_Running_WaitOnDET is 1 w/ LPM activity, goto FRE
                P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
                return;
          //2.2 Otherwise continue on to WaitOnDET1
            } else {
                ChannelExtension->StartState.ChannelNextStartState = WaitWhileDET1;
                RecordExecutionHistory(ChannelExtension, 0x10a10016);//P_Running_WaitOnDET is 1
                P_Running_WaitWhileDET1(ChannelExtension, TimerCallbackProcess);
                return;
            }
      //3.1 If DET==3 we are ready for WaitOnFRE already.
        } else if (ssts.DET == 3)  {
            ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE;
            RecordExecutionHistory(ChannelExtension, 0x10a20016);//P_Running_WaitOnDET is 3
            P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
            return;

        } else {
            P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
            RecordExecutionHistory(ChannelExtension, 0x10a30016);//P_Running_WaitOnDET is Bogus, aborting
            return;
        }
    }
}

VOID
P_Running_WaitWhileDET1(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    Waiting on establishment of link level communications phase.
    A '1' should become a '3'.  If it doesn't, help it along.
    NOTE:   When a COMINIT is received, the PxSSTS.DET field shall be set to 1h.
            That means a device is detected, but communications has not finished
    Polled 100 times in 1 second

Called By:
    P_Running
It assumes:
    Channel is start capable
    DET was previously the value of 1
It performs:
    (overview)
    1 Look for signs of life which is DET == 3
    2 Look for signs of live while DET == 1
    (details)
    1.1 If DET moves from 1 to 3, go to WaitOnFRE
    2.1 Wait for 1 second for DET to become 3
    2.2 After 1 second of waiting, force the controller to 150MB/s speeds and go to WaitOnDET3
Affected Variables/Registers:
    SCTL, SERR
*/
{
    AHCI_SERIAL_ATA_STATUS  ssts;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

WaitWhileDET1_Start:
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000017);//P_Running_WaitWhileDET1
    }

  //1.1 If DET moves from 1 to 3, go to WaitOnFRE
    ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->SSTS.AsUlong);

    if (ssts.DET == 3) {
        ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE;
        RecordExecutionHistory(ChannelExtension, 0x10a20017);//P_Running_WaitWhileDET1 done
        P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
        return;

    } else {
      //2.2 After 1 second of waiting, force the controller to 150MB/s speeds and go to WaitOnDET3
        if (ChannelExtension->StartState.ChannelStateDET1Count > 100) {
            //A very wise woman once taught me this trick
            //it is possible that the device is not handling speed negotiation very well
            //help it out by allowing only 150MB/s
            ForceGenxSpeed(ChannelExtension, 1);

            ChannelExtension->StartState.ChannelNextStartState = WaitOnDET3;
            RecordExecutionHistory(ChannelExtension, 0x100a0017);//P_Running_WaitWhileDET1 timed out, speed stepping down
            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(10000); //10 milliseconds
                P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess);
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 10000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(10000); //10 milliseconds
                    P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess);
                }
            }
            return;
      //2.1 Wait for DET to become 3 for 1 second
        } else {
            ChannelExtension->StartState.ChannelStateDET1Count++;
            RecordExecutionHistory(ChannelExtension, 0x10600017);//P_Running_WaitWhileDET1 still waiting
            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(10000); //10 milliseconds
                goto WaitWhileDET1_Start;
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 10000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(10000); //10 milliseconds
                    goto WaitWhileDET1_Start;
                }
            }
            return;
        }
    }
}

VOID
P_Running_WaitOnDET3(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    Done monkeying phase.
    From here on out only a DET of 3 will do

Called By:
    P_Running

It assumes:
    Channel is start capable

It performs:
    (overview)
    1 Look for signs of life which are DET == 3
    (details)
    1.1 Wait for 1 second until DET becomes 3
    1.2 After waiting for 1 second, give up on starting the channel
    1.3 If DET==3 we are ready for WaitOnFRE

Affected Variables/Registers:
    none
*/
{
    AHCI_SERIAL_ATA_STATUS  ssts;
    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

WaitOnDET3_Start:
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000018);//P_Running_WaitOnDET3
    }

  //1.2 After waiting for 1 second, give up on starting the channel
    ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->SSTS.AsUlong);

    if (ssts.DET != 3) {

        if (ChannelExtension->StartState.ChannelStateDET3Count > 100) {
            P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
            RecordExecutionHistory(ChannelExtension, 0x10ff0018);//P_Running_WaitOnDET3 timed out
            return;
      //1.1 Wait for 1 second until DET becomes 3
        } else {
            ChannelExtension->StartState.ChannelStateDET3Count++;
            RecordExecutionHistory(ChannelExtension, 0x10080018);//P_Running_WaitOnDET3 still waiting
            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(10000); //10 milliseconds
                goto WaitOnDET3_Start;
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 10000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(10000); //10 milliseconds
                    goto WaitOnDET3_Start;
                }
            }
            return;
        }
  //1.3 If DET==3 we are ready for WaitOnFRE
    } else {
        ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE;
        RecordExecutionHistory(ChannelExtension, 0x100a0018);//P_Running_WaitOnDET3 Success
        P_Running_WaitOnFRE(ChannelExtension, TimerCallbackProcess);
        return;
    }
}

VOID
P_Running_WaitOnFRE(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    Start the Receive buffer phase
    Wait for confirmation is a 'nice to have' but isn't necessary.

Called By:
    P_Running

It assumes:
    DET == 3 or IPM == 02 or 06

It performs:
    (overview)
    1 Set FRE
    2 Wait for 50ms for FR to reflect running status
    3 Move to WaitOnBSYDRQ
    (details)
    1.1 Set FRE
    2.1 Wait for 50ms for FR to reflect running status
    3.1 Move to WaitOnBSYDRQ
Affected Variables/Registers:
    CMD
*/
{
    AHCI_COMMAND cmd;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

WaitOnFRE_Start:
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000019);//P_Running_FRE
    }

    // There is a device connected when we get here. Trace how long the port need to set DET from 0 to 1.
    if (ChannelExtension->StartState.ChannelStateDETCount > 1) {
        DebugPrint((1, "STORAHCI: Channel %u: waited more than %u ms for DET 0 -> 1\n",
                      ChannelExtension->PortNumber,
                      10 * ChannelExtension->StartState.ChannelStateDETCount));
    }

  //1.1 Set FRE
    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong);
    cmd.FRE = 1;
    StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong);

    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong);

    if (cmd.FR == 1) {
      //3.1 Move to WaitOnBSYDRQ
        ChannelExtension->StartState.ChannelNextStartState = WaitOnBSYDRQ;
        RecordExecutionHistory(ChannelExtension, 0x000a0019);//P_Running_WaitOnFRE Success
        P_Running_WaitOnBSYDRQ(ChannelExtension, TimerCallbackProcess);
        return;

    } else {
      //3.1 Move to WaitOnBSYDRQ
        if (ChannelExtension->StartState.ChannelStateFRECount > 5) {
            ChannelExtension->StartState.ChannelNextStartState = WaitOnBSYDRQ;
            RecordExecutionHistory(ChannelExtension, 0x00ff0019);//P_Running_WaitOnFRE timed out
            P_Running_WaitOnBSYDRQ(ChannelExtension, TimerCallbackProcess);
            return;
      //2.1 Wait for 50ms for FR to reflect running status
        } else {
            ChannelExtension->StartState.ChannelStateFRECount++;
            RecordExecutionHistory(ChannelExtension, 0x00080019);//P_Running_WaitOnFRE still waiting
            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(10000); //10 milliseconds
                goto WaitOnFRE_Start;
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 10000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(10000); //10 milliseconds
                    goto WaitOnFRE_Start;
                }
            }
            return;
        }
    }
}

VOID
P_Running_WaitOnBSYDRQ(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*
    Home stretch

Called By:
    P_Running
It assumes:
    DET == 3 or IPM == 02 or 06
It performs:
    (overview)
    1 Enable BSY and DRQ to go to 0
    2 Wait for BSY and DRQ to go to 0
    3 Set ST to 1
    (details)
    1.1 Clear serr.DIAG.X
    2.1 Wait for the rest of the 10 seconds for BSY and DRQ to clear
    2.2 After a total amount of 3 seconds working on the start COMRESET
    2.3 After waiting for the rest of the 4 seconds for BSY and DRQ to clear, give up
    3.1 Set ST to 1
Affected Variables/Registers:

*/
{
    AHCI_COMMAND cmd;
    AHCI_TASK_FILE_DATA tfd;
    AHCI_SERIAL_ATA_ERROR serr;
    USHORT totalstarttime;

    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

WaitOnBSYDRQ_Start:
    RecordExecutionHistory(ChannelExtension, 0x0000001a);//P_Running_WaitOnBSYDRQ
    tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong);

    //1.1 Enable BSY and DRQ to go to 0
    if ( (tfd.STS.BSY) || (tfd.STS.DRQ) ) {
        //When [serr.DIAG.X is] set to one this bit indicates a COMINIT signal was received.  This bit is reflected in the P0IS.PCS bit.
        //to allow the TFD to be updated serr.DIAG.X must be cleared.
        serr.AsUlong = 0;
        serr.DIAG.X = 1;
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SERR.AsUlong, serr.AsUlong);
    }
    //3.1 Set ST to 1
    if ( ( tfd.STS.BSY == 0) && ( tfd.STS.DRQ == 0) ) {
        STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 };
        BOOLEAN needSpinLock;

        if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) {
            //This is timer callback and a direct port start process has been started separately, bail out this one.
            RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));
            return;
        }

        needSpinLock = TimerCallbackProcess || (ChannelExtension->StartState.AtDIRQL == 0);

        if (needSpinLock) {
            AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
        }

        //3.2 Enable Interrupts on the channel (AHCI 1.1 Section 10.1.2 - 7)
        PortClearPendingInterrupt(ChannelExtension);
        Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE);

        //We made it!  Set ST and start the IO we have collected!
        cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong);
        cmd.ST = 1;
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong);

        ChannelExtension->StartState.ChannelNextStartState = StartComplete;
        ChannelExtension->StateFlags.IgnoreHotplugInterrupt = FALSE;

        StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Port Started\n", ChannelExtension->PortNumber);

        //
        // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are
        // all cleared to 0 according to port start state machine implementation. But typically
        // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the
        // total port start time here is still able to reflect the rough port start status.
        //
        ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount +
                                                       ChannelExtension->StartState.ChannelStateDET1Count +
                                                       ChannelExtension->StartState.ChannelStateDET3Count +
                                                       ChannelExtension->StartState.ChannelStateFRECount +
                                                       (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10;

        //Start requests on this Port
        ActivateQueue(ChannelExtension, TRUE);

        ChannelExtension->StartState.DirectStartInProcess = 0;

        RecordExecutionHistory(ChannelExtension, 0x100a001a);//Exit P_Running_WaitOnBSYDRQ Succeeded

        if (needSpinLock) {
            AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
        }

        if (ChannelExtension->StateFlags.NeedQDR) {
            StorPortIssueDpc(ChannelExtension->AdapterExtension, &(ChannelExtension->BusChangeDpc), ChannelExtension, NULL);
            ChannelExtension->StateFlags.NeedQDR = FALSE;
        }

        RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));

        return;

    } else {
        //2.3 After waiting for the remainder of the 60 second maximum Channel Start time for BSY and DRQ to clear, give up
        //Some big HDDs takes close to 20 second to spin up
        ULONG portStartTimeoutIn10MS = (AHCI_PORT_START_TIMEOUT_IN_SECONDS * 100);

        // calculate the total time in unit of 10 ms.
        totalstarttime = ChannelExtension->StartState.ChannelStateDETCount  +
                         ChannelExtension->StartState.ChannelStateDET1Count +
                         ChannelExtension->StartState.ChannelStateDET3Count +
                         ChannelExtension->StartState.ChannelStateFRECount  +
                        (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2);

        if ( totalstarttime > portStartTimeoutIn10MS ) {
            P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
            RecordExecutionHistory(ChannelExtension, 0x00ff001a);//P_Running_WaitOnBSYDRQ timed out
           return;
        //2.2 After a total amount of 1 second working on the start COMRESET
        } else if ( ChannelExtension->StartState.ChannelStateBSYDRQCount == 50 ){
            //Stop FRE,FR in preparation for RESET
            if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){
                //It takes 1/2 second for stop to fail.
                //This is taking way too long and the controller is not responding properly. Abort the start.
                P_Running_StartFailed(ChannelExtension, TimerCallbackProcess);
                RecordExecutionHistory(ChannelExtension, 0x00fc001a);//P_Running_WaitOnBSYDRQ Stop Failed on COMRESET
                return;
            }

            //All set, bring down the hammer
            AhciCOMRESET(ChannelExtension, ChannelExtension->Px);

            //Set the timers for time remaining.  This is a best case scenario and the best that can be offered.
            ChannelExtension->StartState.ChannelStateDETCount = 0;
            ChannelExtension->StartState.ChannelStateDET1Count = 0;
            ChannelExtension->StartState.ChannelStateDET3Count = 0;     // won't exceed 100 * 10ms for 1 second
            ChannelExtension->StartState.ChannelStateFRECount = 0;      // won't exceed 5 * 10ms for 50 ms
            ChannelExtension->StartState.ChannelStateBSYDRQCount = 51;  // won't exceed 3000 * 20ms for 60 seconds minus what DET3 and FRE use.

            //Go back to WaitOnDet3
            ChannelExtension->StartState.ChannelNextStartState = WaitOnDET3;
            RecordExecutionHistory(ChannelExtension, 0x00fd001a);//P_Running_WaitOnBSYDRQ crossing 1 second.  COMRESET done, going to WaitOnDET3
            P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess);
            return;
        //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear
        } else {
            ChannelExtension->StartState.ChannelStateBSYDRQCount++;
            RecordExecutionHistory(ChannelExtension, 0x0008001a);//P_Running_WaitOnBSYDRQ still waiting
            if (IsDumpMode(adapterExtension)) {
                StorPortStallExecution(20000); //20 milliseconds
                goto WaitOnBSYDRQ_Start;
            } else {
                ULONG status;
                status = StorPortRequestTimer(adapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 20000, 0);     //10 milliseconds
                if ((status != STOR_STATUS_SUCCESS) && (status != STOR_STATUS_BUSY)) {
                    StorPortStallExecution(20000); //20 milliseconds
                    goto WaitOnBSYDRQ_Start;
                }
            }
            return;
        }
    }
}

VOID
P_Running_StartFailed(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    )
/*

Called By:
    P_Running_WaitOnDET
    P_Running_WaitOnDET3
    P_Running_WaitOnBSYDRQ

It assumes:
    nothing

It performs:
    (overview)
    1 clean up internal state structures
    2 complete all the commands that showed up while the start channel failed
    (details)
    1.1 update state machines
    1.2 clear out the programmed slots
    2.1 complete all outstanding commands

Affected Variables/Registers:
    none
*/
{
    STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 };
    BOOLEAN needSpinLock;

    ++(ChannelExtension->TotalCountRunningStartFailed);
    //
    // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are
    // all cleared to 0 according to port start state machine implementation. But typically
    // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the
    // total port start time here is still able to reflect the rough port start status.
    //
    ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount +
                                                   ChannelExtension->StartState.ChannelStateDET1Count +
                                                   ChannelExtension->StartState.ChannelStateDET3Count +
                                                   ChannelExtension->StartState.ChannelStateFRECount +
                                                   (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10;

    if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) {
        //This is timer callback and a direct port start process has been started separately, bail out this one.
        RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));
        return;
    }

    needSpinLock = TimerCallbackProcess || (ChannelExtension->StartState.AtDIRQL == 0);

    if (needSpinLock) {
        AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    }

    //1.1 update state machines
    ChannelExtension->StartState.ChannelNextStartState = StartFailed;
    ChannelExtension->StateFlags.IgnoreHotplugInterrupt = FALSE;
    ChannelExtension->StateFlags.D3ColdEnabled = FALSE;     //this flag may be set to "TRUE" for last connected device, clear it when no device connected.

    //1.1.1 release initial commands for previous device.
    if (ChannelExtension->DeviceInitCommands.CommandTaskFile != NULL) {
        StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)ChannelExtension->DeviceInitCommands.CommandTaskFile);
        ChannelExtension->DeviceInitCommands.CommandTaskFile = NULL;
        ChannelExtension->DeviceInitCommands.CommandCount = 0;
        ChannelExtension->DeviceInitCommands.ValidCommandCount = 0;
        ChannelExtension->DeviceInitCommands.CommandToSend = 0;
    }

    if (ChannelExtension->PersistentSettings.Slots > 0) {
        AhciZeroMemory((PCHAR)&ChannelExtension->PersistentSettings, sizeof(PERSISTENT_SETTINGS));
    }

    //1.1.2 clean up cached device information
    AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES));
    AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED));

    //1.2 clear out the programmed slots
    ChannelExtension->SlotManager.CommandsToComplete = GetOccupiedSlots(ChannelExtension);
    ChannelExtension->SlotManager.CommandsIssued = 0;
    ChannelExtension->SlotManager.NCQueueSliceIssued = 0;
    ChannelExtension->SlotManager.NormalQueueSliceIssued = 0;
    ChannelExtension->SlotManager.SingleIoSliceIssued = 0;
    ChannelExtension->SlotManager.NCQueueSlice = 0;
    ChannelExtension->SlotManager.NormalQueueSlice = 0;
    ChannelExtension->SlotManager.SingleIoSlice = 0;
    ChannelExtension->SlotManager.HighPriorityAttribute = 0;

    //1.3 Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7)
    PortClearPendingInterrupt(ChannelExtension);
    Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE);

    //2.1 Call AhciPortFailAllIos to complete all outstanding commands now that ChannelNextStartState is StartFailed
    AhciPortFailAllIos(ChannelExtension, SRB_STATUS_NO_DEVICE, TRUE);

    ChannelExtension->StartState.DirectStartInProcess = 0;

    if (needSpinLock) {
        AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    }

    RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL));

    return;
}

VOID
AhciNonQueuedErrorRecovery(
    PAHCI_CHANNEL_EXTENSION ChannelExtension
  )
{
/*
AHCI 1.1 Section 6.2.2.1
"The flow for system software to recover from an error when non-queued commands are issued is as follows:
    Reads PxCI to see which commands are still outstanding
    Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred
    Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’
    Clears any error bits in PxSERR to enable capturing new errors.
    Clears status bits in PxIS as appropriate
    If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state
    Sets PxCMD.ST to ‘1’ to enable issuing new commands

It assumes:
    Called asynchronously

Called by:
    AhciPortErrorRecovery <-- AhciHwInterrupt


It performs:
    (overview)
    1 Recover from an error
    2 Complete the Failed Command
    3 Complete Succeeded Commands

    (details)
    1.1 Initialize
    1.2 Reads PxCI to see which commands are still outstanding
    1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred
    1.4.1 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’
     1.5 Clears any error bits in PxSERR to enable capturing new errors.
     1.6 Clears status bits in PxIS as appropriate
    1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state
    1.4.3 If a COMRESET was issued, restore Preserved Settings
    1.4.4 Start the channel

    2.1 Complete the command being issued if there was one
    2.2 Restore the unsent programmed commands for careful reprocessing
    2.3 If a request sense SRB was created due to this error

    3.1 If there were commands that are ready to complete, complete them now

Affected Variables/Registers:
    CI, CMD, TFD
    Channel Extension
*/
    BOOLEAN performedCOMRESET;
    ULONG   ci;
    ULONG   localCommandsIssued;
    UCHAR   numberCommandsOutstanding;
    ULONG   failingCommand;

    AHCI_COMMAND        cmd;
    AHCI_TASK_FILE_DATA tfd;
    PSCSI_REQUEST_BLOCK senseSrb;
    PAHCI_SRB_EXTENSION srbExtension;

  //1.1 Initialize variables
    RecordExecutionHistory(ChannelExtension, 0x00000013);//AhciNonQueuedErrorRecovery

    ++(ChannelExtension->TotalCountNonQueuedErrorRecovery);
    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdNonqueuedErrorRecovery,
                                       "AhciNonQueuedErrorRecovery",
                                       0,
                                       NULL,
                                       0
                                       );

    senseSrb = NULL;
    performedCOMRESET = FALSE;

  //1.2 Reads PxCI to see which commands are still outstanding
    ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI);

  //1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred
    cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);

  //1.4 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’
    if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){ //This clears PxCI
        RecordExecutionHistory(ChannelExtension, 0x10160013);//AhciNonQueuedErrorRecovery, Port Stop Failed
        AhciPortReset(ChannelExtension, FALSE);
        return;
    }

  //2.1 Complete the command being issued if there was one

    //Determine how many commands are outstanding
    localCommandsIssued = ChannelExtension->SlotManager.CommandsIssued;
    numberCommandsOutstanding = 0;
    failingCommand = 0;
    while (localCommandsIssued) {
        if (localCommandsIssued & 1) {
            numberCommandsOutstanding++;
        }
        //To keep from having to walk through the localCommandsIssued mask again,
        //if there is only 1 command, failing commands will be 1 greater than that slot after this while loop.
        failingCommand++;
        localCommandsIssued >>= 1;
    }

    //If there is only one command outstanding, then we know which one to complete.
    if (numberCommandsOutstanding == 1) {
        failingCommand--;
        //As long as there is an SRB in that command slot.
        if (ChannelExtension->Slot[failingCommand].Srb == NULL) {
            RecordExecutionHistory(ChannelExtension, 0x10170013);//AhciNonQueuedErrorRecovery, didn't find the expected command from slot.
            AhciPortReset(ChannelExtension, FALSE);
            return;
        }
    //If NonQueuedErrorRecovery should not be done for more than 1 command stop here.
    } else if ( (cmd.ST == 1) && AdapterNoNonQueuedErrorRecovery(ChannelExtension->AdapterExtension) ) {
        RecordExecutionHistory(ChannelExtension, 0x10180013);//AhciNonQueuedErrorRecovery, NCQ_NeverNonQueuedErrorRecovery
        AhciPortReset(ChannelExtension, FALSE);
        return;
    } else {
        //otherwise the failing command is in CCS, as long as there is an SRB in that command slot.
        if( ((ChannelExtension->SlotManager.CommandsIssued & (1 << cmd.CCS)) > 0) &&
            (ChannelExtension->Slot[cmd.CCS].Srb) ) {
            failingCommand = cmd.CCS;
        } else {
            RecordExecutionHistory(ChannelExtension, 0x10190013);//AhciNonQueuedErrorRecovery, command to be failed is not indicated by PxCMD.CCS
            AhciPortReset(ChannelExtension, FALSE);
            return;
        }
    }

    // Keep in mind although Px.CMD.ST was just shut off, we are working with the previous snapshot of ST.
    if (cmd.ST == 1) {

        ULONG storStatus = STOR_STATUS_NOT_IMPLEMENTED;
        STOR_REQUEST_INFO requestInfo = {0};

        srbExtension = GetSrbExtension(ChannelExtension->Slot[failingCommand].Srb);
        // Remove the errant command from the Commands Issued.
        ci &= ~(1 << failingCommand);
        // Move the errant command from Issued list to the 'to complete' list
        ChannelExtension->SlotManager.CommandsIssued &= ~(1 << failingCommand);
        ChannelExtension->SlotManager.NCQueueSliceIssued &= ~(1 << failingCommand);
        ChannelExtension->SlotManager.NormalQueueSliceIssued &= ~(1 << failingCommand);
        ChannelExtension->SlotManager.SingleIoSliceIssued &= ~(1 << failingCommand);
        ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << failingCommand);
        ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand);

        // Fill in the status of the command
        srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar;
        srbExtension->AtaError = ChannelExtension->TaskFileData.ERR;

        // Check whether the failed command is paging IO, if so, return busy status to indicate storport to retry it.
        storStatus = StorPortGetRequestInfo(ChannelExtension, (PSCSI_REQUEST_BLOCK)ChannelExtension->Slot[failingCommand].Srb, &requestInfo);
        if ((storStatus == STOR_STATUS_SUCCESS) && 
            (requestInfo.Flags & REQUEST_INFO_PAGING_IO_FLAG)) {
            ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_BUSY;
        } else {
            ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_ERROR;
        }

        // Handle Request Sense if needed
        if ( NeedRequestSense(ChannelExtension->Slot[failingCommand].Srb) ) {
            // This is for ATAPI device only
            senseSrb = BuildRequestSenseSrb(ChannelExtension, ChannelExtension->Slot[failingCommand].Srb); // it will be programmed to slot later in this routine.
            if (senseSrb != NULL) {
                //Make the slot available again now, its slot content will not be cleaned up later.
                ChannelExtension->Slot[failingCommand].CmdHeader = NULL;
                ChannelExtension->Slot[failingCommand].CommandHistoryIndex = 0;
                ChannelExtension->Slot[failingCommand].Srb = NULL;
                //Do not complete this command normally, it will be completed when it's request sense is completed
                ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << failingCommand);
                RecordExecutionHistory(ChannelExtension, 0x10730013);//NonNCQ Error Received RequestSense
            }
        }

        if (senseSrb == NULL) {
            // if not need Request Sense or cannot get Request Sense Srb, Complete the command manually
            ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE); //&ChannelExtension->Slot[cmd.CCS] );
            RecordExecutionHistory(ChannelExtension, 0x10630013);//NonNCQ Error Completed failed command
        }
    }

    //1.5 Clears any error bits in PxSERR to enable capturing new errors.
        //Handled in the Interrupt routine
    //1.6 Clears status bits in PxIS as appropriate
        //Handled in the Interrupt routine

    //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state
    tfd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong);

    if(tfd.STS.BSY || tfd.STS.DRQ) {
        AhciCOMRESET(ChannelExtension, ChannelExtension->Px);
        performedCOMRESET = TRUE;
    }

    //2.2 Restore the unsent programmed commands for careful reprocessing
    ChannelExtension->SlotManager.NormalQueueSlice |= ci;   //put the commands that didn't get a chance to finish back into the normal queue
    ChannelExtension->SlotManager.CommandsIssued &= ~ci;    //Remove the unfinished commands from the 'issued' list
    ChannelExtension->SlotManager.NCQueueSliceIssued &= ~ci;
    ChannelExtension->SlotManager.NormalQueueSliceIssued &= ~ci;
    ChannelExtension->SlotManager.SingleIoSliceIssued &= ~ci;

    //2.3 If there were commands that are ready to complete, complete them
    if (ChannelExtension->SlotManager.CommandsToComplete) {
        AhciCompleteIssuedSRBs(ChannelExtension, SRB_STATUS_SUCCESS, TRUE); //complete the successful commands to start the RS SRB.
    }

    //3.1 If a request sense SRB was created due to this error
    if (senseSrb != NULL) {
        ChannelExtension->StateFlags.QueuePaused = FALSE;
        AhciProcessIo(ChannelExtension, (PSTORAGE_REQUEST_BLOCK)senseSrb, TRUE);  //program Sense Srb into Slot
    }

    //3.2 If a COMRESET was issued, restore Preserved Settings
    if (performedCOMRESET == TRUE) {
        RestorePreservedSettings(ChannelExtension, TRUE);
    }

    //3.3 Start the channel, IO will be resumed after port start complete.
    P_Running_StartAttempt(ChannelExtension, TRUE);

    RecordExecutionHistory(ChannelExtension, 0x10000013);//Exit AhciNonQueuedErrorRecovery
}

VOID
NcqErrorRecoveryCompletion (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
    )
/*
    This completion routine continue handling NCQ Error Recovery after NCQ Error Log is read.

    It finds out the failing command and completes it back.
    Other commands are resent to adapter in miniport level.

*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);
    ULONG issuedCommands = (ULONG)(ULONG_PTR)srbExtension->CompletionContext;
    STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0};
    BOOLEAN fallBacktoReset = FALSE;

    RecordExecutionHistory(ChannelExtension, 0x0000001b);   //Enter NcqErrorRecoveryCompletion

    ++(ChannelExtension->TotalCountNCQErrorRecoveryComplete);
    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdNCQErrorRecoveryComplete,
                                       "NcqErrorRecoveryCompletion",
                                       0,
                                       NULL,
                                       0
                                       );

    //
    // In case of reset happened when Read Log command was executing, all requests have been returned back to port driver.
    // There is no more recovery work this routine needs to do.
    //
    if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) {
        StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read - reset\n", ChannelExtension->PortNumber);
        ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0;
        ChannelExtension->DeviceExtension->IoRecord.NcqReadLogErrorCount++;

        RecordExecutionHistory(ChannelExtension, 0x1001001b);   //NcqErrorRecoveryCompletion RESET happened on read NCQ Error Log command.

        return;
    }

    if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
        //
        // Read NCQ Error Log succeeded. Only complete the failing command.
        // Other commands will be put back into NCQ slices and will be issued again to adapter.
        //
        PGP_LOG_NCQ_COMMAND_ERROR ncqErrorLog = (PGP_LOG_NCQ_COMMAND_ERROR)ChannelExtension->DeviceExtension->ReadLogExtPageData;
        UCHAR failingCommand;
        PSTORAGE_REQUEST_BLOCK failingSrb = NULL;
        PAHCI_SRB_EXTENSION failingSrbExtension = NULL;

        NT_ASSERT(ncqErrorLog->NonQueuedCmd == 0);
        NT_ASSERT(ncqErrorLog->NcqTag != 0);

        failingCommand = ncqErrorLog->NcqTag;

        //
        // The failing command should be one of the issued commands.
        // Otherwise, it's a device issue and we fall back to use reset process.
        //
        if (((1 << failingCommand) & issuedCommands) != 0) {

            failingSrb = ChannelExtension->Slot[failingCommand].Srb;

            //
            // If the surprise remove happens just after the NCQ error handling, then
            // there is chance that the failingSrb is NULL here.
            //
            if (failingSrb != NULL) {
                failingSrbExtension = GetSrbExtension(failingSrb);

                //
                // Record error and status
                // 
                failingSrbExtension->AtaStatus = ncqErrorLog->Status;
                failingSrbExtension->AtaError = ncqErrorLog->Error;

                if( IsReturnResults(failingSrbExtension->Flags) ) {
                    SetReturnRegisterValues(ChannelExtension, failingSrb, NULL);
                }

                //
                // Get Sense Data from log, or from translation.
                //
                if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesSupported.NCQAutosense == 1) &&
                    (ncqErrorLog->SenseKey != SCSI_SENSE_NO_SENSE)) {

                    StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. Sense Data returned: %02X/%02X/%02X\n", 
                                           ChannelExtension->PortNumber, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ);

                    AhciSetSenseData(failingSrb, SRB_STATUS_ERROR, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ);
                } else {
                    //
                    // Set SrbStatus indicates it's an error condition.
                    // Sense data will be translated from Error register in ReleaseSlottedCommand()
                    //
                    StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. No Sense Data returned\n", ChannelExtension->PortNumber);

                    failingSrb->SrbStatus = SRB_STATUS_ERROR;
                    AtaMapError(ChannelExtension, failingSrb, ChannelExtension->Slot[failingCommand].StateFlags.FUA);
                }

                //
                // Inform ReleaseSlottedCommand() function that sense data has been set.
                //
                SETMASK(failingSrbExtension->Flags, ATA_FLAGS_SENSEDATA_SET);

                //
                // Acquire spinlock before touching Slots.
                //
                AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);

                //
                // Remove the failing command from NCQ Slice.
                // Put back other previous outstanding commands.
                //
                ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << failingCommand);

                //
                // Clear the NCQ Error Recovery state flag, so that queue will be resumed in ReleaseSlottedCommand().
                //
                ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0;

                //
                // Complete the failing command.
                //
                ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand);
                ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE);

                ActivateQueue(ChannelExtension, TRUE);

                AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);

            } else {
                //
                // If failingSrb is NULL, clear the NCQ Error Recovery state flag.
                //
                ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0;

                StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully, but the failing srb is NULL!\n", ChannelExtension->PortNumber);

                //
                // Capture the scenarios that failing srb is NULL, need investigate cases other than surprise remove.
                //
                NT_ASSERT(failingSrb != NULL);
            }

        } else {
            StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Tag doesn't belong to issued commands.\n", ChannelExtension->PortNumber);
            NT_ASSERT(((1 << failingCommand) & issuedCommands) != 0);
            fallBacktoReset = TRUE;

            RecordExecutionHistory(ChannelExtension, 0x1002001b);   //NcqErrorRecoveryCompletion, NCQ Tag doesn't belong to issued commands.
        }

    } else if ((Srb->SrbStatus == SRB_STATUS_BUSY) &&
               (srbExtension->QueueTag == 0xFF)) {
        //
        // Read NCQ Error Log cannot be issued, as all slots are occupied.
        //
        fallBacktoReset = TRUE;

        RecordExecutionHistory(ChannelExtension, 0x1004001b);   //NcqErrorRecoveryCompletion, Read NCQ Error Log cannot be issued, as all slots are occupied..

    } else {
        //
        // Read NCQ Error Log failed. Completes all NCQ commands back to upper layer.
        //
        StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read - Failed\n", ChannelExtension->PortNumber);
        fallBacktoReset = TRUE;
        ChannelExtension->DeviceExtension->IoRecord.NcqReadLogErrorCount++;

        RecordExecutionHistory(ChannelExtension, 0x1003001b);   //NcqErrorRecoveryCompletion, read NCQ Error Log failed.
    }

    if (fallBacktoReset) {
        AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);

        ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0;

        //
        // Recover SlotManager context.
        //
        ChannelExtension->SlotManager.NCQueueSlice &= ~issuedCommands;
        ChannelExtension->SlotManager.CommandsIssued |= issuedCommands;
        ChannelExtension->SlotManager.NCQueueSliceIssued |= issuedCommands;
        AhciPortReset(ChannelExtension, FALSE);

        AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    }

    if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x1000001b);   //Exit NcqErrorRecoveryCompletion.
    }

    return;
}

VOID
AhciNcqErrorRecovery(
    PAHCI_CHANNEL_EXTENSION ChannelExtension
  )
/*
AHCI 1.1 Section 6.2.2.2 Native Command Queuing Error Recovery
    The flow for system software to recover from an error when native command queuing commands are
    issued is as follows:

    • Reads PxSACT to see which commands have not yet completed
    • Clears PxCMD.ST to ‘0’ to reset the PxCI and PxSACT registers, waits for PxCMD.CR to clear to ‘0’
    • Clears any error bits in PxSERR to enable capturing new errors.
    • Clears status bits in PxIS as appropriate
    • If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state
    • Sets PxCMD.ST to ‘1’ to enable issuing new commands
    • Issue READ LOG EXT to determine the cause of the error condition if software did not have to perform a reset (COMRESET or software reset) as part of the error recovery

    Software then either completes commands that did not finish with error to higher level software, or reissues
    them to the device.

    To be able to finish this process, StorAHCI needs to hold all other requests until READ LOG EXT command is completed.
    This is achieved by utilizing "ChannelExtension->StateFlags.QueuePaused" flag.
        1. READ LOG EXT command will be issued using miniport reserved slot 0, so it will be the next command being executed (by StorAHCI slot management design).
        2. This command is a SingleSlice command. StorAHCI will set "QueuePaused" flag to be TRUE.
        3. "QueuePaused" flag will be set to FALSE in NcqErrorRecoveryCompletion after process is completed.

It assumes:
    Called asynchronously

Called by:
    AhciPortErrorRecovery <-- AhciHwInterrupt

*/
{
    BOOLEAN needCOMRESET = FALSE;
    ULONG   allocated;

#ifdef DBG
    ULONG   ci;
    ULONG   sact;
#endif

    //
    // Record history for NCQ error recovery
    //
    RecordExecutionHistory(ChannelExtension, 0x00000014);   //AhciNcqErrorRecovery

    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdNCQErrorRecovery,
                                       "AhciNcqErrorRecovery Enter",
                                       0,
                                       NULL,
                                       0
                                       );

    //
    // Reads PxSACT and PxCI to see which commands are still outstanding
    //
#ifdef DBG
    ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI);
    sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT);
#endif

    //
    // Clears PxCMD.ST to ‘0’, waits for PxCMD.CR to clear to ‘0’
    // If it doesn't succeed, issue COMRESET to recover.
    //
    if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) {
        needCOMRESET = TRUE;
    }

    //
    // Clears any error bits in PxSERR to enable capturing new errors.
    // It's handled in the Interrupt routine
    //

    //
    // Clears status bits in PxIS as appropriate
    // It's handled in the Interrupt routine
    //

    if (needCOMRESET == FALSE) {
        //
        // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state
        // ChannelExtension->TaskFileData has been read from register in ISR.
        //
        if ((ChannelExtension->TaskFileData.STS.BSY != 0) || (ChannelExtension->TaskFileData.STS.DRQ != 0)) {
            needCOMRESET = TRUE;
        }
    }

    if (needCOMRESET == TRUE) {
        //
        // COMRESET is issued, bail out the function.
        //
        RecordExecutionHistory(ChannelExtension, 0x10160014);       //AhciNcqErrorRecovery, RESET is being issued.
        AhciPortReset(ChannelExtension, FALSE);
        return;
    }

    //
    // See if local SRB is in use.  If yes, fall back to reset the port.
    //
    allocated = GetOccupiedSlots(ChannelExtension);
    if ((allocated & (1 << 0)) > 0) {
        //
        // COMRESET is issued, bail out the function.
        //
        RecordExecutionHistory(ChannelExtension, 0x10170014);       //AhciNcqErrorRecovery, RESET is being issued because slot 0 is in use.
        AhciPortReset(ChannelExtension, FALSE);
        return;
    }

    StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: Send READ LOG EXT to read NCQ Error Log. Issued Requests:: %08X\n",
                       ChannelExtension->PortNumber, ChannelExtension->SlotManager.CommandsIssued);

    //
    // Only one NCQ Error Recovery should be happening at a time.
    // If we get another error before we finished processing the previous one
    // then this is likely a device issue so do reset.
    //
    NT_ASSERT(ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 0);
    if (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess) {
        RecordExecutionHistory(ChannelExtension, 0x10170015);       //AhciNcqErrorRecovery, RESET is being issued because NCQ error recovery is already in progress.
        AhciPortReset(ChannelExtension, FALSE);
        return;
    }    

    //
    // Set the state flag indicating it's in NCQ Error Recovery process.
    // This doesn't get cleared until NcqErrorRecoverCompletion is called,
    // which happens after the local Sense.Srb is removed from the completion
    // queue. (It also gets cleared on a port reset.)
    //
    ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 1;

    //
    // When COMRESET is not issued, adapter still has information about error.
    // Issue READ LOG EXT command to read NCQ Error Log (10h). From the log, we can
    // find which command fails and the sense data if NcqAutoSense is supported.
    //

    //
    // For ATA device, use Sense.Srb to retrieve NCQ Log.
    // NOTE: When device is ATAPI, Sense.Srb is used to retrieve Sense Data.
    //
    NT_ASSERT(IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters));

    AhciZeroMemory((PCHAR)&ChannelExtension->Sense.Srb, sizeof(SCSI_REQUEST_BLOCK));
    AhciZeroMemory((PCHAR)ChannelExtension->Sense.SrbExtension, sizeof(AHCI_SRB_EXTENSION));

    ChannelExtension->Sense.Srb.SrbExtension = (PVOID)ChannelExtension->Sense.SrbExtension;
    ChannelExtension->Sense.Srb.OriginalRequest = &ChannelExtension->Sense.Srb;

    NT_ASSERT(((ci | sact) & ~ChannelExtension->SlotManager.CommandsIssued) == 0);
    ChannelExtension->Sense.SrbExtension->CompletionContext = (PVOID)ChannelExtension->SlotManager.CommandsIssued;

    //
    // Put the issued commands back to NCQ Slices.
    //
    ChannelExtension->SlotManager.NCQueueSlice |= ChannelExtension->SlotManager.CommandsIssued;
    ChannelExtension->SlotManager.CommandsIssued = 0;
    ChannelExtension->SlotManager.NCQueueSliceIssued = 0;

    IssueReadLogExtCommand( ChannelExtension,
                            (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb,
                            IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS,
                            0,
                            1,
                            0,      // feature field
                            &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress,
                            (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData,
                            NcqErrorRecoveryCompletion
                            );

    ChannelExtension->StateFlags.QueuePaused = FALSE;
    AhciProcessIo(ChannelExtension, (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb, TRUE);  //program Sense Srb into Slot

    //
    // Start the channel, IO will be resumed after port start complete.
    //
    P_Running_StartAttempt(ChannelExtension, TRUE);

    RecordExecutionHistory(ChannelExtension, 0x10000014);   //Exit AhciNcqErrorRecovery

    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdNCQErrorRecovery,
                                       "AhciNcqErrorRecovery Exit",
                                       0,
                                       NULL,
                                       0
                                       );

    return;
}

VOID
AhciPortErrorRecovery(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
  )
/*
    Error recovery routine for Port
    AHCI: 6.2.2.1 Non-Queued Error Recovery
          6.2.2.2 Native Command Queuing Error Recovery
          6.2.2.3 Recovery of Unsolicited COMINIT

It assumes:
    nothing

Called by:
    AhciHwInterrupt

Return Values:
    None
*/
{
    ++(ChannelExtension->TotalCountPortErrorRecovery);
    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdPortErrorRecovery,
                                       "AhciPortErrorRecovery",
                                       0,
                                       NULL,
                                       0
                                       );

    // Log the StateFlags
    StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension,
                             (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                             StorportEtwEventOperational,
                             AhciEtwEventHandleInterrupt,
                             L"AhciPortErrorRecovery",
                             STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE,
                             StorportEtwLevelError,
                             StorportEtwEventOpcodeInfo,
                             NULL,
                             L"StateFlags",
                             *(ULONGLONG *)&(ChannelExtension->StateFlags),
                             L"PortErrRecovery",
                             ChannelExtension->TotalCountPortErrorRecovery,
                             L"StartState",
                             *(ULONGLONG *)&(ChannelExtension->StartState),
                             L"PortReset",
                             ChannelExtension->TotalCountPortReset,
                             L"RunningStrtFail",
                             ChannelExtension->TotalCountRunningStartFailed,
                             L"NonQ'dErrRecov",
                             ChannelExtension->TotalCountNonQueuedErrorRecovery,
                             L"NCQError|NCQErrorComplete",
                             (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) |
                             ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)),
                             L"BusChange",
                             ChannelExtension->TotalCountBusChange);

    if (ChannelExtension->StateFlags.CallAhciReportBusChange == 1) {
        // Handle AHCI 6.2.2.3 Recovery of Unsolicited COMINIT and hot plug
        // AhciPortBusChangeDpcRoutine() will issue RESET, ignore other error recovery marks.
        ChannelExtension->StateFlags.CallAhciReportBusChange = 0;
        ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0;
        ChannelExtension->StateFlags.CallAhciReset = 0;
        ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0;

        ++(ChannelExtension->TotalCountBusChange);
        if (ChannelExtension->TotalCountBusChange >= AHCI_BUS_CHANGE_COUNT_WARNING_THRESHOLD) {
            AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyBusChange, 0, TRUE);
        }

        if (!IsDumpMode(ChannelExtension->AdapterExtension)) {
            StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeDpc, ChannelExtension, NULL);
        } else {
            AhciPortBusChangeDpcRoutine(&ChannelExtension->BusChangeDpc, ChannelExtension->AdapterExtension, ChannelExtension, NULL);
        }
    }

    if (ChannelExtension->StateFlags.CallAhciNcqErrorRecovery == 1) {
        //
        // Handle NCQ Errors by not issuing COMRESET if possible;
        // and READ LOG to understand which command fails.
        //
        ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0;
        ChannelExtension->StateFlags.CallAhciReset = 0;
        ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0;

        ++(ChannelExtension->TotalCountNCQError);
        if (ChannelExtension->TotalCountNCQError >= AHCI_NCQ_ERROR_COUNT_WARNING_THRESHOLD) {
            AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNCQError, 0, TRUE);
        }

        if (SupportsEnhancedNcqErrorRecovery(ChannelExtension)) {
            AhciNcqErrorRecovery(ChannelExtension);
        } else {
            //
            // In case Enhanced NCQ Error Recovery is not supported, use the legacy method to reset device.
            //
            ChannelExtension->StateFlags.CallAhciReset = 1;
        }
    }

    if (ChannelExtension->StateFlags.CallAhciReset == 1) {
        // Handle AHCI 6.2.2.2 Native Command Queuing Error Recovery and other events require RESET.
        ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0;
        ChannelExtension->StateFlags.CallAhciReset = 0;

        AhciPortReset(ChannelExtension, FALSE);
    }

    if (ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery == 1) {
        // Handle AHCI 6.2.2.1 Non-Queued Error Recovery
        ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0;

        if (ChannelExtension->TotalCountNonQueuedErrorRecovery >= AHCI_NON_QUEUED_ERROR_COUNT_WARNING_THRESHOLD) {
            AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNonQueuedError, 0, TRUE);
        }

        AhciNonQueuedErrorRecovery(ChannelExtension);
    }

    return;
}

BOOLEAN
AhciPortReset(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN CompleteAllRequests
    )
/*++
    AhciPortReset can be called even if the miniport driver is not ready for another request.
    The miniport driver should complete all pending requests and must reset the given channel.

It assumes:
    DIRQL

Called by:

It performs:
    COMRESET along with completing commands to be retried and restoring settings that may be be persistent across a COMRESET

    (overview)
    1 Initialize
    2 Perform COMReset
    3 Complete all outstanding commands
    4 Restore device configuration

Affected Variables/Registers:
    SCTL, CI, SACT
    Channel Extension

Return Values:
    AhciPortReset return TRUE if the reset operation succeeded.
    If the reset failed the routine must return FALSE.
--*/
{
    ULONG commandsToCompleteCount = 0;
    BOOLEAN isPortStartInProgress = FALSE;
    BOOLEAN isCompletionDPCQueued = FALSE;
    BOOLEAN isCompletionDPCInterrupted = FALSE;

    //1.1 Initialize Variables
    ++(ChannelExtension->TotalCountPortReset);

    if ((CompleteAllRequests) &&
        (ChannelExtension->StartState.ChannelNextStartState != StartComplete)) {

        NT_ASSERT(FALSE);

        isPortStartInProgress = TRUE;
        RecordExecutionHistory(ChannelExtension, 0x10040050); //AhciPortReset, Port Start still in progress, device probably hang

    } else {
        RecordExecutionHistory(ChannelExtension, 0x00000050); //AhciPortReset
    }

    AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdPortReset,
                                       (isPortStartInProgress ? "PortStartTakeTooLong" : "AhciPortReset"),
                                       (isPortStartInProgress ? AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING : 0),
                                       NULL,
                                       0
                                       );

    if (ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart > ChannelExtension->LastTimeStampDpcStart.QuadPart) {
        isCompletionDPCQueued = TRUE;
    }

    if (ChannelExtension->LastTimeStampDpcStart.QuadPart > ChannelExtension->LastTimeStampDpcCompletion.QuadPart) {
        isCompletionDPCInterrupted = TRUE;
    }

    if (isCompletionDPCQueued || isCompletionDPCInterrupted) {

        LARGE_INTEGER currentTimeStamp;
        StorPortQuerySystemTime(&currentTimeStamp);
        ULONGLONG duration = 0;

        NT_ASSERT(FALSE);

        if (isCompletionDPCQueued && isCompletionDPCInterrupted) {
            if (ChannelExtension->LastTimeStampDpcStart.QuadPart < ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart) {
                duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart);
            } else {
                duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart);
            }
        } else if (isCompletionDPCQueued) {
            duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart);
        } else if (isCompletionDPCInterrupted) {
            duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart);
        }

        RecordExecutionHistory(ChannelExtension, 0x10050050); //AhciPortReset, DPC queued or interrupted

        AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                           (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                           AhciTelemetryEventIdPortReset,
                                           "DPC queued or interrupted",
                                           AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING,
                                           "Duration(in 100ns)",
                                           duration
                                           );
    }

    StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension,
                             (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                             StorportEtwEventOperational,
                             AhciEtwEventPortReset,
                             L"Port reset",
                             STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE,
                             StorportEtwLevelInformational,
                             StorportEtwEventOpcodeInfo,
                             NULL,
                             L"PortReset",
                             ChannelExtension->TotalCountPortReset,
                             L"RunningStrtFail",
                             ChannelExtension->TotalCountRunningStartFailed,
                             L"PortErrRecovery",
                             ChannelExtension->TotalCountPortErrorRecovery,
                             L"NonQ'dErrRecov",
                             ChannelExtension->TotalCountNonQueuedErrorRecovery,
                             L"NCQErrRecovery",
                             ChannelExtension->TotalCountNCQError,
                             L"NCQErrRecovCmpl",
                             ChannelExtension->TotalCountNCQErrorRecoveryComplete,
                             L"SurpriseRemove",
                             ChannelExtension->TotalCountSurpriseRemove,
                             L"StateFlags",
                             *(ULONGLONG *)&(ChannelExtension->StateFlags));

    //2.1 Stop the channel
    P_NotRunning(ChannelExtension, ChannelExtension->Px);

    //2.2 Perform the COMRESET
    // AHCI 10.1.2 - 3: If PxCMD.CR or PxCMD.FR do not clear to '0' correctly, then software may attempt a port reset or a full HBA reset to recover.
    AhciCOMRESET(ChannelExtension, ChannelExtension->Px);

    //
    // Clear NCQ Error Recovery state.
    //
    if (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 1) {
        ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0;
        ChannelExtension->StateFlags.QueuePaused = 0;
    }

    if ((CompleteAllRequests) &&
        (ChannelExtension->StateFlags.QueuePaused == 1) &&
        (ChannelExtension->SlotManager.CommandsIssued == 0)) {

        //
        // This should never happen. Resetting state.
        //

        NT_ASSERT(FALSE);

        ChannelExtension->StateFlags.QueuePaused = 0;

        RecordExecutionHistory(ChannelExtension, 0x10010050); //AhciPortReset, Unpausing queue
        AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                           (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                           AhciTelemetryEventIdPortReset,
                                           "Unpausing queue",
                                           AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING,
                                           NULL,
                                           0
                                           );
    }

    //2.3 If either Init Command or PreservedSettings Command is being processed, reset the command index to start from the first one again.
    //    The process will be continued by the Srb's completion routine.
    if (ChannelExtension->StateFlags.ReservedSlotInUse == 1) {
        ChannelExtension->DeviceInitCommands.CommandToSend = 0;
        ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots;
    }

    if ((CompleteAllRequests) &&
        (ChannelExtension->StateFlags.ReservedSlotInUse == 1) &&
        (ChannelExtension->SlotManager.SingleIoSliceIssued == 0) &&
        ((ChannelExtension->SlotManager.SingleIoSlice & 0x1) == 0)) {

        NT_ASSERT(FALSE);

        //
        // This could happen if init/preserved setting command completed by device, but the completion DPC is queued(or interrupted) for long time.
        // If there is other scenario, it is needed to further triage.
        //
        // Note: Force clear ReservedSlotInUse flag only if corresponding registry key is set.
        //       Otherwise, there maybe add queue issue. E.g. Buggy device repeatedly generate bus change interrupt,
        //       which could result in add queue bugcheck eventually.
        //

        if (!(isCompletionDPCQueued || isCompletionDPCInterrupted)) {
            RecordExecutionHistory(ChannelExtension, 0x10020050); //AhciPortReset, ReservedSlotInUse flag maybe stuck
            //
            // There maybe possibility that init/restore preserved settings command stuck in device and never complete.
            //
            AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureDeviceStuck, 0, TRUE);
        } else {
            RecordExecutionHistory(ChannelExtension, 0x10060050); //AhciPortReset, ReservedSlotInUse flag outstanding
        }

    }

    if (((ChannelExtension->SlotManager.SingleIoSliceIssued & 0x1) != 0) &&
        (ChannelExtension->Slot[0].Srb != NULL) &&
        ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == ChannelExtension->Slot[0].Srb)) {

        if ((ChannelExtension->Local.SrbExtension != NULL) &&
            ((ChannelExtension->Local.SrbExtension->CompletionRoutine == IssuePreservedSettingCommands) ||
             (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands))) {

            BOOLEAN isInitCommand = (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands) ? (TRUE) : (FALSE);

            NT_ASSERT(FALSE);

            RecordExecutionHistory(ChannelExtension, 0x10030050); //AhciPortReset, Init/Preserved Setting command timeout
            AhciTelemetryLogResetErrorRecovery(ChannelExtension,
                                               (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                               AhciTelemetryEventIdPortReset,
                                               "Slot 0 command timeout",
                                               AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING,
                                               (isInitCommand ? "Init Command" : "Preserved Setting Command"),
                                               0
                                               );
        }
    }

    //3.1 Complete all issued commands
    ChannelExtension->SlotManager.CommandsToComplete = ChannelExtension->SlotManager.CommandsIssued;
    ChannelExtension->SlotManager.CommandsIssued = 0;
    ChannelExtension->SlotManager.NCQueueSliceIssued = 0;
    ChannelExtension->SlotManager.NormalQueueSliceIssued = 0;
    ChannelExtension->SlotManager.SingleIoSliceIssued = 0;
    ChannelExtension->SlotManager.HighPriorityAttribute &= ~ChannelExtension->SlotManager.CommandsToComplete;

    commandsToCompleteCount = NumberOfSetBits(ChannelExtension->SlotManager.CommandsToComplete);

    if (commandsToCompleteCount > 0) {
        UCHAR completeStatus = SRB_STATUS_BUS_RESET;

        if (commandsToCompleteCount == 1) {
            ULONG   i;

            for (i = 0; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) {
                if (((ChannelExtension->SlotManager.CommandsToComplete & (1 << i)) != 0) &&
                    (ChannelExtension->Slot[i].Srb != NULL) &&
                    (!IsMiniportInternalSrb(ChannelExtension, ChannelExtension->Slot[i].Srb))) {
                    //
                    // we know the error is for this command as it's the only one programmed to adapter,
                    // set status to be SRB_STATUS_ERROR for external Srb to make sure the function - AtaMapError() assigns the real error to Srb
                    // It's preferred to return SRB_STATUS_BUS_RESET to internal request to make it aware of reset happened.
                    //
                    completeStatus = SRB_STATUS_ERROR;
                }
            }
        }

        AhciCompleteIssuedSRBs(ChannelExtension, completeStatus, TRUE); //AhciPortReset is under Interrupt spinlock
    }

    //3.2 Complete all other commands miniport own for this device, when it's necessary or when the Reset is requested by Storport
    if (CompleteAllRequests) {
        AhciPortFailAllIos(ChannelExtension, SRB_STATUS_BUS_RESET, TRUE);
    }

    //4.1 Restore device configuration
    if (ChannelExtension->StateFlags.ReservedSlotInUse == 0) {
        RestorePreservedSettings(ChannelExtension, TRUE);
    }

    //2.3 Start the channel
    P_Running_StartAttempt(ChannelExtension, TRUE); //AhciPortReset is under Interrupt spinlock

    // record that the channel is reset.
    RecordExecutionHistory(ChannelExtension, 0x10000050);//Exit AhciPortReset

    return TRUE;
}


#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4152)
#pragma warning(default:4214)
#pragma warning(default:4201)
#endif


<|start_filename|>wmi/wmisamp/wmisamp.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
    EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

Module Name:

    WmiSamp.h

Abstract:

    --

Environment:

    Kernel mode

--*/

#include <ntddk.h>
#include <wdf.h>

#include <initguid.h>   // required for GUID definitions
#include <wdmguid.h>    // required for WMILIB_CONTEXT
#include <wmistr.h>
#include <wmilib.h>
#include "WmiData.h"


//
// A macro for Pointer addition.
//
#define Add2Ptr(P,I)    ((PVOID)((PUCHAR)(P) + (I)))


//
// Define DebugPrint for sending messages to the debugger in checked builds.
//
#if DBG
#define DebugPrint      KdPrint
#else
#define DebugPrint
#endif

#define POOL_ZERO_DOWN_LEVEL_SUPPORT

typedef struct _WMI_SAMPLE_INSTANCE_CONFIG {

    GUID Guid;
    ULONG MinSize;
    PFN_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance;
    PFN_WDF_WMI_INSTANCE_SET_INSTANCE EvtWmiInstanceSetInstance;
    PFN_WDF_WMI_INSTANCE_SET_ITEM EvtWmiInstanceSetItem;
    PFN_WDF_WMI_INSTANCE_EXECUTE_METHOD EvtWmiInstanceExecuteMethod;

} WMI_SAMPLE_INSTANCE_CONFIG, *PWMI_SAMPLE_INSTANCE_CONFIG;


#define EC1_COUNT       4
#define EC2_COUNT       4

//
// Data storage for WMI data blocks.
//
typedef struct _WMI_SAMPLE_DEVICE_DATA {

    ULONG Ec1Count;
    ULONG Ec1Length[EC1_COUNT];
    ULONG Ec1ActualLength[EC1_COUNT];
    PEC1 Ec1[EC1_COUNT];
    WDFSPINLOCK Ec1Lock;

    ULONG Ec2Count;
    ULONG Ec2Length[EC2_COUNT];
    ULONG Ec2ActualLength[EC2_COUNT];
    PEC2 Ec2[EC2_COUNT];
    WDFSPINLOCK Ec2Lock;

    WDFWMIINSTANCE DynamicInstance;
    ULONG CreateCount;

} WMI_SAMPLE_DEVICE_DATA, *PWMI_SAMPLE_DEVICE_DATA;


//
// Specify an accessor method for the WMI_SAMPLE_DEVICE_DATA structure.
//
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WMI_SAMPLE_DEVICE_DATA, GetWmiSampleDeviceData)


DRIVER_INITIALIZE DriverEntry;

EVT_WDF_DRIVER_DEVICE_ADD WmiSampEvtDeviceAdd;
EVT_WDF_DEVICE_CONTEXT_DESTROY WmiSampDeviceEvtDestroyCallback;

NTSTATUS
WmiSampWmiRegistration(
    _In_ WDFDEVICE Device
    );

NTSTATUS
WmiSampDynamicWmiRegistration(
    _In_ WDFDEVICE Device
    );

_Success_(return > 0)
ULONG
WmiSampGetEc1(
    _In_    PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _Out_ PVOID Buffer,
    _In_    ULONG Index
    );

VOID
WmiSampSetEc1(
    _In_ PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _In_ PVOID Buffer,
    _In_ ULONG Length,
    _In_ ULONG Index
    );

_Success_(return > 0)
ULONG
WmiSampGetEc2(
    _In_    PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _Out_ PVOID Buffer,
    _In_    ULONG Index
    );

VOID
WmiSampSetEc2(
    _In_ PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _In_ PVOID Buffer,
    _In_ ULONG Length,
    _In_ ULONG Index
    );



<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APOOSUpgradeTest.cpp<|end_filename|>
#include <stdafx.h>

#include <initguid.h>
#include "propkey.h"
#include <AudioClient.h>
#include <SpatialAudioClient.h>
#include <functiondiscoverykeys.h>
#include <DevPKey.h>
#include <AudioEngineBaseAPOP.h>
#include "TestMediaType.h"
#include <audioenginebaseapo.h>
#include <MMDeviceAPI.h>
#include <MMDeviceAPIP.h>

#include <WexTestClass.h>

#include <list>
#include <memory>
#include <ctime>
#include <thread>
#include <future>
#include <mutex>
#include <functional>
#include <atlbase.h>

#include "APOOSUpgradeTest.h"
#include "APOStressTest.h"
#include "sinewave.h"

using namespace std;
using namespace wil;
using namespace WEX::Logging;
using namespace WEX::Common;

#define IF_FAILED_RETURN(hr) { HRESULT hrCode = hr; if(FAILED(hrCode)) { return hrCode; } }
#define IF_SUCCEEDED(hr) { HRESULT hrCode = hr; if (!(hrCode == S_OK || hrCode == AUDCLNT_E_WRONG_ENDPOINT_TYPE)) { VERIFY_SUCCEEDED(hrCode); } }

bool SetupSkipRTHeap();
bool CleanupSkipRTHeap();

bool CAPOUpgradeTest::setUpMethod()
{
    return SetupSkipRTHeap();
}

bool CAPOUpgradeTest::tearDownMethod()
{
    return CleanupSkipRTHeap();
}

void CAPOUpgradeTest::TestUpgrade()
{
    UINT cDevices = 0;
    com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    com_ptr_nothrow<IMMDeviceCollection> spDevices;

    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));
    VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spDevices));
    VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

    for (UINT i = 0; i < cDevices; i++)
    {
        com_ptr_nothrow<IMMDevice> pDevice = nullptr;
        com_ptr_nothrow<IAudioClient2> pAudioClient = nullptr;
        com_ptr_nothrow<IMMEndpoint> pEndpoint = nullptr;
        BOOL bOffloadCapable = FALSE;
        EDataFlow eFlow = eAll;

        VERIFY_SUCCEEDED(spDevices->Item(i, &pDevice));

        VERIFY_SUCCEEDED(com_query_to_nothrow(pDevice, &pEndpoint));
        VERIFY_SUCCEEDED(pEndpoint->GetDataFlow(&eFlow));

        VERIFY_SUCCEEDED(pDevice->Activate(__uuidof(IAudioClient2), CLSCTX_ALL, NULL, (void**)&pAudioClient));
        VERIFY_SUCCEEDED(pAudioClient->IsOffloadCapable(AudioCategory_Media, &bOffloadCapable));

        if (eFlow == eAll || eFlow == eRender)
        {
            VERIFY_SUCCEEDED(BasicAudioStreaming(pDevice.get()));

            VERIFY_SUCCEEDED(BasicSpatialAudio(pDevice.get()));

            VERIFY_SUCCEEDED(BasicAudioLoopback(pDevice.get()));

            if (bOffloadCapable)
            {
                VERIFY_SUCCEEDED(BasicOffloadStreaming(pDevice.get()));
            }
        }
        
        if (eFlow == eAll || eFlow == eCapture)
        {
            VERIFY_SUCCEEDED(BasicAudioCapture(pDevice.get()));
        }
    }
}


<|start_filename|>audio/simpleaudiosample/Source/Main/common.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    common.cpp

Abstract:

    Implementation of the AdapterCommon class. 
--*/

#pragma warning (disable : 4127)

#include <initguid.h>
#include "definitions.h"
#include "hw.h"
#include "savedata.h"
#include "endpoints.h"

//-----------------------------------------------------------------------------
// CSaveData statics
//-----------------------------------------------------------------------------

PSAVEWORKER_PARAM       CSaveData::m_pWorkItems = NULL;
PDEVICE_OBJECT          CSaveData::m_pDeviceObject = NULL;
//=============================================================================
// Classes
//=============================================================================

///////////////////////////////////////////////////////////////////////////////
// CAdapterCommon
//   
class CAdapterCommon : 
    public IAdapterCommon,
    public IAdapterPowerManagement,
    public CUnknown    
{
    private:
        PSERVICEGROUP           m_pServiceGroupWave;
        PDEVICE_OBJECT          m_pDeviceObject;
        PDEVICE_OBJECT          m_pPhysicalDeviceObject;
        WDFDEVICE               m_WdfDevice;            // Wdf device. 
        DEVICE_POWER_STATE      m_PowerState;  

        PCSimpleAudioSampleHW   m_pHW;                  // Virtual Simple Audio Sample HW object
        PPORTCLSETWHELPER       m_pPortClsEtwHelper;

        static LONG             m_AdapterInstances;     // # of adapter objects.

        DWORD                   m_dwIdleRequests;

    public:
        //=====================================================================
        // Default CUnknown
        DECLARE_STD_UNKNOWN();
        DEFINE_STD_CONSTRUCTOR(CAdapterCommon);
        ~CAdapterCommon();

        //=====================================================================
        // Default IAdapterPowerManagement
        IMP_IAdapterPowerManagement;

        //=====================================================================
        // IAdapterCommon methods      

        STDMETHODIMP_(NTSTATUS) Init
        (   
            _In_  PDEVICE_OBJECT  DeviceObject
        );

        STDMETHODIMP_(PDEVICE_OBJECT)   GetDeviceObject(void);
        
        STDMETHODIMP_(PDEVICE_OBJECT)   GetPhysicalDeviceObject(void);
        
        STDMETHODIMP_(WDFDEVICE)        GetWdfDevice(void);

        STDMETHODIMP_(void)     SetWaveServiceGroup
        (   
            _In_  PSERVICEGROUP   ServiceGroup
        );

        STDMETHODIMP_(BOOL)     bDevSpecificRead();

        STDMETHODIMP_(void)     bDevSpecificWrite
        (
            _In_  BOOL            bDevSpecific
        );
        STDMETHODIMP_(INT)      iDevSpecificRead();

        STDMETHODIMP_(void)     iDevSpecificWrite
        (
            _In_  INT             iDevSpecific
        );
        STDMETHODIMP_(UINT)     uiDevSpecificRead();

        STDMETHODIMP_(void)     uiDevSpecificWrite
        (
            _In_  UINT            uiDevSpecific
        );

        STDMETHODIMP_(BOOL)     MixerMuteRead
        (
            _In_  ULONG           Index,
            _In_  ULONG           Channel
        );

        STDMETHODIMP_(void)     MixerMuteWrite
        (
            _In_  ULONG           Index,
            _In_  ULONG           Channel,
            _In_  BOOL            Value
        );

        STDMETHODIMP_(ULONG)    MixerMuxRead(void);

        STDMETHODIMP_(void)     MixerMuxWrite
        (
            _In_  ULONG           Index
        );

        STDMETHODIMP_(void)     MixerReset(void);

        STDMETHODIMP_(LONG)     MixerVolumeRead
        ( 
            _In_  ULONG           Index,
            _In_  ULONG           Channel
        );

        STDMETHODIMP_(void)     MixerVolumeWrite
        ( 
            _In_  ULONG           Index,
            _In_  ULONG           Channel,
            _In_  LONG            Value 
        );

        STDMETHODIMP_(LONG)     MixerPeakMeterRead
        ( 
            _In_  ULONG           Index,
            _In_  ULONG           Channel
        );

        STDMETHODIMP_(NTSTATUS) WriteEtwEvent 
        ( 
            _In_ EPcMiniportEngineEvent    miniportEventType,
            _In_ ULONGLONG      ullData1,
            _In_ ULONGLONG      ullData2,
            _In_ ULONGLONG      ullData3,
            _In_ ULONGLONG      ullData4
        );

        STDMETHODIMP_(VOID)     SetEtwHelper 
        ( 
            PPORTCLSETWHELPER _pPortClsEtwHelper
        );
        
        STDMETHODIMP_(NTSTATUS) InstallSubdevice
        ( 
            _In_opt_        PIRP                                        Irp,
            _In_            PWSTR                                       Name,
            _In_opt_        PWSTR                                       TemplateName,
            _In_            REFGUID                                     PortClassId,
            _In_            REFGUID                                     MiniportClassId,
            _In_opt_        PFNCREATEMINIPORT                           MiniportCreate,
            _In_            ULONG                                       cPropertyCount,
            _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY   * pProperties,
            _In_opt_        PVOID                                       DeviceContext,
            _In_            PENDPOINT_MINIPAIR                          MiniportPair,
            _In_opt_        PRESOURCELIST                               ResourceList,
            _In_            REFGUID                                     PortInterfaceId,
            _Out_opt_       PUNKNOWN                                  * OutPortInterface,
            _Out_opt_       PUNKNOWN                                  * OutPortUnknown,
            _Out_opt_       PUNKNOWN                                  * OutMiniportUnknown
        );
        
        STDMETHODIMP_(NTSTATUS) UnregisterSubdevice
        (
            _In_opt_ PUNKNOWN               UnknownPort
        );
        
        STDMETHODIMP_(NTSTATUS) ConnectTopologies
        (
            _In_ PUNKNOWN                   UnknownTopology,
            _In_ PUNKNOWN                   UnknownWave,
            _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
            _In_ ULONG                      PhysicalConnectionCount
        );
        
        STDMETHODIMP_(NTSTATUS) DisconnectTopologies
        (
            _In_ PUNKNOWN                   UnknownTopology,
            _In_ PUNKNOWN                   UnknownWave,
            _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
            _In_ ULONG                      PhysicalConnectionCount
        );
        
        STDMETHODIMP_(NTSTATUS) InstallEndpointFilters
        (
            _In_opt_    PIRP                Irp, 
            _In_        PENDPOINT_MINIPAIR  MiniportPair,
            _In_opt_    PVOID               DeviceContext,
            _Out_opt_   PUNKNOWN *          UnknownTopology,
            _Out_opt_   PUNKNOWN *          UnknownWave,
            _Out_opt_   PUNKNOWN *          UnknownMiniportTopology,
            _Out_opt_   PUNKNOWN *          UnknownMiniportWave
        );
        
        STDMETHODIMP_(NTSTATUS) RemoveEndpointFilters
        (
            _In_        PENDPOINT_MINIPAIR  MiniportPair,
            _In_opt_    PUNKNOWN            UnknownTopology,
            _In_opt_    PUNKNOWN            UnknownWave
        );

        STDMETHODIMP_(NTSTATUS) GetFilters
        (
            _In_        PENDPOINT_MINIPAIR  MiniportPair,
            _Out_opt_   PUNKNOWN            *UnknownTopologyPort,
            _Out_opt_   PUNKNOWN            *UnknownTopologyMiniport,
            _Out_opt_   PUNKNOWN            *UnknownWavePort,
            _Out_opt_   PUNKNOWN            *UnknownWaveMiniport
        );

        STDMETHODIMP_(NTSTATUS) SetIdlePowerManagement
        (
            _In_        PENDPOINT_MINIPAIR  MiniportPair,
            _In_        BOOL                bEnabled
        );

        STDMETHODIMP_(VOID) Cleanup();

        //=====================================================================
        // friends
        friend NTSTATUS         NewAdapterCommon
        ( 
            _Out_       PUNKNOWN *              Unknown,
            _In_        REFCLSID,
            _In_opt_    PUNKNOWN                UnknownOuter,
            _In_        POOL_FLAGS              PoolFlags
        );

    private:

    LIST_ENTRY m_SubdeviceCache;

    NTSTATUS GetCachedSubdevice
    (
        _In_ PWSTR Name,
        _Out_opt_ PUNKNOWN *OutUnknownPort,
        _Out_opt_ PUNKNOWN *OutUnknownMiniport
    );

    NTSTATUS CacheSubdevice
    (
        _In_ PWSTR Name,
        _In_ PUNKNOWN UnknownPort,
        _In_ PUNKNOWN UnknownMiniport
    );
    
    NTSTATUS RemoveCachedSubdevice
    (
        _In_ PWSTR Name
    );

    VOID EmptySubdeviceCache();

    NTSTATUS CreateAudioInterfaceWithProperties
    (
        _In_ PCWSTR                                                 ReferenceString,
        _In_opt_ PCWSTR                                             TemplateReferenceString,
        _In_ ULONG                                                  cPropertyCount,
        _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY        *pProperties,
        _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName
    );

    NTSTATUS MigrateDeviceInterfaceTemplateParameters
    (
        _In_ PUNICODE_STRING    SymbolicLinkName,
        _In_opt_ PCWSTR         TemplateReferenceString
    );
};

typedef struct _MINIPAIR_UNKNOWN
{
    LIST_ENTRY              ListEntry;
    WCHAR                   Name[MAX_PATH];
    PUNKNOWN                PortInterface;
    PUNKNOWN                MiniportInterface;
    PADAPTERPOWERMANAGEMENT PowerInterface;
} MINIPAIR_UNKNOWN;

#define MAX_DEVICE_REG_KEY_LENGTH 0x100

//
// Used to implement the singleton pattern.
//
LONG  CAdapterCommon::m_AdapterInstances = 0;



//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple
(
    _In_ PUNICODE_STRING                                        SymbolicLinkName,
    _In_ ULONG                                                  cPropertyCount,
    _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY        *pProperties
)
{
    NTSTATUS ntStatus;

    PAGED_CODE();

    if (pProperties)
    {
        for (ULONG i = 0; i < cPropertyCount; i++)
        {
            ntStatus = IoSetDeviceInterfacePropertyData(
                SymbolicLinkName,
                pProperties[i].PropertyKey,
                LOCALE_NEUTRAL,
                PLUGPLAY_PROPERTY_PERSISTENT,
                pProperties[i].Type,
                pProperties[i].BufferSize,
                pProperties[i].Buffer);

            if (!NT_SUCCESS(ntStatus))
            {
                return ntStatus;
            }
        }
    }

    return STATUS_SUCCESS;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
NewAdapterCommon
( 
    _Out_       PUNKNOWN *              Unknown,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN                UnknownOuter,
    _In_        POOL_FLAGS               PoolFlags
)
/*++

Routine Description:

  Creates a new CAdapterCommon

Arguments:

  Unknown - 

  UnknownOuter -

  PoolType

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(Unknown);

    NTSTATUS ntStatus;

    //
    // This sample supports only one instance of this object.
    // (b/c of CSaveData's static members and Bluetooth HFP logic). 
    //
    if (InterlockedCompareExchange(&CAdapterCommon::m_AdapterInstances, 1, 0) != 0)
    {
        ntStatus = STATUS_DEVICE_BUSY;
        DPF(D_ERROR, ("NewAdapterCommon failed, only one instance is allowed"));
        goto Done;
    }
    
    //
    // Allocate an adapter object.
    //
    CAdapterCommon *p = new(PoolFlags, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter);
    if (p == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        DPF(D_ERROR, ("NewAdapterCommon failed, 0x%x", ntStatus));
        goto Done;
    }

    // 
    // Success.
    //
    *Unknown = PUNKNOWN((PADAPTERCOMMON)(p));
    (*Unknown)->AddRef(); 
    ntStatus = STATUS_SUCCESS; 

Done:    
    return ntStatus;
} // NewAdapterCommon

//=============================================================================
#pragma code_seg("PAGE")
CAdapterCommon::~CAdapterCommon
( 
    void 
)
/*++

Routine Description:

  Destructor for CAdapterCommon.

Arguments:

Return Value:

  void

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::~CAdapterCommon]"));

    if (m_pHW)
    {
        delete m_pHW;
        m_pHW = NULL;
    }
    
    CSaveData::DestroyWorkItems();
    SAFE_RELEASE(m_pPortClsEtwHelper);
    SAFE_RELEASE(m_pServiceGroupWave);
 
    if (m_WdfDevice)
    {
        WdfObjectDelete(m_WdfDevice);
        m_WdfDevice = NULL;
    }

    InterlockedDecrement(&CAdapterCommon::m_AdapterInstances);
    ASSERT(CAdapterCommon::m_AdapterInstances == 0);
} // ~CAdapterCommon  

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(PDEVICE_OBJECT)   
CAdapterCommon::GetDeviceObject
(
    void
)
/*++

Routine Description:

  Returns the deviceobject

Arguments:

Return Value:

  PDEVICE_OBJECT

--*/
{
    PAGED_CODE();
    
    return m_pDeviceObject;
} // GetDeviceObject

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(PDEVICE_OBJECT)   
CAdapterCommon::GetPhysicalDeviceObject
(
    void
)
/*++

Routine Description:

  Returns the PDO.

Arguments:

Return Value:

  PDEVICE_OBJECT

--*/
{
    PAGED_CODE();
    
    return m_pPhysicalDeviceObject;
} // GetPhysicalDeviceObject

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(WDFDEVICE)   
CAdapterCommon::GetWdfDevice
(
    void
)
/*++

Routine Description:

  Returns the associated WDF miniport device. Note that this is NOT an audio
  miniport. The WDF miniport device is the WDF device associated with the
  adapter.

Arguments:

Return Value:

  WDFDEVICE

--*/
{
    PAGED_CODE();
    
    return m_WdfDevice;
} // GetWdfDevice

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CAdapterCommon::Init
( 
    _In_  PDEVICE_OBJECT          DeviceObject 
)
/*++

Routine Description:

    Initialize adapter common object.

Arguments:

    DeviceObject - pointer to the device object

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::Init]"));

    ASSERT(DeviceObject);

    NTSTATUS        ntStatus    = STATUS_SUCCESS;

    m_pServiceGroupWave     = NULL;
    m_pDeviceObject         = DeviceObject;
    m_pPhysicalDeviceObject = NULL;
    m_WdfDevice             = NULL;
    m_PowerState            = PowerDeviceD0;
    m_pHW                   = NULL;
    m_pPortClsEtwHelper     = NULL;

    InitializeListHead(&m_SubdeviceCache);

    //
    // Get the PDO.
    //
    ntStatus = PcGetPhysicalDeviceObject(DeviceObject, &m_pPhysicalDeviceObject);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("PcGetPhysicalDeviceObject failed, 0x%x", ntStatus)),
        Done);

    //
    // Create a WDF miniport to represent the adapter. Note that WDF miniports 
    // are NOT audio miniports. An audio adapter is associated with a single WDF
    // miniport. This driver uses WDF to simplify the handling of the Bluetooth
    // SCO HFP Bypass interface.
    //
    ntStatus = WdfDeviceMiniportCreate( WdfGetDriver(),
                                        WDF_NO_OBJECT_ATTRIBUTES,
                                        DeviceObject,           // FDO
                                        NULL,                   // Next device.
                                        NULL,                   // PDO
                                       &m_WdfDevice);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("WdfDeviceMiniportCreate failed, 0x%x", ntStatus)),
        Done);

    // Initialize HW.
    // 
    m_pHW = new (POOL_FLAG_NON_PAGED, SIMPLEAUDIOSAMPLE_POOLTAG)  CSimpleAudioSampleHW;
    if (!m_pHW)
    {
        DPF(D_TERSE, ("Insufficient memory for Simple Audio Sample HW"));
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    IF_FAILED_JUMP(ntStatus, Done);
    
    m_pHW->MixerReset();

    //
    // Initialize SaveData class.
    //
    CSaveData::SetDeviceObject(DeviceObject);   //device object is needed by CSaveData
    ntStatus = CSaveData::InitializeWorkItems(DeviceObject);
    IF_FAILED_JUMP(ntStatus, Done);
Done:

    return ntStatus;
} // Init

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CAdapterCommon::MixerReset
( 
    void 
)
/*++

Routine Description:

  Reset mixer registers from registry.

Arguments:

Return Value:

  void

--*/
{
    PAGED_CODE();
    
    if (m_pHW)
    {
        m_pHW->MixerReset();
    }
} // MixerReset

//=============================================================================
/* Here are the definitions of the standard miniport events.

Event type  : eMINIPORT_IHV_DEFINED 
Parameter 1 : Defined and used by IHVs
Parameter 2 : Defined and used by IHVs
Parameter 3 : Defined and used by IHVs
Parameter 4 :Defined and used by IHVs

Event type: eMINIPORT_BUFFER_COMPLETE
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: Data length completed
Parameter 4:0

Event type: eMINIPORT_PIN_STATE
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received        
Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN 
Parameter 4:0

Event type: eMINIPORT_GET_STREAM_POS
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: 0
Parameter 4:0


Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POS
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: the arget WaveRtBufferWritePosition received from portcls
Parameter 4:0

Event type: eMINIPORT_GET_PRESENTATION_POS
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: Presentation position
Parameter 4:0

Event type: eMINIPORT_PROGRAM_DMA 
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: Starting  WaveRt buffer offset
Parameter 4: Data length

Event type: eMINIPORT_GLITCH_REPORT
Parameter 1: Current linear buffer position    
Parameter 2: the previous WaveRtBufferWritePosition that the drive received    
Parameter 3: major glitch code: 1:WaveRT buffer is underrun, 
                                2:decoder errors, 
                                3:receive the same wavert buffer two in a row in event driven mode
Parameter 4: minor code for the glitch cause

Event type: eMINIPORT_LAST_BUFFER_RENDERED
Parameter 1: Current linear buffer position    
Parameter 2: the very last WaveRtBufferWritePosition that the driver received    
Parameter 3: 0
Parameter 4: 0

*/
#pragma code_seg()
STDMETHODIMP
CAdapterCommon::WriteEtwEvent
( 
    _In_ EPcMiniportEngineEvent    miniportEventType,
    _In_ ULONGLONG  ullData1,
    _In_ ULONGLONG  ullData2,
    _In_ ULONGLONG  ullData3,
    _In_ ULONGLONG  ullData4
)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    if (m_pPortClsEtwHelper)
    {
        ntStatus = m_pPortClsEtwHelper->MiniportWriteEtwEvent( miniportEventType, ullData1, ullData2, ullData3, ullData4) ;
    }
    return ntStatus;
} // WriteEtwEvent

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CAdapterCommon::SetEtwHelper
( 
    PPORTCLSETWHELPER _pPortClsEtwHelper
)
{
    PAGED_CODE();
    
    SAFE_RELEASE(m_pPortClsEtwHelper);

    m_pPortClsEtwHelper = _pPortClsEtwHelper;

    if (m_pPortClsEtwHelper)
    {
        m_pPortClsEtwHelper->AddRef();
    }
} // SetEtwHelper

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP
CAdapterCommon::NonDelegatingQueryInterface
( 
    _In_ REFIID                      Interface,
    _COM_Outptr_ PVOID *        Object 
)
/*++

Routine Description:

  QueryInterface routine for AdapterCommon

Arguments:

  Interface - 

  Object -

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(Object);

    if (IsEqualGUIDAligned(Interface, IID_IUnknown))
    {
        *Object = PVOID(PUNKNOWN(PADAPTERCOMMON(this)));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IAdapterCommon))
    {
        *Object = PVOID(PADAPTERCOMMON(this));
    }
    else if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement))
    {
        *Object = PVOID(PADAPTERPOWERMANAGEMENT(this));
    }
    else
    {
        *Object = NULL;
    }

    if (*Object)
    {
        PUNKNOWN(*Object)->AddRef();
        return STATUS_SUCCESS;
    }

    return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CAdapterCommon::SetWaveServiceGroup
( 
    _In_ PSERVICEGROUP            ServiceGroup 
)
/*++

Routine Description:


Arguments:

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::SetWaveServiceGroup]"));
    
    SAFE_RELEASE(m_pServiceGroupWave);

    m_pServiceGroupWave = ServiceGroup;

    if (m_pServiceGroupWave)
    {
        m_pServiceGroupWave->AddRef();
    }
} // SetWaveServiceGroup

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(BOOL)
CAdapterCommon::bDevSpecificRead()
/*++

Routine Description:

  Fetch Device Specific information.

Arguments:

  N/A

Return Value:

    BOOL - Device Specific info

--*/
{
    if (m_pHW)
    {
        return m_pHW->bGetDevSpecific();
    }

    return FALSE;
} // bDevSpecificRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::bDevSpecificWrite
(
    _In_  BOOL                    bDevSpecific
)
/*++

Routine Description:

  Store the new value in the Device Specific location.

Arguments:

  bDevSpecific - Value to store

Return Value:

  N/A.

--*/
{
    if (m_pHW)
    {
        m_pHW->bSetDevSpecific(bDevSpecific);
    }
} // DevSpecificWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(INT)
CAdapterCommon::iDevSpecificRead()
/*++

Routine Description:

  Fetch Device Specific information.

Arguments:

  N/A

Return Value:

    INT - Device Specific info

--*/
{
    if (m_pHW)
    {
        return m_pHW->iGetDevSpecific();
    }

    return 0;
} // iDevSpecificRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::iDevSpecificWrite
(
    _In_  INT                    iDevSpecific
)
/*++

Routine Description:

  Store the new value in the Device Specific location.

Arguments:

  iDevSpecific - Value to store

Return Value:

  N/A.

--*/
{
    if (m_pHW)
    {
        m_pHW->iSetDevSpecific(iDevSpecific);
    }
} // iDevSpecificWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(UINT)
CAdapterCommon::uiDevSpecificRead()
/*++

Routine Description:

  Fetch Device Specific information.

Arguments:

  N/A

Return Value:

    UINT - Device Specific info

--*/
{
    if (m_pHW)
    {
        return m_pHW->uiGetDevSpecific();
    }

    return 0;
} // uiDevSpecificRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::uiDevSpecificWrite
(
    _In_  UINT                    uiDevSpecific
)
/*++

Routine Description:

  Store the new value in the Device Specific location.

Arguments:

  uiDevSpecific - Value to store

Return Value:

  N/A.

--*/
{
    if (m_pHW)
    {
        m_pHW->uiSetDevSpecific(uiDevSpecific);
    }
} // uiDevSpecificWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(BOOL)
CAdapterCommon::MixerMuteRead
(
    _In_  ULONG               Index,
    _In_  ULONG               Channel
)
/*++

Routine Description:

  Store the new value in mixer register array.

Arguments:

  Index - node id

Return Value:

    BOOL - mixer mute setting for this node

--*/
{
    if (m_pHW)
    {
        return m_pHW->GetMixerMute(Index, Channel);
    }

    return 0;
} // MixerMuteRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::MixerMuteWrite
(
    _In_  ULONG                   Index,
    _In_  ULONG                   Channel,
    _In_  BOOL                    Value
)
/*++

Routine Description:

  Store the new value in mixer register array.

Arguments:

  Index - node id

  Value - new mute settings

Return Value:

  NT status code.

--*/
{
    if (m_pHW)
    {
        m_pHW->SetMixerMute(Index, Channel, Value);
    }
} // MixerMuteWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(ULONG)
CAdapterCommon::MixerMuxRead() 
/*++

Routine Description:

  Return the mux selection

Arguments:

  Index - node id

  Value - new mute settings

Return Value:

  NT status code.

--*/
{
    if (m_pHW)
    {
        return m_pHW->GetMixerMux();
    }

    return 0;
} // MixerMuxRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::MixerMuxWrite
(
    _In_  ULONG                   Index
)
/*++

Routine Description:

  Store the new mux selection

Arguments:

  Index - node id

  Value - new mute settings

Return Value:

  NT status code.

--*/
{
    if (m_pHW)
    {
        m_pHW->SetMixerMux(Index);
    }
} // MixerMuxWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(LONG)
CAdapterCommon::MixerVolumeRead
( 
    _In_  ULONG                   Index,
    _In_  ULONG                   Channel
)
/*++

Routine Description:

  Return the value in mixer register array.

Arguments:

  Index - node id

  Channel = which channel

Return Value:

    Byte - mixer volume settings for this line

--*/
{
    if (m_pHW)
    {
        return m_pHW->GetMixerVolume(Index, Channel);
    }

    return 0;
} // MixerVolumeRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::MixerVolumeWrite
( 
    _In_  ULONG                   Index,
    _In_  ULONG                   Channel,
    _In_  LONG                    Value
)
/*++

Routine Description:

  Store the new value in mixer register array.

Arguments:

  Index - node id

  Channel - which channel

  Value - new volume level

Return Value:

    void

--*/
{
    if (m_pHW)
    {
        m_pHW->SetMixerVolume(Index, Channel, Value);
    }
} // MixerVolumeWrite

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(LONG)
CAdapterCommon::MixerPeakMeterRead
( 
    _In_  ULONG                   Index,
    _In_  ULONG                   Channel
)
/*++

Routine Description:

  Return the value in mixer register array.

Arguments:

  Index - node id

  Channel = which channel

Return Value:

    Byte - mixer sample peak meter settings for this line

--*/
{
    if (m_pHW)
    {
        return m_pHW->GetMixerPeakMeter(Index, Channel);
    }

    return 0;
} // MixerVolumeRead

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(void)
CAdapterCommon::PowerChangeState
( 
    _In_  POWER_STATE             NewState 
)
/*++

Routine Description:


Arguments:

  NewState - The requested, new power state for the device. 

Return Value:

    void

Note:
  From MSDN:

  To assist the driver, PortCls will pause any active audio streams prior to calling
  this method to place the device in a sleep state. After calling this method, PortCls
  will unpause active audio streams, to wake the device up. Miniports can opt for 
  additional notification by utilizing the IPowerNotify interface.

  The miniport driver must perform the requested change to the device's power state 
  before it returns from the PowerChangeState call. If the miniport driver needs to 
  save or restore any device state before a power-state change, the miniport driver 
  should support the IPowerNotify interface, which allows it to receive advance warning
  of any such change. Before returning from a successful PowerChangeState call, the 
  miniport driver should cache the new power state.

  While the miniport driver is in one of the sleep states (any state other than 
  PowerDeviceD0), it must avoid writing to the hardware. The miniport driver must cache
  any hardware accesses that need to be deferred until the device powers up again. If
  the power state is changing from one of the sleep states to PowerDeviceD0, the 
  miniport driver should perform any deferred hardware accesses after it has powered up
  the device. If the power state is changing from PowerDeviceD0 to a sleep state, the 
  miniport driver can perform any necessary hardware accesses during the PowerChangeState
  call before it powers down the device.

  While powered down, a miniport driver is never asked to create a miniport driver object
  or stream object. PortCls always places the device in the PowerDeviceD0 state before
  calling the miniport driver's NewStream method.
  
--*/
{
    DPF_ENTER(("[CAdapterCommon::PowerChangeState]"));

    // Notify all registered miniports of a power state change
    PLIST_ENTRY le = NULL;
    for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache; le = le->Flink)
    {
        MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry);

        if (pRecord->PowerInterface)
        {
            pRecord->PowerInterface->PowerChangeState(NewState);
        }
    }

    // is this actually a state change??
    //
    if (NewState.DeviceState != m_PowerState)
    {
        // switch on new state
        //
        switch (NewState.DeviceState)
        {
            case PowerDeviceD0:
            case PowerDeviceD1:
            case PowerDeviceD2:
            case PowerDeviceD3:
                m_PowerState = NewState.DeviceState;

                DPF
                ( 
                    D_VERBOSE, 
                    ("Entering D%u", ULONG(m_PowerState) - ULONG(PowerDeviceD0)) 
                );

                break;
    
            default:
            
                DPF(D_VERBOSE, ("Unknown Device Power State"));
                break;
        }
    }
} // PowerStateChange

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::QueryDeviceCapabilities
( 
    _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES    PowerDeviceCaps 
)
/*++

Routine Description:

    Called at startup to get the caps for the device.  This structure provides 
    the system with the mappings between system power state and device power 
    state.  This typically will not need modification by the driver.         

Arguments:

  PowerDeviceCaps - The device's capabilities. 

Return Value:

  NT status code.

--*/
{
    UNREFERENCED_PARAMETER(PowerDeviceCaps);

    DPF_ENTER(("[CAdapterCommon::QueryDeviceCapabilities]"));

    return (STATUS_SUCCESS);
} // QueryDeviceCapabilities

//=============================================================================
#pragma code_seg()
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::QueryPowerChangeState
( 
    _In_  POWER_STATE             NewStateQuery 
)
/*++

Routine Description:

  Query to see if the device can change to this power state 

Arguments:

  NewStateQuery - The requested, new power state for the device

Return Value:

  NT status code.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;

    DPF_ENTER(("[CAdapterCommon::QueryPowerChangeState]"));

    // query each miniport for it's power state, we're finished if even one indicates
    // it cannot go to this power state.
    PLIST_ENTRY le = NULL;
    for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && NT_SUCCESS(status); le = le->Flink)
    {
        MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry);

        if (pRecord->PowerInterface)
        {
            status = pRecord->PowerInterface->QueryPowerChangeState(NewStateQuery);
        }
    }

    return status;
} // QueryPowerChangeState

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CAdapterCommon::CreateAudioInterfaceWithProperties
(
    _In_ PCWSTR ReferenceString,
    _In_opt_ PCWSTR TemplateReferenceString,
    _In_ ULONG cPropertyCount,
    _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY *pProperties,
    _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName
)
/*++

Routine Description:

Create the audio interface (in disabled mode).

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::CreateAudioInterfaceWithProperties]"));

    NTSTATUS        ntStatus;
    UNICODE_STRING  referenceString;

    RtlInitUnicodeString(&referenceString, ReferenceString);

    //
    // Reset output value.
    //
    RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING));

    //
    // Register an audio interface if not already present.
    //
    ntStatus = IoRegisterDeviceInterface(
        GetPhysicalDeviceObject(),
        &KSCATEGORY_AUDIO,
        &referenceString,
        AudioSymbolicLinkName);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: IoRegisterDeviceInterface(KSCATEGORY_AUDIO): failed, 0x%x", ntStatus)),
        Done);

    //
    // Migrate optional device interface parameters from the template if it exists
    // This is done first, so that any additional parameters in pProperties will override the defaults.
    //
    if (NULL != TemplateReferenceString)
    {
        ntStatus = MigrateDeviceInterfaceTemplateParameters(AudioSymbolicLinkName, TemplateReferenceString);

        IF_FAILED_ACTION_JUMP(
            ntStatus,
            DPF(D_ERROR, ("MigrateDeviceInterfaceTempalteParameters: MigrateDeviceInterfaceTemplateParameters(...): failed, 0x%x", ntStatus)),
            Done);
    }

    //
    // Set properties on the interface
    //
    ntStatus = SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple(AudioSymbolicLinkName, cPropertyCount, pProperties);

    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: SimpleAudioSampleIoSetDeviceInterfacePropertyDataMultiple(...): failed, 0x%x", ntStatus)),
        Done);

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:
    if (!NT_SUCCESS(ntStatus))
    {
        RtlFreeUnicodeString(AudioSymbolicLinkName);
        RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING));
    }
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::InstallSubdevice
( 
    _In_opt_        PIRP                                    Irp,
    _In_            PWSTR                                   Name,
    _In_opt_        PWSTR                                   TemplateName,
    _In_            REFGUID                                 PortClassId,
    _In_            REFGUID                                 MiniportClassId,
    _In_opt_        PFNCREATEMINIPORT                       MiniportCreate,
    _In_            ULONG                                   cPropertyCount,
    _In_reads_opt_(cPropertyCount) const SIMPLEAUDIOSAMPLE_DEVPROPERTY * pProperties,
    _In_opt_        PVOID                                   DeviceContext,
    _In_            PENDPOINT_MINIPAIR                      MiniportPair,
    _In_opt_        PRESOURCELIST                           ResourceList,
    _In_            REFGUID                                 PortInterfaceId,
    _Out_opt_       PUNKNOWN                              * OutPortInterface,
    _Out_opt_       PUNKNOWN                              * OutPortUnknown,
    _Out_opt_       PUNKNOWN                              * OutMiniportUnknown
)
{
/*++

Routine Description:

    This function creates and registers a subdevice consisting of a port       
    driver, a minport driver and a set of resources bound together.  It will   
    also optionally place a pointer to an interface on the port driver in a    
    specified location before initializing the port driver.  This is done so   
    that a common ISR can have access to the port driver during 
    initialization, when the ISR might fire.                                   

Arguments:

    Irp - pointer to the irp object.

    Name - name of the miniport. Passes to PcRegisterSubDevice
 
    PortClassId - port class id. Passed to PcNewPort.

    MiniportClassId - miniport class id. Passed to PcNewMiniport.

    MiniportCreate - pointer to a miniport creation function. If NULL, 
                     PcNewMiniport is used.
                     
    DeviceContext - deviceType specific.

    MiniportPair - endpoint configuration info.    

    ResourceList - pointer to the resource list.

    PortInterfaceId - GUID that represents the port interface.
       
    OutPortInterface - pointer to store the port interface

    OutPortUnknown - pointer to store the unknown port interface.

    OutMiniportUnknown - pointer to store the unknown miniport interface

Return Value:

    NT status code.

--*/
    PAGED_CODE();
    DPF_ENTER(("[InstallSubDevice %S]", Name));

    ASSERT(Name != NULL);
    ASSERT(m_pDeviceObject != NULL);

    NTSTATUS                    ntStatus;
    PPORT                       port            = NULL;
    PUNKNOWN                    miniport        = NULL;
    PADAPTERCOMMON              adapterCommon   = NULL;
    UNICODE_STRING              symbolicLink    = { 0 };

    adapterCommon = PADAPTERCOMMON(this);

    ntStatus = CreateAudioInterfaceWithProperties(Name, TemplateName, cPropertyCount, pProperties, &symbolicLink);
    if (NT_SUCCESS(ntStatus))
    {
        // Currently have no use for the symbolic link
        RtlFreeUnicodeString(&symbolicLink);

        // Create the port driver object
        //
        ntStatus = PcNewPort(&port, PortClassId);
    }

    // Create the miniport object
    //
    if (NT_SUCCESS(ntStatus))
    {
        if (MiniportCreate)
        {
            ntStatus = 
                MiniportCreate
                ( 
                    &miniport,
                    MiniportClassId,
                    NULL,
                    POOL_FLAG_NON_PAGED,
                    adapterCommon,
                    DeviceContext,
                    MiniportPair
                );
        }
        else
        {
            ntStatus = 
                PcNewMiniport
                (
                    (PMINIPORT *) &miniport, 
                    MiniportClassId
                );
        }
    }

    // Init the port driver and miniport in one go.
    //
    if (NT_SUCCESS(ntStatus))
    {
#pragma warning(push)
        // IPort::Init's annotation on ResourceList requires it to be non-NULL.  However,
        // for dynamic devices, we may no longer have the resource list and this should
        // still succeed.
        //
#pragma warning(disable:6387)
        ntStatus = 
            port->Init
            ( 
                m_pDeviceObject,
                Irp,
                miniport,
                adapterCommon,
                ResourceList 
            );
#pragma warning (pop)

        if (NT_SUCCESS(ntStatus))
        {
            // Register the subdevice (port/miniport combination).
            //
            ntStatus = 
                PcRegisterSubdevice
                ( 
                    m_pDeviceObject,
                    Name,
                    port 
                );
        }
    }

    // Deposit the port interfaces if it's needed.
    //
    if (NT_SUCCESS(ntStatus))
    {
        if (OutPortUnknown)
        {
            ntStatus = 
                port->QueryInterface
                ( 
                    IID_IUnknown,
                    (PVOID *)OutPortUnknown 
                );
        }

        if (OutPortInterface)
        {
            ntStatus = 
                port->QueryInterface
                ( 
                    PortInterfaceId,
                    (PVOID *) OutPortInterface 
                );
        }

        if (OutMiniportUnknown)
        {
            ntStatus = 
                miniport->QueryInterface
                ( 
                    IID_IUnknown,
                    (PVOID *)OutMiniportUnknown 
                );
        }

    }

    if (port)
    {
        port->Release();
    }

    if (miniport)
    {
        miniport->Release();
    }

    return ntStatus;
} // InstallSubDevice

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::UnregisterSubdevice
(
    _In_opt_   PUNKNOWN     UnknownPort
)
/*++

Routine Description:

  Unregisters and releases the specified subdevice.

Arguments:

  UnknownPort - Wave or topology port interface.

Return Value:

  NTSTATUS

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::UnregisterSubdevice]"));

    ASSERT(m_pDeviceObject != NULL);
    
    NTSTATUS                ntStatus            = STATUS_SUCCESS;
    PUNREGISTERSUBDEVICE    unregisterSubdevice = NULL;
    
    if (NULL == UnknownPort)
    {
        return ntStatus;
    }

    //
    // Get the IUnregisterSubdevice interface.
    //
    ntStatus = UnknownPort->QueryInterface( 
        IID_IUnregisterSubdevice,
        (PVOID *)&unregisterSubdevice);

    //
    // Unregister the port object.
    //
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = unregisterSubdevice->UnregisterSubdevice(
            m_pDeviceObject,
            UnknownPort);

        //
        // Release the IUnregisterSubdevice interface.
        //
        unregisterSubdevice->Release();
    }
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::ConnectTopologies
(
    _In_ PUNKNOWN                   UnknownTopology,
    _In_ PUNKNOWN                   UnknownWave,
    _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
    _In_ ULONG                      PhysicalConnectionCount
)
/*++

Routine Description:

  Connects the bridge pins between the wave and mixer topologies.

Arguments:

Return Value:

  NTSTATUS

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::ConnectTopologies]"));
    
    ASSERT(m_pDeviceObject != NULL);
    
    NTSTATUS        ntStatus            = STATUS_SUCCESS;

    //
    // register wave <=> topology connections
    // This will connect bridge pins of wave and topology
    // miniports.
    //
    for (ULONG i = 0; i < PhysicalConnectionCount && NT_SUCCESS(ntStatus); i++)
    {
    
        switch(PhysicalConnections[i].eType)
        {
            case CONNECTIONTYPE_TOPOLOGY_OUTPUT:
                ntStatus =
                    PcRegisterPhysicalConnection
                    ( 
                        m_pDeviceObject,
                        UnknownTopology,
                        PhysicalConnections[i].ulTopology,
                        UnknownWave,
                        PhysicalConnections[i].ulWave
                    );
                if (!NT_SUCCESS(ntStatus))
                {
                    DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(render) failed, 0x%x", ntStatus));
                }
                break;
            case CONNECTIONTYPE_WAVE_OUTPUT:
                ntStatus =
                    PcRegisterPhysicalConnection
                    ( 
                        m_pDeviceObject,
                        UnknownWave,
                        PhysicalConnections[i].ulWave,
                        UnknownTopology,
                        PhysicalConnections[i].ulTopology
                    );
                if (!NT_SUCCESS(ntStatus))
                {
                    DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(capture) failed, 0x%x", ntStatus));
                }
                break;
        }
    }    

    //
    // Cleanup in case of error.
    //
    if (!NT_SUCCESS(ntStatus))
    {
        // disconnect all connections on error, ignore error code because not all
        // connections may have been made
        DisconnectTopologies(UnknownTopology, UnknownWave, PhysicalConnections, PhysicalConnectionCount);
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::DisconnectTopologies
(
    _In_ PUNKNOWN                   UnknownTopology,
    _In_ PUNKNOWN                   UnknownWave,
    _In_ PHYSICALCONNECTIONTABLE*   PhysicalConnections,
    _In_ ULONG                      PhysicalConnectionCount
)
/*++

Routine Description:

  Disconnects the bridge pins between the wave and mixer topologies.

Arguments:

Return Value:

  NTSTATUS

--*/
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::DisconnectTopologies]"));
    
    ASSERT(m_pDeviceObject != NULL);
    
    NTSTATUS                        ntStatus                        = STATUS_SUCCESS;
    NTSTATUS                        ntStatus2                       = STATUS_SUCCESS;
    PUNREGISTERPHYSICALCONNECTION   unregisterPhysicalConnection    = NULL;

    //
    // Get the IUnregisterPhysicalConnection interface
    //
    ntStatus = UnknownTopology->QueryInterface( 
        IID_IUnregisterPhysicalConnection,
        (PVOID *)&unregisterPhysicalConnection);
    
    if (NT_SUCCESS(ntStatus))
    { 
        for (ULONG i = 0; i < PhysicalConnectionCount; i++)
        {
            switch(PhysicalConnections[i].eType)
            {
                case CONNECTIONTYPE_TOPOLOGY_OUTPUT:
                    ntStatus =
                        unregisterPhysicalConnection->UnregisterPhysicalConnection(
                            m_pDeviceObject,
                            UnknownTopology,
                            PhysicalConnections[i].ulTopology,
                            UnknownWave,
                            PhysicalConnections[i].ulWave
                        );

                    if (!NT_SUCCESS(ntStatus))
                    {
                        DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(render) failed, 0x%x", ntStatus));
                    }
                    break;
                case CONNECTIONTYPE_WAVE_OUTPUT:
                    ntStatus =
                        unregisterPhysicalConnection->UnregisterPhysicalConnection(
                            m_pDeviceObject,
                            UnknownWave,
                            PhysicalConnections[i].ulWave,
                            UnknownTopology,
                            PhysicalConnections[i].ulTopology
                        );
                    if (!NT_SUCCESS(ntStatus2))
                    {
                        DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(capture) failed, 0x%x", ntStatus2));
                    }
                    break;
            }

            // cache and return the first error encountered, as it's likely the most relevent
            if (NT_SUCCESS(ntStatus))
            {
                ntStatus = ntStatus2;
            }
        }    
    }
    
    //
    // Release the IUnregisterPhysicalConnection interface.
    //
    SAFE_RELEASE(unregisterPhysicalConnection);

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CAdapterCommon::GetCachedSubdevice
(
    _In_ PWSTR Name,
    _Out_opt_ PUNKNOWN *OutUnknownPort,
    _Out_opt_ PUNKNOWN *OutUnknownMiniport
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::GetCachedSubdevice]"));

    // search list, return interface to device if found, fail if not found
    PLIST_ENTRY le = NULL;
    BOOL bFound = FALSE;

    for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bFound; le = le->Flink)
    {
        MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry);

        if (0 == wcscmp(Name, pRecord->Name))
        {
            if (OutUnknownPort)
            {
                *OutUnknownPort = pRecord->PortInterface;
                (*OutUnknownPort)->AddRef();
            }

            if (OutUnknownMiniport)
            {
                *OutUnknownMiniport = pRecord->MiniportInterface;
                (*OutUnknownMiniport)->AddRef();
            }

            bFound = TRUE;
        }
    }

    return bFound?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND;
}



//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CAdapterCommon::CacheSubdevice
(
    _In_ PWSTR Name,
    _In_ PUNKNOWN UnknownPort,
    _In_ PUNKNOWN UnknownMiniport
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::CacheSubdevice]"));

    // add the item with this name/interface to the list
    NTSTATUS         ntStatus       = STATUS_SUCCESS;
    MINIPAIR_UNKNOWN *pNewSubdevice = NULL;

    pNewSubdevice = new(POOL_FLAG_NON_PAGED, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN;

    if (!pNewSubdevice)
    {
        DPF(D_TERSE, ("Insufficient memory to cache subdevice"));
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (NT_SUCCESS(ntStatus))
    {
        memset(pNewSubdevice, 0, sizeof(MINIPAIR_UNKNOWN));

        ntStatus = RtlStringCchCopyW(pNewSubdevice->Name, SIZEOF_ARRAY(pNewSubdevice->Name), Name);
    }

    if (NT_SUCCESS(ntStatus))
    {
        pNewSubdevice->PortInterface = UnknownPort;
        pNewSubdevice->PortInterface->AddRef();

        pNewSubdevice->MiniportInterface = UnknownMiniport;
        pNewSubdevice->MiniportInterface->AddRef();

        // cache the IAdapterPowerManagement interface (if available) from the filter. Some endpoints,
        // like FM and cellular, have their own power requirements that we must track. If this fails,
        // it just means this filter doesn't do power management.
        UnknownMiniport->QueryInterface(IID_IAdapterPowerManagement, (PVOID *)&(pNewSubdevice->PowerInterface));

        InsertTailList(&m_SubdeviceCache, &pNewSubdevice->ListEntry);
    }

    if (!NT_SUCCESS(ntStatus))
    {
        if (pNewSubdevice)
        {
            delete pNewSubdevice;
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CAdapterCommon::RemoveCachedSubdevice
(
    _In_ PWSTR Name
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::RemoveCachedSubdevice]"));

    // search list, remove the entry from the list

    PLIST_ENTRY le = NULL;
    BOOL bRemoved = FALSE;

    for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bRemoved; le = le->Flink)
    {
        MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry);

        if (0 == wcscmp(Name, pRecord->Name))
        {
            SAFE_RELEASE(pRecord->PortInterface);
            SAFE_RELEASE(pRecord->MiniportInterface);
            SAFE_RELEASE(pRecord->PowerInterface);
            memset(pRecord->Name, 0, sizeof(pRecord->Name));
            RemoveEntryList(le);
            bRemoved = TRUE;
            delete pRecord;
            break;
        }
    }

    return bRemoved?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND;
}

#pragma code_seg("PAGE")
VOID
CAdapterCommon::EmptySubdeviceCache()
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::EmptySubdeviceCache]"));

    while (!IsListEmpty(&m_SubdeviceCache))
    {
        PLIST_ENTRY le = RemoveHeadList(&m_SubdeviceCache);
        MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry);

        SAFE_RELEASE(pRecord->PortInterface);
        SAFE_RELEASE(pRecord->MiniportInterface);
        SAFE_RELEASE(pRecord->PowerInterface);
        memset(pRecord->Name, 0, sizeof(pRecord->Name));

        delete pRecord;
    }
}

#pragma code_seg("PAGE")
VOID
CAdapterCommon::Cleanup()
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::Cleanup]"));
    EmptySubdeviceCache();
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::InstallEndpointFilters
(
    _In_opt_    PIRP                Irp, 
    _In_        PENDPOINT_MINIPAIR  MiniportPair,
    _In_opt_    PVOID               DeviceContext,
    _Out_opt_   PUNKNOWN *          UnknownTopology,
    _Out_opt_   PUNKNOWN *          UnknownWave,
    _Out_opt_   PUNKNOWN *          UnknownMiniportTopology,
    _Out_opt_   PUNKNOWN *          UnknownMiniportWave
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::InstallEndpointFilters]"));
    
    NTSTATUS            ntStatus            = STATUS_SUCCESS;
    PUNKNOWN            unknownTopology     = NULL;
    PUNKNOWN            unknownWave         = NULL;
    BOOL                bTopologyCreated    = FALSE;
    BOOL                bWaveCreated        = FALSE;
    PUNKNOWN            unknownMiniTopo     = NULL;
    PUNKNOWN            unknownMiniWave     = NULL;

    // Initialize output optional parameters if needed
    if (UnknownTopology)
    {
        *UnknownTopology = NULL;
    }

    if (UnknownWave)
    {
        *UnknownWave = NULL;
    }
  
    if (UnknownMiniportTopology)
    {
        *UnknownMiniportTopology = NULL;
    }

    if (UnknownMiniportWave)
    {
        *UnknownMiniportWave = NULL;
    }

    ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopology, &unknownMiniTopo);
    if (!NT_SUCCESS(ntStatus) || NULL == unknownTopology || NULL == unknownMiniTopo)
    {
        bTopologyCreated = TRUE;

        // Install Simple Audio Sample topology miniport for the render endpoint.
        //
        ntStatus = InstallSubdevice(Irp,
                                    MiniportPair->TopoName, // make sure this name matches with SIMPLEAUDIOSAMPLE.<TopoName>.szPname in the inf's [Strings] section
                                    MiniportPair->TemplateTopoName,
                                    CLSID_PortTopology,
                                    CLSID_PortTopology, 
                                    MiniportPair->TopoCreateCallback,
                                    MiniportPair->TopoInterfacePropertyCount,
                                    MiniportPair->TopoInterfaceProperties,
                                    DeviceContext,
                                    MiniportPair,
                                    NULL,
                                    IID_IPortTopology,
                                    NULL,
                                    &unknownTopology,
                                    &unknownMiniTopo
                                    );
        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = CacheSubdevice(MiniportPair->TopoName, unknownTopology, unknownMiniTopo);
        }
    }

    ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWave, &unknownMiniWave);
    if (!NT_SUCCESS(ntStatus) || NULL == unknownWave || NULL == unknownMiniWave)
    {
        bWaveCreated = TRUE;

        // Install Simple Audio Sample wave miniport for the render endpoint.
        //
        ntStatus = InstallSubdevice(Irp,
                                    MiniportPair->WaveName, // make sure this name matches with SIMPLEAUDIOSAMPLE.<WaveName>.szPname in the inf's [Strings] section
                                    MiniportPair->TemplateWaveName,
                                    CLSID_PortWaveRT,
                                    CLSID_PortWaveRT,   
                                    MiniportPair->WaveCreateCallback,
                                    MiniportPair->WaveInterfacePropertyCount,
                                    MiniportPair->WaveInterfaceProperties,
                                    DeviceContext,
                                    MiniportPair,
                                    NULL,
                                    IID_IPortWaveRT,
                                    NULL, 
                                    &unknownWave,
                                    &unknownMiniWave
                                    );

        if (NT_SUCCESS(ntStatus))
        {
            ntStatus = CacheSubdevice(MiniportPair->WaveName, unknownWave, unknownMiniWave);
        }
    }

    if (unknownTopology && unknownWave)
    {
        //
        // register wave <=> topology connections
        // This will connect bridge pins of wave and topology
        // miniports.
        //
        ntStatus = ConnectTopologies(
            unknownTopology,
            unknownWave,
            MiniportPair->PhysicalConnections,
            MiniportPair->PhysicalConnectionCount);
    }

    if (NT_SUCCESS(ntStatus))
    {
        //
        // Set output parameters.
        //
        if (UnknownTopology != NULL && unknownTopology != NULL)
        {
            unknownTopology->AddRef();
            *UnknownTopology = unknownTopology;
        }
        
        if (UnknownWave != NULL && unknownWave != NULL)
        {
            unknownWave->AddRef();
            *UnknownWave = unknownWave;
        }
        if (UnknownMiniportTopology != NULL && unknownMiniTopo != NULL)
        {
            unknownMiniTopo->AddRef();
            *UnknownMiniportTopology = unknownMiniTopo;
        }

        if (UnknownMiniportWave != NULL && unknownMiniWave != NULL)
        {
            unknownMiniWave->AddRef();
            *UnknownMiniportWave = unknownMiniWave;
        }

    }
    else
    {
        if (bTopologyCreated && unknownTopology != NULL)
        {
            UnregisterSubdevice(unknownTopology);
            RemoveCachedSubdevice(MiniportPair->TopoName);
        }
            
        if (bWaveCreated && unknownWave != NULL)
        {
            UnregisterSubdevice(unknownWave);
            RemoveCachedSubdevice(MiniportPair->WaveName);
        }
    }
   
    SAFE_RELEASE(unknownMiniTopo);
    SAFE_RELEASE(unknownTopology);
    SAFE_RELEASE(unknownMiniWave);
    SAFE_RELEASE(unknownWave);

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::RemoveEndpointFilters
(
    _In_        PENDPOINT_MINIPAIR  MiniportPair,
    _In_opt_    PUNKNOWN            UnknownTopology,
    _In_opt_    PUNKNOWN            UnknownWave
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::RemoveEndpointFilters]"));
    
    NTSTATUS    ntStatus   = STATUS_SUCCESS;
    
    if (UnknownTopology != NULL && UnknownWave != NULL)
    {
        ntStatus = DisconnectTopologies(
            UnknownTopology,
            UnknownWave,
            MiniportPair->PhysicalConnections,
            MiniportPair->PhysicalConnectionCount);

        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_VERBOSE, ("RemoveEndpointFilters: DisconnectTopologies failed: 0x%x", ntStatus));
        }
    }

        
    RemoveCachedSubdevice(MiniportPair->WaveName);

    ntStatus = UnregisterSubdevice(UnknownWave);
    if (!NT_SUCCESS(ntStatus))
    {
        DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(wave) failed: 0x%x", ntStatus));
    }

    RemoveCachedSubdevice(MiniportPair->TopoName);

    ntStatus = UnregisterSubdevice(UnknownTopology);
    if (!NT_SUCCESS(ntStatus))
    {
        DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(topology) failed: 0x%x", ntStatus));
    }

    //
    // All Done.
    //
    ntStatus = STATUS_SUCCESS;
    
    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::GetFilters
(
    _In_        PENDPOINT_MINIPAIR  MiniportPair,
    _Out_opt_   PUNKNOWN *          UnknownTopologyPort,
    _Out_opt_   PUNKNOWN *          UnknownTopologyMiniport,
    _Out_opt_   PUNKNOWN *          UnknownWavePort,
    _Out_opt_   PUNKNOWN *          UnknownWaveMiniport
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::GetFilters]"));
    
    NTSTATUS    ntStatus   = STATUS_SUCCESS; 
    PUNKNOWN            unknownTopologyPort     = NULL;
    PUNKNOWN            unknownTopologyMiniport = NULL;
    PUNKNOWN            unknownWavePort         = NULL;
    PUNKNOWN            unknownWaveMiniport     = NULL;

    // if the client requested the topology filter, find it and return it
    if (UnknownTopologyPort != NULL || UnknownTopologyMiniport != NULL)
    {
        ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopologyPort, &unknownTopologyMiniport);
        if (NT_SUCCESS(ntStatus))
        {
            if (UnknownTopologyPort)
            {
                *UnknownTopologyPort = unknownTopologyPort;
            }

            if (UnknownTopologyMiniport)
            {
                *UnknownTopologyMiniport = unknownTopologyMiniport;
            }
        }
    }

    // if the client requested the wave filter, find it and return it
    if (NT_SUCCESS(ntStatus) && (UnknownWavePort != NULL || UnknownWaveMiniport != NULL))
    {
        ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWavePort, &unknownWaveMiniport);
        if (NT_SUCCESS(ntStatus))
        {
            if (UnknownWavePort)
            {
                *UnknownWavePort = unknownWavePort;
            }

            if (UnknownWaveMiniport)
            {
                *UnknownWaveMiniport = unknownWaveMiniport;
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CAdapterCommon::SetIdlePowerManagement
(
  _In_  PENDPOINT_MINIPAIR  MiniportPair,
  _In_  BOOL bEnabled
)
{
    PAGED_CODE();
    DPF_ENTER(("[CAdapterCommon::SetIdlePowerManagement]"));

    NTSTATUS      ntStatus   = STATUS_SUCCESS; 
    IUnknown      *pUnknown = NULL;
    PPORTCLSPOWER pPortClsPower = NULL;
    // refcounting disable requests. Each miniport is responsible for calling this in pairs,
    // disable on the first request to disable, enable on the last request to enable.

    // make sure that we always call SetIdlePowerManagment using the IPortClsPower
    // from the requesting port, so we don't cache a reference to a port
    // indefinitely, preventing it from ever unloading.
    ntStatus = GetFilters(MiniportPair, NULL, NULL, &pUnknown, NULL);
    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = 
            pUnknown->QueryInterface
            (
                IID_IPortClsPower,
                (PVOID*) &pPortClsPower
            );
    }

    if (NT_SUCCESS(ntStatus))
    {
        if (bEnabled)
        {
            m_dwIdleRequests--;

            if (0 == m_dwIdleRequests)
            {
                pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, TRUE);
            }
        }
        else
        {
            if (0 == m_dwIdleRequests)
            {
                pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, FALSE);
            }

            m_dwIdleRequests++;
        }
    }

    SAFE_RELEASE(pUnknown);
    SAFE_RELEASE(pPortClsPower);

    return ntStatus;
}

#pragma code_seg("PAGE")
NTSTATUS
CopyRegistryValues(HANDLE _hSourceKey, HANDLE _hDestinationKey)
/*++

Routine Description:

  This method copies the registry values in _hSourceKey to _hDestinationKey.

Return Value:

  NT status code.

--*/
{
    NTSTATUS                    ntStatus = STATUS_SUCCESS;
    PKEY_VALUE_FULL_INFORMATION kvFullInfo = NULL;
    ULONG                       ulFullInfoLength = 0;
    ULONG                       ulFullInfoResultLength = 0;
    PWSTR                       pwstrKeyValueName = NULL;
    UNICODE_STRING              strKeyValueName;
    PAGED_CODE();
    // Allocate the KEY_VALUE_FULL_INFORMATION structure
    ulFullInfoLength = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH;
    kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG);
    IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit);

    // Iterate over each value and copy it to the destination
    for (UINT i = 0; NT_SUCCESS(ntStatus); i++)
    {
        // Enumerate the next value
        ntStatus = ZwEnumerateValueKey(_hSourceKey, i, KeyValueFullInformation, kvFullInfo, ulFullInfoLength, &ulFullInfoResultLength);

        // Jump out of this loop if there are no more values
        IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, Exit);

        // Handle incorrect buffer size
        if (ntStatus == STATUS_BUFFER_TOO_SMALL || ntStatus == STATUS_BUFFER_OVERFLOW)
        {
            // Free and re-allocate the KEY_VALUE_FULL_INFORMATION structure with the correct size
            ExFreePoolWithTag(kvFullInfo, MINADAPTER_POOLTAG);

            ulFullInfoLength = ulFullInfoResultLength;

            kvFullInfo = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulFullInfoLength, MINADAPTER_POOLTAG);
            IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit);

            // Try to enumerate the current value again
            ntStatus = ZwEnumerateValueKey(_hSourceKey, i, KeyValueFullInformation, kvFullInfo, ulFullInfoLength, &ulFullInfoResultLength);

            // Jump out of this loop if there are no more values
            IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, Exit);
            IF_FAILED_JUMP(ntStatus, loop_exit);
        }
        else
        {
            IF_FAILED_JUMP(ntStatus, loop_exit);
        }

        // Allocate the key value name string
        pwstrKeyValueName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kvFullInfo->NameLength + sizeof(WCHAR)*2, MINADAPTER_POOLTAG);
        IF_TRUE_ACTION_JUMP(kvFullInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit);

        // Copy the key value name from the full information struct
        RtlStringCbCopyNW(pwstrKeyValueName, kvFullInfo->NameLength + sizeof(WCHAR)*2, kvFullInfo->Name, kvFullInfo->NameLength);

        // Make sure the string is null terminated
        pwstrKeyValueName[(kvFullInfo->NameLength) / sizeof(WCHAR)] = 0;

        // Copy the key value name string to a UNICODE string
        RtlInitUnicodeString(&strKeyValueName, pwstrKeyValueName);

        // Write the key value from the source into the destination
        ntStatus = ZwSetValueKey(_hDestinationKey, &strKeyValueName, 0, kvFullInfo->Type, (PVOID)((PUCHAR)kvFullInfo + kvFullInfo->DataOffset), kvFullInfo->DataLength);
        IF_FAILED_JUMP(ntStatus, loop_exit);

    loop_exit:
        // Free the key value name string
        if (pwstrKeyValueName)
        {
            ExFreePoolWithTag(pwstrKeyValueName, MINADAPTER_POOLTAG);
        }

        // Bail if anything failed
        IF_FAILED_JUMP(ntStatus, Exit);
    }

Exit:
    // Free the KEY_VALUE_FULL_INFORMATION structure
    if (kvFullInfo)
    {
        ExFreePoolWithTag(kvFullInfo, MINADAPTER_POOLTAG);
    }

    return ntStatus;
}

NTSTATUS
CopyRegistryKey(HANDLE _hSourceKey, HANDLE _hDestinationKey, BOOL _bOverwrite = FALSE)
/*++

Routine Description:

  This method recursively copies the registry values in _hSourceKey to _hDestinationKey.
  Set _bOverwrite to indicate whether the first level values are copied or not.
  Normal use is to set false for the initial call, and then all sub paths will be copied.

Return Value:

  NT status code.

--*/
{
    NTSTATUS                ntStatus = STATUS_UNSUCCESSFUL;
    PKEY_BASIC_INFORMATION  kBasicInfo = NULL;
    ULONG                   ulBasicInfoLength = 0;
    ULONG                   ulBasicInfoResultLength = 0;
    ULONG                   ulDisposition = 0;
    PWSTR                   pwstrKeyName = NULL;
    UNICODE_STRING          strKeyName;
    OBJECT_ATTRIBUTES       hCurrentSourceKeyAttributes;
    OBJECT_ATTRIBUTES       hNewDestinationKeyAttributes;
    HANDLE                  hCurrentSourceKey = NULL;
    HANDLE                  hNewDestinationKey = NULL;
    PAGED_CODE();
    // Validate parameters
    IF_TRUE_ACTION_JUMP(_hSourceKey == nullptr, ntStatus = STATUS_INVALID_PARAMETER, Exit);
    IF_TRUE_ACTION_JUMP(_hDestinationKey == nullptr, ntStatus = STATUS_INVALID_PARAMETER, Exit);

    // Allocate the KEY_BASIC_INFORMATION structure
    ulBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_DEVICE_REG_KEY_LENGTH;
    kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG);
    IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, Exit);

    ntStatus = STATUS_SUCCESS;
    // Iterate over each key and copy it
    for (UINT i = 0; NT_SUCCESS(ntStatus); i++)
    {
        // Enumerate the next key
        ntStatus = ZwEnumerateKey(_hSourceKey, i, KeyBasicInformation, kBasicInfo, ulBasicInfoLength, &ulBasicInfoResultLength);

        // Jump out of this loop if there are no more keys
        IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, copy_values);

        // Handle incorrect buffer size
        if (ntStatus == STATUS_BUFFER_TOO_SMALL || ntStatus == STATUS_BUFFER_OVERFLOW)
        {
            // Free and re-allocate the KEY_BASIC_INFORMATION structure with the correct size.
            ExFreePoolWithTag(kBasicInfo, MINADAPTER_POOLTAG);
            ulBasicInfoLength = ulBasicInfoResultLength;
            kBasicInfo = (PKEY_BASIC_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, ulBasicInfoLength, MINADAPTER_POOLTAG);
            IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit);

            // Try to enumerate the current key again.
            ntStatus = ZwEnumerateKey(_hSourceKey, i, KeyBasicInformation, kBasicInfo, ulBasicInfoLength, &ulBasicInfoResultLength);

            // Jump out of this loop if there are no more keys
            IF_TRUE_ACTION_JUMP(ntStatus == STATUS_NO_MORE_ENTRIES, ntStatus = STATUS_SUCCESS, copy_values);
            IF_FAILED_JUMP(ntStatus, loop_exit);
        }
        else
        {
            IF_FAILED_JUMP(ntStatus, loop_exit);
        }

        // Allocate the key name string 
        pwstrKeyName = (PWSTR)ExAllocatePool2(POOL_FLAG_NON_PAGED, kBasicInfo->NameLength + sizeof(WCHAR), MINADAPTER_POOLTAG);
        IF_TRUE_ACTION_JUMP(kBasicInfo == NULL, ntStatus = STATUS_INSUFFICIENT_RESOURCES, loop_exit);

        // Copy the key name from the basic information struct
        RtlStringCbCopyNW(pwstrKeyName, kBasicInfo->NameLength + sizeof(WCHAR), kBasicInfo->Name, kBasicInfo->NameLength);

        // Make sure the string is null terminated
        pwstrKeyName[(kBasicInfo->NameLength) / sizeof(WCHAR)] = 0;

        // Copy the key name string to a UNICODE string
        RtlInitUnicodeString(&strKeyName, pwstrKeyName);

        // Initialize attributes to open the currently enumerated source key
        InitializeObjectAttributes(&hCurrentSourceKeyAttributes, &strKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, _hSourceKey, NULL);

        // Open the currently enumerated source key
        ntStatus = ZwOpenKey(&hCurrentSourceKey, KEY_READ, &hCurrentSourceKeyAttributes);
        IF_FAILED_ACTION_JUMP(ntStatus, ZwClose(hCurrentSourceKey), loop_exit);

        // Initialize attributes to create the new destination key
        InitializeObjectAttributes(&hNewDestinationKeyAttributes, &strKeyName, OBJ_KERNEL_HANDLE, _hDestinationKey, NULL);

        // Create the key at the destination
        ntStatus = ZwCreateKey(&hNewDestinationKey, KEY_WRITE, &hNewDestinationKeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &ulDisposition);
        IF_FAILED_ACTION_JUMP(ntStatus, ZwClose(hCurrentSourceKey), loop_exit);

        // Now copy the contents of the currently enumerated key to the destination
        ntStatus = CopyRegistryKey(hCurrentSourceKey, hNewDestinationKey, TRUE);
        IF_FAILED_JUMP(ntStatus, loop_exit);

    loop_exit:
        // Free the key name string
        if (pwstrKeyName)
        {
            ExFreePoolWithTag(pwstrKeyName, MINADAPTER_POOLTAG);
        }

        // Close the current source key
        if (hCurrentSourceKey)
        {
            ZwClose(hCurrentSourceKey);
        }

        // Close the new destination key
        if (hNewDestinationKey)
        {
            ZwClose(hNewDestinationKey);
        }

        // Bail if anything failed
        IF_FAILED_JUMP(ntStatus, Exit);
    }

copy_values:
    // Copy the values 
    if (_bOverwrite)
    {
        ntStatus = CopyRegistryValues(_hSourceKey, _hDestinationKey);
        IF_FAILED_JUMP(ntStatus, Exit);
    }

Exit:
    // Free the basic information structure
    if (kBasicInfo)
    {
        ExFreePoolWithTag(kBasicInfo, MINADAPTER_POOLTAG);
    }
    return ntStatus;
}


NTSTATUS CAdapterCommon::MigrateDeviceInterfaceTemplateParameters
(
    _In_ PUNICODE_STRING    SymbolicLinkName,
    _In_opt_ PCWSTR         TemplateReferenceString
)
/*++

Routine Description:

  This method copies all of the properties from the template interface,
  which is specified in the inf, to the actual interface being used which
  may be dynamically generated at run time. This allows for a driver 
  to reuse a single inf entry for multiple audio endpoints. The primary 
  purpose for this is to allow for sideband audio endpoints to dynamically
  generate the reference string at run time, tied to the peripheral connected,
  while still having a simple static inf entry for setting up apo's or other 
  parameters.

  For example, if you have an interface in your inf defined with reference string
  "SpeakerWave". At runtime you could generate "SpeakerWave-1234ABCDE", and specify
  "SpeakerWave" as the template name. When "SpeakerWave-1234ABCDE" is installed
  we will copy all of the parameters that were specified in the inf for "SpeakerWave"
  over to "SpeakerWave-1234ABCDE". You simply need to specify "SpeakerWave" as the
  "TemplateName" in the ENDPOINT_MINIPAIRS.

  By default, the first level of registry keys are not copied. Only the 2nd level and
  deeper are copied. This way the friendly name and other PNP properties will not
  be modified, but the EP and FX properties will be copied.
  
Return Value:

  NT status code.

--*/

{
    NTSTATUS            ntStatus = STATUS_SUCCESS;
    HANDLE              hDeviceInterfaceParametersKey(NULL);
    HANDLE              hTemplateDeviceInterfaceParametersKey(NULL);
    UNICODE_STRING      TemplateSymbolicLinkName;
    UNICODE_STRING      referenceString;

    RtlInitUnicodeString(&TemplateSymbolicLinkName, NULL);
    RtlInitUnicodeString(&referenceString, TemplateReferenceString);

    //
    // Register an audio interface if not already present for the template interface, so we can access
    // the registry path. If it's already registered, this simply returns the symbolic link name. 
    // No need to unregister it (there is no mechanism to), and we'll never make it active.
    //
    ntStatus = IoRegisterDeviceInterface(
        GetPhysicalDeviceObject(),
        &KSCATEGORY_AUDIO,
        &referenceString,
        &TemplateSymbolicLinkName);

    // Open the template device interface's registry key path
    ntStatus = IoOpenDeviceInterfaceRegistryKey(&TemplateSymbolicLinkName, GENERIC_READ, &hTemplateDeviceInterfaceParametersKey);
    IF_FAILED_JUMP(ntStatus, Exit);

    // Open the new device interface's registry key path that we plan to activate
    ntStatus = IoOpenDeviceInterfaceRegistryKey(SymbolicLinkName, GENERIC_WRITE, &hDeviceInterfaceParametersKey);
    IF_FAILED_JUMP(ntStatus, Exit);

    // Copy the template device parameters key to the device interface key
    ntStatus = CopyRegistryKey(hTemplateDeviceInterfaceParametersKey, hDeviceInterfaceParametersKey);
    IF_FAILED_JUMP(ntStatus, Exit);

Exit:
    RtlFreeUnicodeString(&TemplateSymbolicLinkName);

    if (hTemplateDeviceInterfaceParametersKey)
    {
        ZwClose(hTemplateDeviceInterfaceParametersKey);
    }

    if (hDeviceInterfaceParametersKey)
    {
        ZwClose(hDeviceInterfaceParametersKey);
    }

    return ntStatus;
}


<|start_filename|>gnss/gnssUmdf/FixHandler.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    Fixhandler.h

Abstract:

    This module contains the type definitions of the fix logic of the Umdf Gnss Driver.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

class CFixSession;

class CFixHandler
{
public:
    NTSTATUS Initialize(_In_ WDFQUEUE Queue);

    NTSTATUS StartFix(_In_ WDFREQUEST Request, _In_ BOOL LocationServiceEnabled);
    NTSTATUS StopFix(_In_ WDFREQUEST Request);
    NTSTATUS GetFixRequest(_In_ WDFREQUEST Request);
    NTSTATUS ModifyFix(_In_ WDFREQUEST Request);
    NTSTATUS StopExistingSinglsShotFix();

private:
    // Session objects (single shot, distance tracking etc.)
    CFixSession _SingleShotSession;

    ULONG _CurrentSinglsShotSessionId = INVALID_SESSION_ID;
};


<|start_filename|>audio/simpleaudiosample/Source/Inc/endpoints.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    endpoints.h

Abstract:

    Node and Pin numbers and other common definitions for simple audio sample.
--*/

#ifndef _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_
#define _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_

// Name Guid
// {0104947F-82AE-4291-A6F3-5E2DE1AD7DC2}
#define STATIC_NAME_SIMPLE_AUDIO_SAMPLE\
    0x104947f, 0x82ae, 0x4291, 0xa6, 0xf3, 0x5e, 0x2d, 0xe1, 0xad, 0x7d, 0xc2
DEFINE_GUIDSTRUCT("0104947F-82AE-4291-A6F3-5E2DE1AD7DC2", NAME_SIMPLE_AUDIO_SAMPLE);
#define NAME_SIMPLE_AUDIO_SAMPLE DEFINE_GUIDNAMED(NAME_SIMPLE_AUDIO_SAMPLE)

//----------------------------------------------------
// New defines for the render endpoints.
//----------------------------------------------------

// Default pin instances.
#define MAX_INPUT_SYSTEM_STREAMS        1

// Wave pins - no mix, no offload
enum 
{
    KSPIN_WAVE_RENDER3_SINK_SYSTEM = 0, 
    KSPIN_WAVE_RENDER3_SOURCE
};

// Wave pins - offloading is NOT supported.
enum 
{
    KSPIN_WAVE_RENDER2_SINK_SYSTEM = 0, 
    KSPIN_WAVE_RENDER2_SINK_LOOPBACK, 
    KSPIN_WAVE_RENDER2_SOURCE
};

// Wave Topology nodes - offloading is NOT supported.
enum 
{
    KSNODE_WAVE_SUM = 0,
    KSNODE_WAVE_VOLUME,
    KSNODE_WAVE_MUTE,
    KSNODE_WAVE_PEAKMETER
};

// Topology pins.
enum
{
    KSPIN_TOPO_WAVEOUT_SOURCE = 0,
    KSPIN_TOPO_LINEOUT_DEST,
};

// Topology nodes.
enum
{
    KSNODE_TOPO_WAVEOUT_VOLUME = 0,
    KSNODE_TOPO_WAVEOUT_MUTE,
    KSNODE_TOPO_WAVEOUT_PEAKMETER
};

//----------------------------------------------------
// New defines for the capture endpoints.
//----------------------------------------------------

// Default pin instances.
#define MAX_INPUT_STREAMS           1       // Number of capture streams.

// Wave pins
enum 
{
    KSPIN_WAVE_BRIDGE = 0,
    KSPIN_WAVEIN_HOST,
};

// Wave Topology nodes.
enum 
{
    KSNODE_WAVE_ADC = 0
};

// Wave Topology nodes.
enum 
{
    KSNODE_WAVE_DAC = 0
};

// topology pins.
enum
{
    KSPIN_TOPO_MIC_ELEMENTS,
    KSPIN_TOPO_BRIDGE
};

// topology nodes.
enum
{
    KSNODE_TOPO_VOLUME,
    KSNODE_TOPO_MUTE,
    KSNODE_TOPO_PEAKMETER
};

// data format attribute range definitions.
static
KSATTRIBUTE PinDataRangeSignalProcessingModeAttribute =
{
    sizeof(KSATTRIBUTE),
    0,
    STATICGUIDOF(KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE),
};

static
PKSATTRIBUTE PinDataRangeAttributes[] =
{
    &PinDataRangeSignalProcessingModeAttribute,
};

static
KSATTRIBUTE_LIST PinDataRangeAttributeList =
{
    ARRAYSIZE(PinDataRangeAttributes),
    PinDataRangeAttributes,
};

#endif // _SIMPLEAUDIOSAMPLE_ENDPOINTS_H_


<|start_filename|>storage/class/classpnp/src/classp.h<|end_filename|>
/*++

Copyright (C) Microsoft Corporation, 1991 - 2010

Module Name:

    classp.h

Abstract:

    Private header file for classpnp.sys modules.  This contains private
    structure and function declarations as well as constant values which do
    not need to be exported.

Author:

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#define RTL_USE_AVL_TABLES 0
#define POOL_ZERO_DOWN_LEVEL_SUPPORT

#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>

#include <ntddk.h>

#include <scsi.h>

#include <wmidata.h>
#include <classpnp.h>
#include <storduid.h>

#if CLASS_INIT_GUID
#include <initguid.h>
#endif

#include <mountdev.h>
#include <ioevent.h>
#include <ntstrsafe.h>
#include <ntintsafe.h>

#include <wdmguid.h>

#if (NTDDI_VERSION >= NTDDI_WIN8)

#include <ntpoapi.h>

#include <srbhelper.h>

#endif

//
// Set component ID for DbgPrintEx calls
//
#ifndef DEBUG_COMP_ID
#define DEBUG_COMP_ID   DPFLTR_CLASSPNP_ID
#endif

//
// Include header file and setup GUID for tracing
//
#include <storswtr.h>
#define WPP_GUID_CLASSPNP       (FA8DE7C4, ACDE, 4443, 9994, C4E2359A9EDB)
#ifndef WPP_CONTROL_GUIDS
#define WPP_CONTROL_GUIDS       WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_CLASSPNP)
#endif

/*
 *  IA64 requires 8-byte alignment for pointers, but the IA64 NT kernel expects 16-byte alignment
 */
#ifdef _WIN64
    #define PTRALIGN                DECLSPEC_ALIGN(16)
#else
    #define PTRALIGN
#endif


extern CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[];

extern GUID ClassGuidQueryRegInfoEx;
extern GUID ClassGuidSenseInfo2;
extern GUID ClassGuidWorkingSet;
extern GUID ClassGuidSrbSupport;

extern ULONG ClassMaxInterleavePerCriticalIo;


#define Add2Ptr(P,I)                                ((PVOID)((PUCHAR)(P) + (I)))

#define CLASSP_REG_SUBKEY_NAME                      (L"Classpnp")

#define CLASSP_REG_HACK_VALUE_NAME                  (L"HackMask")
#define CLASSP_REG_MMC_DETECTION_VALUE_NAME         (L"MMCDetectionState")
#define CLASSP_REG_WRITE_CACHE_VALUE_NAME           (L"WriteCacheEnableOverride")
#define CLASSP_REG_PERF_RESTORE_VALUE_NAME          (L"RestorePerfAtCount")
#define CLASSP_REG_REMOVAL_POLICY_VALUE_NAME        (L"UserRemovalPolicy")
#define CLASSP_REG_IDLE_INTERVAL_NAME               (L"IdleInterval")
#define CLASSP_REG_IDLE_ACTIVE_MAX                  (L"IdleOutstandingIoMax")
#define CLASSP_REG_IDLE_PRIORITY_SUPPORTED          (L"IdlePrioritySupported")
#define CLASSP_REG_ACCESS_ALIGNMENT_NOT_SUPPORTED   (L"AccessAlignmentQueryNotSupported")
#define CLASSP_REG_DISBALE_IDLE_POWER_NAME          (L"DisableIdlePowerManagement")
#define CLASSP_REG_IDLE_TIMEOUT_IN_SECONDS          (L"IdleTimeoutInSeconds")
#define CLASSP_REG_DISABLE_D3COLD                   (L"DisableD3Cold")
#define CLASSP_REG_QERR_OVERRIDE_MODE               (L"QERROverrideMode")
#define CLASSP_REG_LEGACY_ERROR_HANDLING            (L"LegacyErrorHandling")
#define CLASSP_REG_COPY_OFFLOAD_MAX_TARGET_DURATION (L"CopyOffloadMaxTargetDuration")

#define CLASS_PERF_RESTORE_MINIMUM                  (0x10)
#define CLASS_ERROR_LEVEL_1                         (0x4)
#define CLASS_ERROR_LEVEL_2                         (0x8)
#define CLASS_MAX_INTERLEAVE_PER_CRITICAL_IO        (0x4)

#define FDO_HACK_CANNOT_LOCK_MEDIA                  (0x00000001)
#define FDO_HACK_GESN_IS_BAD                        (0x00000002)
#define FDO_HACK_NO_SYNC_CACHE                      (0x00000004)
#define FDO_HACK_NO_RESERVE6                        (0x00000008)
#define FDO_HACK_GESN_IGNORE_OPCHANGE               (0x00000010)

#define FDO_HACK_VALID_FLAGS                        (0x0000001F)
#define FDO_HACK_INVALID_FLAGS                      (~FDO_HACK_VALID_FLAGS)

/*
 *  Lots of retries of synchronized SCSI commands that devices may not
 *  even support really slows down the system (especially while booting).
 *  (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
 *  If a disk cannot return a small initialization buffer at startup
 *  in two attempts (with delay interval) then we cannot expect it to return
 *  data consistently with four retries.
 *  So don't set the retry counts as high here as for data SRBs.
 *
 *  If we find that these requests are failing consecutively,
 *  despite the retry interval, on otherwise reliable media,
 *  then we should either increase the retry interval for
 *  that failure or (by all means) increase these retry counts as appropriate.
 */
#define NUM_LOCKMEDIAREMOVAL_RETRIES    1
#define NUM_MODESENSE_RETRIES           1
#define NUM_MODESELECT_RETRIES          1
#define NUM_DRIVECAPACITY_RETRIES       1
#define NUM_THIN_PROVISIONING_RETRIES   32

#if (NTDDI_VERSION >= NTDDI_WINBLUE)

//
// New code should use the MAXIMUM_RETRIES value.
//
#define NUM_IO_RETRIES MAXIMUM_RETRIES
#define LEGACY_NUM_IO_RETRIES 8

#else

/*
 *  We retry failed I/O requests at 1-second intervals.
 *  In the case of a failure due to bus reset, we want to make sure that we retry after the allowable
 *  reset time.  For SCSI, the allowable reset time is 5 seconds.  ScsiPort queues requests during
 *  a bus reset, which should cause us to retry after the reset is over; but the requests queued in
 *  the miniport are failed all the way back to us immediately.  In any event, in order to make
 *  extra sure that our retries span the allowable reset time, we should retry more than 5 times.
 */
#define NUM_IO_RETRIES      8

#endif // NTDDI_VERSION >= NTDDI_WINBLUE

#define CLASS_FILE_OBJECT_EXTENSION_KEY             'eteP'
#define CLASSP_VOLUME_VERIFY_CHECKED                0x34

#define CLASS_TAG_PRIVATE_DATA                      'CPcS'
#define CLASS_TAG_SENSE2                            '2ScS'
#define CLASS_TAG_WORKING_SET                       'sWcS'
#define CLASSPNP_POOL_TAG_GENERIC                   'pCcS'
#define CLASSPNP_POOL_TAG_TOKEN_OPERATION           'oTcS'
#define CLASSPNP_POOL_TAG_SRB                       'rScS'
#define CLASSPNP_POOL_TAG_VPD                       'pVcS'
#define CLASSPNP_POOL_TAG_LOG_MESSAGE               'mlcS'
#define CLASSPNP_POOL_TAG_ADDITIONAL_DATA           'DAcS'
#define CLASSPNP_POOL_TAG_FIRMWARE                  'wFcS'

//
// Macros related to Token Operation commands
//
#define MAX_LIST_IDENTIFIER                                         MAXULONG
#define NUM_POPULATE_TOKEN_RETRIES                                  1
#define NUM_WRITE_USING_TOKEN_RETRIES                               2
#define NUM_RECEIVE_TOKEN_INFORMATION_RETRIES                       2
#define MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH                   MAXUSHORT
#define MAX_RECEIVE_TOKEN_INFORMATION_PARAMETER_DATA_LENGTH         MAXULONG
#define MAX_TOKEN_TRANSFER_SIZE                                     MAXULONGLONG
#define MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR         MAXULONG
#define DEFAULT_MAX_TARGET_DURATION                                 4 // 4sec
#define DEFAULT_MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN         (64ULL * 1024 * 1024)  // 64MB
#define MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN                 (256ULL * 1024 * 1024) // 256MB
#define MIN_TOKEN_LIST_IDENTIFIERS                                  256
#define MAX_TOKEN_LIST_IDENTIFIERS                                  MAXULONG
#define MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS                         64

#define REG_DISK_CLASS_CONTROL                                      L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\DISK"
#define REG_MAX_LIST_IDENTIFIER_VALUE                               L"MaximumListIdentifier"

#define VPD_PAGE_HEADER_SIZE                                        0x04


//
// Number of times to retry get LBA status in case of an error
// that can be caused by VPD data change
//

#define GET_LBA_STATUS_RETRY_COUNT_MAX                              (2)

extern ULONG MaxTokenOperationListIdentifier;
extern volatile ULONG TokenOperationListIdentifier;

extern LIST_ENTRY IdlePowerFDOList;
extern PVOID PowerSettingNotificationHandle;
extern PVOID ScreenStateNotificationHandle;
extern BOOLEAN ClasspScreenOff;
extern KGUARDED_MUTEX IdlePowerFDOListMutex;
extern ULONG DiskIdleTimeoutInMS;

//
// Definitions from ntos\rtl\time.c
//

extern CONST LARGE_INTEGER Magic10000;
#define SHIFT10000               13

//
// Constant to help wih various time conversions
//
#define CONST_MSECS_PER_SEC                 1000

#define Convert100nsToMilliseconds(LARGE_INTEGER)                       \
    (                                                                   \
    RtlExtendedMagicDivide((LARGE_INTEGER), Magic10000, SHIFT10000)     \
    )

#define ConvertMillisecondsTo100ns(MILLISECONDS) (                      \
    RtlExtendedIntegerMultiply ((MILLISECONDS), 10000)                  \
    )

typedef struct _MEDIA_CHANGE_DETECTION_INFO {

    //
    // Mutex to synchronize enable/disable requests and media state changes
    //

    KMUTEX MediaChangeMutex;

    //
    // The current state of the media (present, not present, unknown)
    // protected by MediaChangeSynchronizationEvent
    //

    MEDIA_CHANGE_DETECTION_STATE MediaChangeDetectionState;

    //
    // This is a count of how many time MCD has been disabled.  if it is
    // set to zero, then we'll poll the device for MCN events with the
    // then-current method (ie. TEST UNIT READY or GESN).  this is
    // protected by MediaChangeMutex
    //

    LONG MediaChangeDetectionDisableCount;


    //
    // The timer value to support media change events.  This is a countdown
    // value used to determine when to poll the device for a media change.
    // The max value for the timer is 255 seconds.  This is not protected
    // by an event -- simply InterlockedExchanged() as needed.
    //

    LONG MediaChangeCountDown;

    //
    // recent changes allowed instant retries of the MCN irp.  Since this
    // could cause an infinite loop, keep a count of how many times we've
    // retried immediately so that we can catch if the count exceeds an
    // arbitrary limit.
    //

    LONG MediaChangeRetryCount;

    //
    // use GESN if it's available
    //

    struct {
        BOOLEAN Supported;
        BOOLEAN HackEventMask;
        UCHAR   EventMask;
        UCHAR   NoChangeEventMask;
        PUCHAR  Buffer;
        PMDL    Mdl;
        ULONG   BufferSize;
    } Gesn;

    //
    // If this value is one, then the irp is currently in use.
    // If this value is zero, then the irp is available.
    // Use InterlockedCompareExchange() to set from "available" to "in use".
    // ASSERT that InterlockedCompareExchange() showed previous value of
    //    "in use" when changing back to "available" state.
    // This also implicitly protects the MediaChangeSrb and SenseBuffer
    //

    LONG MediaChangeIrpInUse;

    //
    // Pointer to the irp to be used for media change detection.
    // protected by Interlocked MediaChangeIrpInUse
    //

    PIRP MediaChangeIrp;

    //
    // The srb for the media change detection.
    // protected by Interlocked MediaChangeIrpInUse
    //

#if (NTDDI_VERSION >= NTDDI_WIN8)
    union {
        SCSI_REQUEST_BLOCK Srb;
        STORAGE_REQUEST_BLOCK SrbEx;
        UCHAR SrbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
    } MediaChangeSrb;
#else
    SCSI_REQUEST_BLOCK MediaChangeSrb;
#endif
    PUCHAR SenseBuffer;
    ULONG SrbFlags;

    //
    // Second timer to keep track of how long the media change IRP has been
    // in use.  If this value exceeds the timeout (#defined) then we should
    // print out a message to the user and set the MediaChangeIrpLost flag
    // protected by using Interlocked() operations in ClasspSendMediaStateIrp,
    // the only routine which should modify this value.
    //

    LONG MediaChangeIrpTimeInUse;

    //
    // Set by CdRomTickHandler when we determine that the media change irp has
    // been lost
    //

    BOOLEAN MediaChangeIrpLost;

    //
    // Buffer size of SenseBuffer
    //
    UCHAR SenseBufferLength;

} MEDIA_CHANGE_DETECTION_INFO, *PMEDIA_CHANGE_DETECTION_INFO;

typedef enum {
    SimpleMediaLock,
    SecureMediaLock,
    InternalMediaLock
} MEDIA_LOCK_TYPE, *PMEDIA_LOCK_TYPE;

typedef struct _FAILURE_PREDICTION_INFO {
    FAILURE_PREDICTION_METHOD Method;
    ULONG CountDown;                // Countdown timer
    ULONG Period;                   // Countdown period

    PIO_WORKITEM WorkQueueItem;

    KEVENT Event;

    //
    // Timestamp of last time the failure prediction info was queried.
    //
    LARGE_INTEGER LastFailurePredictionQueryTime;

} FAILURE_PREDICTION_INFO, *PFAILURE_PREDICTION_INFO;



//
// This struct must always fit within four PVOIDs of info,
// as it uses the irp's "PVOID DriverContext[4]" to store
// this info
//
typedef struct _CLASS_RETRY_INFO {
    struct _CLASS_RETRY_INFO *Next;
} CLASS_RETRY_INFO, *PCLASS_RETRY_INFO;

typedef struct _CSCAN_LIST {

    //
    // The current block which has an outstanding request.
    //

    ULONGLONG BlockNumber;

    //
    // The list of blocks past the CurrentBlock to which we're going to do
    // i/o.  This list is maintained in sorted order.
    //

    LIST_ENTRY CurrentSweep;

    //
    // The list of blocks behind the current block for which we'll have to
    // wait until the next scan across the disk.  This is kept as a stack,
    // the cost of sorting it is taken when it's moved over to be the
    // running list.
    //

    LIST_ENTRY NextSweep;

} CSCAN_LIST, *PCSCAN_LIST;

//
// add to the front of this structure to help prevent illegal
// snooping by other utilities.
//



typedef enum _CLASS_DETECTION_STATE {
    ClassDetectionUnknown = 0,
    ClassDetectionUnsupported = 1,
    ClassDetectionSupported = 2
} CLASS_DETECTION_STATE, *PCLASS_DETECTION_STATE;

#if _MSC_VER >= 1600
#pragma warning(push)
#endif
#pragma warning(disable:4214) // bit field types other than int
//
// CLASS_ERROR_LOG_DATA will still use SCSI_REQUEST_BLOCK even
// when using extended SRB as an extended SRB is too large to
// fit into. Should revisit this code once classpnp starts to
// use greater than 16 byte CDB.
//
typedef struct _CLASS_ERROR_LOG_DATA {
    LARGE_INTEGER TickCount;        // Offset 0x00
    ULONG PortNumber;               // Offset 0x08

    UCHAR ErrorPaging    : 1;       // Offset 0x0c
    UCHAR ErrorRetried   : 1;
    UCHAR ErrorUnhandled : 1;
    UCHAR ErrorReserved  : 5;

    UCHAR Reserved[3];

    SCSI_REQUEST_BLOCK Srb;     // Offset 0x10

    /*
     *  We define the SenseData as the default length.
     *  Since the sense data returned by the port driver may be longer,
     *  SenseData must be at the end of this structure.
     *  For our internal error log, we only log the default length.
     */
    SENSE_DATA SenseData;     // Offset 0x50 for x86 (or 0x68 for ia64) (ULONG32 Alignment required!)

} CLASS_ERROR_LOG_DATA, *PCLASS_ERROR_LOG_DATA;
#if _MSC_VER >= 1600
#pragma warning(pop)
#endif

#define NUM_ERROR_LOG_ENTRIES   16
#define DBG_NUM_PACKET_LOG_ENTRIES (64*2)   // 64 send&receive's

#if (NTDDI_VERSION >= NTDDI_WIN8)
typedef
VOID
(*PCONTINUATION_ROUTINE)(
    _In_ PVOID Context
    );
#endif

typedef struct _TRANSFER_PACKET {

        LIST_ENTRY AllPktsListEntry;    // entry in fdoData's static AllTransferPacketsList
        SLIST_ENTRY SlistEntry;         // for when in free list (use fast slist)

        PIRP Irp;
        PDEVICE_OBJECT Fdo;

        /*
         *  This is the client IRP that this TRANSFER_PACKET is currently
         *  servicing.
         */
        PIRP OriginalIrp;
        BOOLEAN CompleteOriginalIrpWhenLastPacketCompletes;

        /*
         *  Stuff for retrying the transfer.
         */
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
        UCHAR NumRetries; // Total number of retries remaining.
        UCHAR NumThinProvisioningRetries; //Number of retries carried out so far for a request failed with THIN_PROVISIONING_SOFT_THRESHOLD_ERROR
        UCHAR NumIoTimeoutRetries; // Number of retries remaining for a timed-out request.
        UCHAR TimedOut; // Indicates if this packet has timed-out.
#else
        ULONG NumRetries;
#endif
        KTIMER RetryTimer;
        KDPC RetryTimerDPC;

        _Field_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
        LONGLONG RetryIn100nsUnits;

        /*
         *  Event for synchronizing the transfer (optional).
         *  (Note that we can't have the event in the packet itself because
         *  by the time a thread waits on an event the packet may have
         *  been completed and re-issued.
         */
        PKEVENT SyncEventPtr;

        /*
         *  Stuff for retrying during extreme low-memory stress
         *  (when we retry 1 page at a time).
         *  NOTE: These fields are also used for StartIO-based
         *  class drivers, even when not in low memory conditions.
         */
        BOOLEAN DriverUsesStartIO; // if this is set, then the below low-mem flags are always used
        BOOLEAN InLowMemRetry;
        PUCHAR LowMemRetry_remainingBufPtr;
        ULONG LowMemRetry_remainingBufLen;
        LARGE_INTEGER LowMemRetry_nextChunkTargetLocation;

        /*
         *  Fields used for cancelling the packet.
         */
        // BOOLEAN Cancelled;
        // KEVENT CancelledEvent;

        /*
         *  We keep the buffer and length values here as well
         *  as in the SRB because some miniports return
         *  the transferred length in SRB.DataTransferLength,
         *  and if the SRB failed we need that value again for the retry.
         *  We don't trust the lower stack to preserve any of these values in the SRB.
         */
        PUCHAR BufPtrCopy;
        ULONG BufLenCopy;
        LARGE_INTEGER TargetLocationCopy;

        /*
         *  This is a standard SCSI structure that receives a detailed
         *  report about a SCSI error on the hardware.
         */
        SENSE_DATA_EX SrbErrorSenseData;

        /*
         *  This is the SRB block for this TRANSFER_PACKET.
         *  For IOCTLs, the SRB block includes two DWORDs for
         *  device object and ioctl code; so these must
         *  immediately follow the SRB block.
         */

#if (NTDDI_VERSION >= NTDDI_WIN8)
        PSTORAGE_REQUEST_BLOCK_HEADER Srb;
#else
        SCSI_REQUEST_BLOCK Srb;
#endif
        // ULONG SrbIoctlDevObj;        // not handling ioctls yet
        // ULONG SrbIoctlCode;

        #if DBG
            LARGE_INTEGER DbgTimeSent;
            LARGE_INTEGER DbgTimeReturned;
            ULONG DbgPktId;
            IRP DbgOriginalIrpCopy;
            MDL DbgMdlCopy;
        #endif

        BOOLEAN UsePartialMdl;
        PMDL PartialMdl;

        PSRB_HISTORY RetryHistory;

        // The time at which this request was sent to port driver.
        ULONGLONG RequestStartTime;

#if (NTDDI_VERSION >= NTDDI_WIN8)
        // ActivityId that is associated with the IRP that this transfer packet services.
        GUID ActivityId;

        // If non-NULL, called at packet completion with this context.
        PCONTINUATION_ROUTINE ContinuationRoutine;
        PVOID ContinuationContext;
        ULONGLONG TransferCount;
        ULONG AllocateNode;
#endif
} TRANSFER_PACKET, *PTRANSFER_PACKET;

/*
 *  MIN_INITIAL_TRANSFER_PACKETS is the minimum number of packets that
 *  we preallocate at startup for each device (we need at least one packet
 *  to guarantee forward progress during memory stress).
 *  MIN_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
 *  we allow to build up and remain for each device;
 *  we _lazily_ work down to this number when they're not needed.
 *  MAX_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
 *  that we _immediately_ reduce to when they are not needed.
 *
 *  The absolute maximum number of packets that we will allocate is
 *  whatever is required by the current activity, up to the memory limit;
 *  as soon as stress ends, we snap down to MAX_WORKINGSET_TRANSFER_PACKETS;
 *  we then lazily work down to MIN_WORKINGSET_TRANSFER_PACKETS.
 */
#define MIN_INITIAL_TRANSFER_PACKETS                        1
#define MIN_WORKINGSET_TRANSFER_PACKETS_Client              16
#define MAX_WORKINGSET_TRANSFER_PACKETS_Client              32
#define MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound   256
#define MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound   32
#define MAX_WORKINGSET_TRANSFER_PACKETS_Server              1024
#define MIN_WORKINGSET_TRANSFER_PACKETS_SPACES              512
#define MAX_WORKINGSET_TRANSFER_PACKETS_SPACES              2048
#define MAX_OUTSTANDING_IO_PER_LUN_DEFAULT                  16
#define MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE                8192



typedef struct _PNL_SLIST_HEADER {
    DECLSPEC_CACHEALIGN SLIST_HEADER SListHeader;
    DECLSPEC_CACHEALIGN ULONG NumFreeTransferPackets;
    ULONG NumTotalTransferPackets;
    ULONG DbgPeakNumTransferPackets;
} PNL_SLIST_HEADER, *PPNL_SLIST_HEADER;

//
// !!! WARNING !!!
// DO NOT use the following structure in code outside of classpnp
// as structure will not be guaranteed between OS versions.
//
// add to the front of this structure to help prevent illegal
// snooping by other utilities.
//
struct _CLASS_PRIVATE_FDO_DATA {

    //
    // The amount of time allowed for a target to complete a copy offload
    // operation, in seconds.  Default is 4s, but it can be modified via
    // registry key.
    //
    ULONG CopyOffloadMaxTargetDuration;


#if (NTDDI_VERSION >= NTDDI_WIN8)

    //
    // Periodic timer for polling for media change detection, failure prediction
    // and class tick function.
    //

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
    PEX_TIMER TickTimer;
    LONGLONG CurrentNoWakeTolerance;
#else
    KTIMER TickTimer;
    KDPC TickTimerDpc;
#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)

    //
    // Power related and release queue SRBs
    //
    union {
        STORAGE_REQUEST_BLOCK SrbEx;
        UCHAR PowerSrbBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
    } PowerSrb;

    union {
        STORAGE_REQUEST_BLOCK SrbEx;
        UCHAR ReleaseQueueSrbBuffer[CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE];
    } ReleaseQueueSrb;

#endif

    ULONG TrackingFlags;

    /*
     * Flag to detect recursion caused by devices
     * reporting different capacity per each request
     */
    ULONG UpdateDiskPropertiesWorkItemActive;

    //
    // Local equivalents of MinWorkingSetTransferPackets and MaxWorkingSetTransferPackets.
    // These values are initialized by the global equivalents but are then adjusted as
    // requested by the class driver.
    //
    ULONG LocalMinWorkingSetTransferPackets;
    ULONG LocalMaxWorkingSetTransferPackets;

#if DBG

    ULONG MaxOutstandingIOPerLUN;

#endif

    /*
     *  Entry in static list used by debug extension to quickly find all class FDOs.
     */
    LIST_ENTRY AllFdosListEntry;

    //
    // this private structure allows us to
    // dynamically re-enable the perf benefits
    // lost due to transient error conditions.
    // in w2k, a reboot was required. :(
    //
    struct {
        ULONG      OriginalSrbFlags;
        ULONG      SuccessfulIO;
        ULONG      ReEnableThreshhold; // 0 means never
    } Perf;

    ULONG_PTR HackFlags;

    STORAGE_HOTPLUG_INFO HotplugInfo;

    // Legacy.  Still used by obsolete legacy code.
    struct {
        LARGE_INTEGER     Delta;       // in ticks
        LARGE_INTEGER     Tick;        // when it should fire
        PCLASS_RETRY_INFO ListHead;    // singly-linked list
        ULONG             Granularity; // static
        KSPIN_LOCK        Lock;        // protective spin lock
        KDPC              Dpc;         // DPC routine object
        KTIMER            Timer;       // timer to fire DPC
    } Retry;

    BOOLEAN TimerInitialized;
    BOOLEAN LoggedTURFailureSinceLastIO;
    BOOLEAN LoggedSYNCFailure;

    //
    // privately allocated release queue irp
    // protected by fdoExtension->ReleaseQueueSpinLock
    //
    BOOLEAN ReleaseQueueIrpAllocated;
    PIRP ReleaseQueueIrp;

    /*
     *  Queues for TRANSFER_PACKETs that contextualize the IRPs and SRBs
     *  that we send down to the port driver.
     *  (The free list is an slist so that we can use fast
     *   interlocked operations on it; but the relatively-static
     *   AllTransferPacketsList list has to be
     *   a doubly-linked list since we have to dequeue from the middle).
     */
    LIST_ENTRY AllTransferPacketsList;
    PPNL_SLIST_HEADER FreeTransferPacketsLists;

    /*
     *  Queue for deferred client irps
     */
    LIST_ENTRY DeferredClientIrpList;

    /*
     *  Precomputed maximum transfer length for the hardware.
     */
    ULONG HwMaxXferLen;

    /*
     *  SCSI_REQUEST_BLOCK template preconfigured with the constant values.
     *  This is slapped into the SRB in the TRANSFER_PACKET for each transfer.
     */

#if (NTDDI_VERSION >= NTDDI_WIN8)
    PSTORAGE_REQUEST_BLOCK_HEADER SrbTemplate;
#else
    SCSI_REQUEST_BLOCK SrbTemplate;
#endif

    KSPIN_LOCK SpinLock;

    /*
     *  For non-removable media, we read the drive capacity at start time and cache it.
     *  This is so that ReadDriveCapacity failures at runtime (e.g. due to memory stress)
     *  don't cause I/O on the paging disk to start failing.
     */
    READ_CAPACITY_DATA_EX LastKnownDriveCapacityData;
    BOOLEAN IsCachedDriveCapDataValid;

    //
    // Idle priority support flag
    //
    BOOLEAN IdlePrioritySupported;

    //
    // Tick timer enabled
    //
    BOOLEAN TickTimerEnabled;

    BOOLEAN ReservedBoolean;

    /*
     *  Circular array of timestamped logs of errors that occurred on this device.
     */
    ULONG ErrorLogNextIndex;
    CLASS_ERROR_LOG_DATA ErrorLogs[NUM_ERROR_LOG_ENTRIES];

    //
    // Number of outstanding critical Io requests from Mm
    //
    ULONG NumHighPriorityPagingIo;

    //
    // Maximum number of normal Io requests that can be interleaved with the critical ones
    //
    ULONG MaxInterleavedNormalIo;

    //
    // The timestamp when entering throttle mode
    //
    LARGE_INTEGER ThrottleStartTime;

    //
    // The timestamp when exiting throttle mode
    //
    LARGE_INTEGER ThrottleStopTime;

    //
    // The longest time ever spent in throttle mode
    //
    LARGE_INTEGER LongestThrottlePeriod;

    #if DBG
        ULONG DbgMaxPktId;

        /*
         *  Logging fields for ForceUnitAccess and Flush
         */
        BOOLEAN DbgInitFlushLogging;         // must reset this to 1 for each logging session
        ULONG DbgNumIORequests;
        ULONG DbgNumFUAs;       // num I/O requests with ForceUnitAccess bit set
        ULONG DbgNumFlushes;    // num SRB_FUNCTION_FLUSH_QUEUE
        ULONG DbgIOsSinceFUA;
        ULONG DbgIOsSinceFlush;
        ULONG DbgAveIOsToFUA;      // average number of I/O requests between FUAs
        ULONG DbgAveIOsToFlush;   // ...
        ULONG DbgMaxIOsToFUA;
        ULONG DbgMaxIOsToFlush;
        ULONG DbgMinIOsToFUA;
        ULONG DbgMinIOsToFlush;

        /*
         *  Debug log of previously sent packets (including retries).
         */
        ULONG DbgPacketLogNextIndex;
        TRANSFER_PACKET DbgPacketLogs[DBG_NUM_PACKET_LOG_ENTRIES];
    #endif

    //
    // Spin lock for low priority I/O list
    //
    KSPIN_LOCK IdleListLock;

    //
    // Queue for low priority I/O
    //
    LIST_ENTRY IdleIrpList;

    //
    // Timer for low priority I/O
    //
    KTIMER IdleTimer;

    //
    // DPC for low priority I/O
    //
    KDPC IdleDpc;

#if (NTDDI_VERSION >= NTDDI_WIN8)

    //
    // Time (ms) since the completion of the last non-idle request before the
    // first idle request should be issued. Due to the coarseness of the idle
    // timer frequency, some variability in the idle interval will be tolerated
    // such that it is the desired idle interval on average.
    //
    USHORT IdleInterval;

    //
    // Max number of active idle requests.
    //
    USHORT IdleActiveIoMax;

#endif

    //
    // Idle duration required to process idle request
    // to avoid starvation
    //
    USHORT StarvationDuration;

    //
    // Idle I/O count
    //
    ULONG IdleIoCount;

    //
    // Flag to indicate timer status
    //
    LONG IdleTimerStarted;

    //
    // Time when the Idle timer was started
    //
    LARGE_INTEGER AntiStarvationStartTime;

    //
    // Normal priority I/O time
    //
    LARGE_INTEGER LastNonIdleIoTime;

    //
    // Time when the last IO of any priority completed.
    //
    LARGE_INTEGER LastIoCompletionTime;

    //
    // Count of active normal priority I/O
    //
    LONG ActiveIoCount;

    //
    // Count of active idle priority I/O
    //
    LONG ActiveIdleIoCount;

    //
    // Support for class drivers to extend
    // the interpret sense information routine
    // and retry history per-packet.  Copy of
    // values in driver extension.
    //
    PCLASS_INTERPRET_SENSE_INFO2 InterpretSenseInfo;

    //
    // power process parameters. they work closely with CLASS_POWER_CONTEXT structure.
    //
    ULONG MaxPowerOperationRetryCount;
    PIRP  PowerProcessIrp;

    //
    // Indicates legacy error handling should be used.
    // This means:
    //  - Max number of retries for an IO request is 8 (instead of 4).
    //
    BOOLEAN LegacyErrorHandling;

    //
    // Maximum number of retries allowed for IO requests for this device.
    //
    UCHAR MaxNumberOfIoRetries;

    //
    // Disable All Throttling in case of Error
    //
    BOOLEAN DisableThrottling;

};

//
// !!! WARNING !!!
// DO NOT use the following structure in code outside of classpnp
// as structure will not be guaranteed between OS versions.
//
// EX_RUNDOWN_REF_CACHE_AWARE is variable size and follows
// RemoveLockFailAcquire. EX_RUNDOWN_REF_CACHE_AWARE must be part
// of the device extension allocation to avoid issues with a device
// that has been PNP remove but still has outstanding references.
// In this case, the removed object may still receive incoming requests.
//
// There are code dependencies on the structure layout. To minimize
// code changes, new fields to _CLASS_PRIVATE_COMMON_DATA should be
// added based on the following guidance.
// - Fixed size: beginning of _CLASS_PRIVATE_COMMON_DATA
// - Variable size: at the end of _CLASS_PRIVATE_COMMON_DATA after the
//   last variable size field.
//

struct _CLASS_PRIVATE_COMMON_DATA {

    //
    // Cacheaware rundown lock reference
    //

    LONG RemoveLockFailAcquire;

    //
    // N.B. EX_RUNDOWN_REF_CACHE_AWARE begins with a pointer-sized item that is
    //      accessed interlocked, and must be aligned on ARM platforms. In order
    //      for this to work on ARM64, an additional 32-bit slot must be allocated.
    //

#if defined(_WIN64)
    LONG Align;
#endif

    // EX_RUNDOWN_REF_CACHE_AWARE (variable size) follows

};

//
// Verify that the size of _CLASS_PRIVATE_COMMON_DATA is pointer size aligned
// to ensure the EX_RUNDOWN_REF_CACHE_AWARE following it is properly aligned.
//

C_ASSERT((sizeof(struct _CLASS_PRIVATE_COMMON_DATA) % sizeof(PVOID)) == 0);

typedef struct _IDLE_POWER_FDO_LIST_ENTRY {
    LIST_ENTRY ListEntry;
    PDEVICE_OBJECT Fdo;
} IDLE_POWER_FDO_LIST_ENTRY, *PIDLE_POWER_FDO_LIST_ENTRY;

typedef struct _OFFLOAD_READ_CONTEXT {

    PDEVICE_OBJECT Fdo;

    //
    // Upper offload read DSM irp.
    //

    PIRP OffloadReadDsmIrp;

    //
    // A pseudo-irp is used despite the operation being async.  This is in
    // contrast to normal read and write, which let TransferPktComplete()
    // complete the upper IRP directly.  Offload requests are enough different
    // that it makes more sense to let them manage their own async steps with
    // minimal help from TransferPktComplete() (just a continuation function
    // call during TransferPktComplete()).
    //

    IRP PseudoIrp;

    //
    // The offload read context tracks one packet in flight at a time - it'll be
    // the POPULATE TOKEN packet first, then RECEIVE ROD TOKEN INFORMATION.
    //
    // This field exists only for debug purposes.
    //

    PTRANSFER_PACKET Pkt;

    PMDL PopulateTokenMdl;

    ULONG BufferLength;

    ULONG ListIdentifier;

    ULONG ReceiveTokenInformationBufferLength;

    //
    // Total sectors that the operation is attempting to process.
    //

    ULONGLONG TotalSectorsToProcess;

    //
    // Total sectors actually processed.
    //

    ULONGLONG TotalSectorsProcessed;

    //
    // Total upper request size in bytes.
    //

    ULONGLONG EntireXferLen;

    //
    // Just a cached copy of what was in the transfer packet.
    //

    SCSI_REQUEST_BLOCK Srb;

    //
    // Pointer into the token part of the SCSI buffer (the buffer immediately
    // after this struct), for easy reference.
    //

    PUCHAR Token;

    // The SCSI buffer (in/out buffer, not CDB) for the commands immediately
    // follows this struct, so no need to have a field redundantly pointing to
    // the buffer.
} OFFLOAD_READ_CONTEXT, *POFFLOAD_READ_CONTEXT;


typedef struct _OFFLOAD_WRITE_CONTEXT {

    PDEVICE_OBJECT Fdo;

    PIRP OffloadWriteDsmIrp;

    ULONGLONG EntireXferLen;
    ULONGLONG TotalRequestSizeSectors;

    ULONG DataSetRangesCount;

    PDEVICE_MANAGE_DATA_SET_ATTRIBUTES DsmAttributes;
    PDEVICE_DATA_SET_RANGE DataSetRanges;
    PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS OffloadWriteParameters;
    ULONGLONG LogicalBlockOffset;

    ULONG MaxBlockDescrCount;
    ULONGLONG MaxLbaCount;

    ULONG BufferLength;
    ULONG ReceiveTokenInformationBufferLength;

    IRP PseudoIrp;

    PMDL WriteUsingTokenMdl;

    ULONGLONG TotalSectorsProcessedSuccessfully;
    ULONG DataSetRangeIndex;
    ULONGLONG DataSetRangeByteOffset;

    PTRANSFER_PACKET Pkt;

    //
    // Per-WUT (WRITE USING TOKEN), not overall.
    //

    ULONGLONG TotalSectorsToProcess;
    ULONGLONG TotalSectorsProcessed;

    ULONG ListIdentifier;

    BOOLEAN TokenInvalidated;

    //
    // Just a cached copy of what was in the transfer packet.
    //

    SCSI_REQUEST_BLOCK Srb;

    ULONGLONG OperationStartTime;

} OFFLOAD_WRITE_CONTEXT, *POFFLOAD_WRITE_CONTEXT;


typedef struct _OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER {
    PIO_WORKITEM WorkItem;
    PVOID SenseData;
    ULONG SenseDataSize;
    UCHAR SrbStatus;
    UCHAR ScsiStatus;
    UCHAR OpCode;
    UCHAR Reserved;
    ULONG ErrorCode;
} OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER, *POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER;

typedef struct _IO_RETRIED_LOG_MESSAGE_CONTEXT {
    OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ContextHeader;
    LARGE_INTEGER Lba;
    ULONG DeviceNumber;
} IO_RETRIED_LOG_MESSAGE_CONTEXT, *PIO_RETRIED_LOG_MESSAGE_CONTEXT;


#define QERR_SET_ZERO_ODX_OR_TP_ONLY 0
#define QERR_SET_ZERO_ALWAYS         1
#define QERR_SET_ZERO_NEVER          2


#define MIN(a, b)   ((a) < (b) ? (a) : (b))
#define MAX(a, b)   ((a) > (b) ? (a) : (b))


#define NOT_READY_RETRY_INTERVAL    10
#define MINIMUM_RETRY_UNITS         ((LONGLONG)32)
#define MODE_PAGE_DATA_SIZE         192

#define CLASS_IDLE_INTERVAL_MIN     12          // 12 milliseconds
#define CLASS_IDLE_INTERVAL         12          // 12 milliseconds
#define CLASS_STARVATION_INTERVAL   500         // 500 milliseconds

//
// Value of 50 milliseconds in 100 nanoseconds units
//
#define FIFTY_MS_IN_100NS_UNITS     50 * 100


/*
 *  Simple singly-linked-list queuing macros, with no synchronization.
 */
__inline VOID SimpleInitSlistHdr(SINGLE_LIST_ENTRY *SListHdr)
{
    SListHdr->Next = NULL;
}
__inline VOID SimplePushSlist(SINGLE_LIST_ENTRY *SListHdr, SINGLE_LIST_ENTRY *SListEntry)
{
    SListEntry->Next = SListHdr->Next;
    SListHdr->Next = SListEntry;
}
__inline SINGLE_LIST_ENTRY *SimplePopSlist(SINGLE_LIST_ENTRY *SListHdr)
{
    SINGLE_LIST_ENTRY *sListEntry = SListHdr->Next;
    if (sListEntry){
        SListHdr->Next = sListEntry->Next;
        sListEntry->Next = NULL;
    }
    return sListEntry;
}
__inline BOOLEAN SimpleIsSlistEmpty(SINGLE_LIST_ENTRY *SListHdr)
{
    return (SListHdr->Next == NULL);
}

__inline
BOOLEAN
ClasspIsIdleRequestSupported(
    PCLASS_PRIVATE_FDO_DATA FdoData,
    PIRP Irp
    )
{
    IO_PRIORITY_HINT ioPriority = IoGetIoPriorityHint(Irp);
    return ((ioPriority <= IoPriorityLow) && (FdoData->IdlePrioritySupported == TRUE));
}

__inline
VOID
ClasspMarkIrpAsIdle(
    PIRP Irp,
    BOOLEAN Idle
    )
{
// truncation is not an issue for this use case
// nonstandard extension used is not an issue for this use case
#pragma warning(suppress:4305; suppress:4213)
    ((BOOLEAN)Irp->Tail.Overlay.DriverContext[1]) = Idle;
}

__inline
BOOLEAN
ClasspIsIdleRequest(
    PIRP Irp
    )
{
#pragma warning(suppress:4305) // truncation is not an issue for this use case
    return ((BOOLEAN)Irp->Tail.Overlay.DriverContext[1]);
}

__inline
LARGE_INTEGER
ClasspGetCurrentTime(
    VOID
    )
{
    LARGE_INTEGER currentTime;

    currentTime.QuadPart = KeQueryUnbiasedInterruptTimePrecise((ULONG64*)&currentTime.QuadPart);

    return currentTime;
}

__inline
ULONGLONG
ClasspTimeDiffToMs(
    ULONGLONG TimeDiff
    )
{
    TimeDiff /= (10 * 1000);

    return TimeDiff;
}

__inline
BOOLEAN
ClasspSupportsUnmap(
    _In_ PCLASS_FUNCTION_SUPPORT_INFO SupportInfo
    )
{
    return SupportInfo->LBProvisioningData.LBPU;
}

__inline
BOOLEAN
ClasspIsThinProvisioned(
    _In_ PCLASS_FUNCTION_SUPPORT_INFO SupportInfo
    )
{
    //
    // We only support thinly provisioned devices that also support UNMAP.
    //
    if (SupportInfo->LBProvisioningData.ProvisioningType == PROVISIONING_TYPE_THIN &&
        SupportInfo->LBProvisioningData.LBPU == TRUE)
    {
        return TRUE;
    }

    return FALSE;
}

__inline
BOOLEAN
ClasspIsObsoletePortDriver(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    if ( (FdoExtension->MiniportDescriptor != NULL) &&
         (FdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSCSIport) ) {
        return TRUE;
    }

    return FALSE;
}


ULONG
ClasspCalculateLogicalSectorSize (
    _In_ PDEVICE_OBJECT Fdo,
    _In_ ULONG          BytesPerBlockInBigEndian
    );

DRIVER_INITIALIZE DriverEntry;

DRIVER_UNLOAD ClassUnload;

_Dispatch_type_(IRP_MJ_CREATE)
_Dispatch_type_(IRP_MJ_CLOSE)
DRIVER_DISPATCH ClassCreateClose;

NTSTATUS
ClasspCreateClose(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

VOID
ClasspCleanupProtectedLocks(
    IN PFILE_OBJECT_EXTENSION FsContext
    );

NTSTATUS
ClasspEjectionControl(
    IN PDEVICE_OBJECT Fdo,
    IN PIRP Irp,
    IN MEDIA_LOCK_TYPE LockType,
    IN BOOLEAN Lock
    );

_Dispatch_type_(IRP_MJ_READ)
_Dispatch_type_(IRP_MJ_WRITE)
DRIVER_DISPATCH ClassReadWrite;

_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
DRIVER_DISPATCH ClassDeviceControlDispatch;

_Dispatch_type_(IRP_MJ_PNP)
DRIVER_DISPATCH ClassDispatchPnp;

NTSTATUS
ClassPnpStartDevice(
    IN PDEVICE_OBJECT DeviceObject
    );

_Dispatch_type_(IRP_MJ_SHUTDOWN)
_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
DRIVER_DISPATCH ClassShutdownFlush;

_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
DRIVER_DISPATCH ClassSystemControl;


//
// Class internal routines
//

DRIVER_ADD_DEVICE ClassAddDevice;

IO_COMPLETION_ROUTINE ClasspSendSynchronousCompletion;

VOID
RetryRequest(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PSCSI_REQUEST_BLOCK Srb,
    BOOLEAN Associated,
    LONGLONG TimeDelta100ns
    );

NTSTATUS
ClassIoCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );

NTSTATUS
ClassPnpQueryFdoRelations(
    IN PDEVICE_OBJECT Fdo,
    IN PIRP Irp
    );

NTSTATUS
ClassRetrieveDeviceRelations(
    IN PDEVICE_OBJECT Fdo,
    IN DEVICE_RELATION_TYPE RelationType,
    OUT PDEVICE_RELATIONS *DeviceRelations
    );

NTSTATUS
ClassGetPdoId(
    IN PDEVICE_OBJECT Pdo,
    IN BUS_QUERY_ID_TYPE IdType,
    IN PUNICODE_STRING IdString
    );

NTSTATUS
ClassQueryPnpCapabilities(
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN PDEVICE_CAPABILITIES Capabilities
    );

DRIVER_STARTIO ClasspStartIo;

NTSTATUS
ClasspPagingNotificationCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PDEVICE_OBJECT RealDeviceObject
    );

NTSTATUS
ClasspMediaChangeCompletion(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    );

NTSTATUS
ClasspMcnControl(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PIRP Irp,
    IN PSCSI_REQUEST_BLOCK Srb
    );

VOID
ClasspRegisterMountedDeviceInterface(
    IN PDEVICE_OBJECT DeviceObject
    );

VOID
ClasspDisableTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

VOID
ClasspEnableTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspInitializeTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
);

VOID
ClasspDeleteTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
);

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
BOOLEAN
ClasspUpdateTimerNoWakeTolerance(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
);
#endif

NTSTATUS
ClasspDuidQueryProperty(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    );

_Dispatch_type_(IRP_MJ_CREATE)
_Dispatch_type_(IRP_MJ_CLOSE)
_Dispatch_type_(IRP_MJ_READ)
_Dispatch_type_(IRP_MJ_WRITE)
_Dispatch_type_(IRP_MJ_SCSI)
_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
_Dispatch_type_(IRP_MJ_SHUTDOWN)
_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
_Dispatch_type_(IRP_MJ_PNP)
_Dispatch_type_(IRP_MJ_POWER)
_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
DRIVER_DISPATCH ClassGlobalDispatch;

VOID
ClassInitializeDispatchTables(
    PCLASS_DRIVER_EXTENSION DriverExtension
    );

NTSTATUS
ClasspPersistentReserve(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

//
// routines for dictionary list support
//

VOID
InitializeDictionary(
    IN PDICTIONARY Dictionary
    );

BOOLEAN
TestDictionarySignature(
    IN PDICTIONARY Dictionary
    );

NTSTATUS
AllocateDictionaryEntry(
    IN PDICTIONARY Dictionary,
    IN ULONGLONG Key,
    IN ULONG Size,
    IN ULONG Tag,
    OUT PVOID *Entry
    );

PVOID
GetDictionaryEntry(
    IN PDICTIONARY Dictionary,
    IN ULONGLONG Key
    );

VOID
FreeDictionaryEntry(
    IN PDICTIONARY Dictionary,
    IN PVOID Entry
    );


NTSTATUS
ClasspAllocateReleaseRequest(
    IN PDEVICE_OBJECT Fdo
    );

VOID
ClasspFreeReleaseRequest(
    IN PDEVICE_OBJECT Fdo
    );

IO_COMPLETION_ROUTINE ClassReleaseQueueCompletion;

VOID
ClasspReleaseQueue(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP ReleaseQueueIrp
    );

VOID
ClasspDisablePowerNotification(
    PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
);

//
// class power routines
//

_Dispatch_type_(IRP_MJ_POWER)
DRIVER_DISPATCH ClassDispatchPower;

NTSTATUS
ClassMinimalPowerHandler(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

_IRQL_requires_same_
NTSTATUS
ClasspEnableIdlePower(
    _In_ PDEVICE_OBJECT DeviceObject
    );

POWER_SETTING_CALLBACK ClasspPowerSettingCallback;

//
// Child list routines
//

VOID
ClassAddChild(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION Parent,
    _In_ PPHYSICAL_DEVICE_EXTENSION Child,
    _In_ BOOLEAN AcquireLock
    );

PPHYSICAL_DEVICE_EXTENSION
ClassRemoveChild(
    IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
    IN PPHYSICAL_DEVICE_EXTENSION Child,
    IN BOOLEAN AcquireLock
    );

VOID
ClasspRetryDpcTimer(
    IN PCLASS_PRIVATE_FDO_DATA FdoData
    );

KDEFERRED_ROUTINE ClasspRetryRequestDpc;

VOID
ClassFreeOrReuseSrb(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN __drv_freesMem(mem) PSCSI_REQUEST_BLOCK Srb
    );

VOID
ClassRetryRequest(
    IN PDEVICE_OBJECT SelfDeviceObject,
    IN PIRP           Irp,
    _In_ _In_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) // this is 100 seconds; already an assert in classpnp based on this
    IN LONGLONG       TimeDelta100ns // in 100ns units
    );

VOID
ClasspBuildRequestEx(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PIRP Irp,
    _In_ __drv_aliasesMem PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS
ClasspAllocateReleaseQueueIrp(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspAllocatePowerProcessIrp(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspInitializeGesn(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMEDIA_CHANGE_DETECTION_INFO Info
    );

VOID
ClassSendEjectionNotification(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

VOID
ClasspScanForSpecialInRegistry(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

VOID
ClasspScanForClassHacks(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN ULONG_PTR Data
    );

NTSTATUS
ClasspInitializeHotplugInfo(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

VOID
ClasspPerfIncrementErrorCount(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );
VOID
ClasspPerfIncrementSuccessfulIo(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

IO_WORKITEM_ROUTINE ClasspUpdateDiskProperties;

__drv_allocatesMem(Mem)
PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo);
VOID DestroyTransferPacket(_In_ __drv_freesMem(mem) PTRANSFER_PACKET Pkt);
VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, __drv_aliasesMem PTRANSFER_PACKET Pkt);
PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded);
PTRANSFER_PACKET DequeueFreeTransferPacketEx(_In_ PDEVICE_OBJECT Fdo, _In_ BOOLEAN AllocIfNeeded, _In_ ULONG Node);
VOID SetupReadWriteTransferPacket(PTRANSFER_PACKET pkt, PVOID Buf, ULONG Len, LARGE_INTEGER DiskLocation, PIRP OriginalIrp);
NTSTATUS SubmitTransferPacket(PTRANSFER_PACKET Pkt);
IO_COMPLETION_ROUTINE TransferPktComplete;
NTSTATUS ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp, BOOLEAN PostToDpc);
VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt);
KDEFERRED_ROUTINE TransferPacketRetryTimerDpc;
BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt);
BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt);
VOID EnqueueDeferredClientIrp(PDEVICE_OBJECT Fdo, PIRP Irp);
PIRP DequeueDeferredClientIrp(PDEVICE_OBJECT Fdo);
VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation);
BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt);
VOID SetupEjectionTransferPacket(TRANSFER_PACKET *Pkt, BOOLEAN PreventMediaRemoval, PKEVENT SyncEventPtr, PIRP OriginalIrp);
VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSenseBuffer, UCHAR ModeSenseBufferLen, UCHAR PageMode, UCHAR SubPage, PIRP OriginalIrp, UCHAR PageControl);
VOID SetupModeSelectTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSelectBuffer, UCHAR ModeSelectBufferLen, BOOLEAN SavePages, PIRP OriginalIrp);
VOID SetupDriveCapacityTransferPacket(TRANSFER_PACKET *Pkt, PVOID ReadCapacityBuffer, ULONG ReadCapacityBufferLen, PKEVENT SyncEventPtr, PIRP OriginalIrp, BOOLEAN Use16ByteCdb);
PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen);
PMDL ClasspBuildDeviceMdl(PVOID Buffer, ULONG BufferLen, BOOLEAN WriteToDevice);
VOID FreeDeviceInputMdl(PMDL Mdl);
VOID ClasspFreeDeviceMdl(PMDL Mdl);
NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo);
VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo);
VOID InterpretCapacityData(PDEVICE_OBJECT Fdo, PREAD_CAPACITY_DATA_EX ReadCapacityData);
IO_WORKITEM_ROUTINE_EX CleanupTransferPacketToWorkingSetSizeWorker;
VOID CleanupTransferPacketToWorkingSetSize(_In_ PDEVICE_OBJECT Fdo, _In_ BOOLEAN LimitNumPktToDelete, _In_ ULONG Node);

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
ClasspSetupPopulateTokenTransferPacket(
    _In_ __drv_aliasesMem POFFLOAD_READ_CONTEXT OffloadReadContext,
    _In_ PTRANSFER_PACKET Pkt,
    _In_ ULONG Length,
    _In_reads_bytes_(Length) PUCHAR PopulateTokenBuffer,
    _In_ PIRP OriginalIrp,
    _In_ ULONG ListIdentifier
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
ClasspSetupReceivePopulateTokenInformationTransferPacket(
    _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
    _In_ PTRANSFER_PACKET Pkt,
    _In_ ULONG Length,
    _In_reads_bytes_(Length) PUCHAR ReceivePopulateTokenInformationBuffer,
    _In_ PIRP OriginalIrp,
    _In_ ULONG ListIdentifier
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
ClasspSetupWriteUsingTokenTransferPacket(
    _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
    _In_ PTRANSFER_PACKET Pkt,
    _In_ ULONG Length,
    _In_reads_bytes_(Length) PUCHAR WriteUsingTokenBuffer,
    _In_ PIRP OriginalIrp,
    _In_ ULONG ListIdentifier
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
    _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
    _In_ PTRANSFER_PACKET Pkt,
    _In_ ULONG Length,
    _In_reads_bytes_(Length) PUCHAR ReceiveWriteUsingTokenInformationBuffer,
    _In_ PIRP OriginalIrp,
    _In_ ULONG ListIdentifier
    );

ULONG ClasspModeSense(
    _In_ PDEVICE_OBJECT Fdo,
    _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
    _In_ ULONG Length,
    _In_ UCHAR PageMode,
    _In_ UCHAR PageControl
    );

NTSTATUS
ClasspModeSelect(
    _In_ PDEVICE_OBJECT Fdo,
    _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
    _In_ ULONG Length,
    _In_ BOOLEAN SavePages
    );

NTSTATUS ClasspWriteCacheProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspAccessAlignmentProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceSeekPenaltyProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceGetLBProvisioningVPDPage(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_opt_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,
    _In_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceGetBlockLimitsVPDPage(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _Inout_bytecount_(SrbSize) PSCSI_REQUEST_BLOCK Srb,
    _In_ ULONG SrbSize,
    _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
    );

NTSTATUS ClasspDeviceTrimProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceLBProvisioningProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceTrimProcess(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ PGUID ActivityId,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceGetLBAStatus(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS ClasspDeviceGetLBAStatusWorker(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PCLASS_VPD_B0_DATA BlockLimitsData,
    _In_ ULONGLONG StartingOffset,
    _In_ ULONGLONG LengthInBytes,
    _Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput,
    _Inout_ PULONG DsmOutputLength,
    _Inout_ PSCSI_REQUEST_BLOCK Srb,
    _In_ BOOLEAN ConsolidateableBlocksOnly,
    _In_ ULONG OutputVersion,
    _Out_ PBOOLEAN BlockLimitsDataMayHaveChanged
    );

VOID ClassQueueThresholdEventWorker(
    _In_ PDEVICE_OBJECT DeviceObject
    );

VOID ClassQueueResourceExhaustionEventWorker(
    _In_ PDEVICE_OBJECT DeviceObject
    );

VOID ClassQueueCapacityChangedEventWorker(
    _In_ PDEVICE_OBJECT DeviceObject
    );

VOID ClassQueueProvisioningTypeChangedEventWorker(
    _In_ PDEVICE_OBJECT DeviceObject
    );

IO_WORKITEM_ROUTINE ClasspLogIOEventWithContext;

VOID
ClasspQueueLogIOEventWithContextWorker(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ ULONG SenseBufferSize,
    _In_ PVOID SenseData,
    _In_ UCHAR SrbStatus,
    _In_ UCHAR ScsiStatus,
    _In_ ULONG ErrorCode,
    _In_ ULONG CdbLength,
    _In_opt_ PCDB Cdb,
    _In_opt_ PTRANSFER_PACKET Pkt
    );

VOID
ClasspZeroQERR(
    _In_ PDEVICE_OBJECT DeviceObject
    );


_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClasspGetMaximumTokenListIdentifier(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_z_ PWSTR RegistryPath,
    _Out_ PULONG MaximumListIdentifier
    );

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClasspGetCopyOffloadMaxDuration(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_z_ PWSTR RegistryPath,
    _Out_ PULONG MaxDuration
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspDeviceCopyOffloadProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspValidateOffloadSupported(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspValidateOffloadInputParameters(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    );

_IRQL_requires_same_
NTSTATUS
ClasspGetTokenOperationCommandBufferLength(
    _In_ PDEVICE_OBJECT Fdo,
    _In_ ULONG ServiceAction,
    _Inout_ PULONG CommandBufferLength,
    _Out_opt_ PULONG TokenOperationBufferLength,
    _Out_opt_ PULONG ReceiveTokenInformationBufferLength
    );

_IRQL_requires_same_
NTSTATUS
ClasspGetTokenOperationDescriptorLimits(
    _In_ PDEVICE_OBJECT Fdo,
    _In_ ULONG ServiceAction,
    _In_ ULONG MaxParameterBufferLength,
    _Out_ PULONG MaxBlockDescriptorsCount,
    _Out_ PULONGLONG MaxBlockDescriptorsLength
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
VOID
ClasspConvertDataSetRangeToBlockDescr(
    _In_    PDEVICE_OBJECT Fdo,
    _In_    PVOID BlockDescr,
    _Inout_ PULONG CurrentBlockDescrIndex,
    _In_    ULONG MaxBlockDescrCount,
    _Inout_ PULONG CurrentLbaCount,
    _In_    ULONGLONG MaxLbaCount,
    _Inout_ PDEVICE_DATA_SET_RANGE DataSetRange,
    _Inout_ PULONGLONG TotalSectorsProcessed
    );

NTSTATUS
ClasspDeviceMediaTypeProperty(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );


_IRQL_requires_same_
PUCHAR
ClasspBinaryToAscii(
    _In_reads_(Length) PUCHAR HexBuffer,
    _In_ ULONG Length,
    _Inout_ PULONG UpdateLength
    );

__inline
BOOLEAN
ClasspIsTokenOperationComplete(
    _In_ ULONG CurrentStatus
    )
{
    BOOLEAN operationCompleted = FALSE;

    switch (CurrentStatus) {
        case OPERATION_COMPLETED_WITH_SUCCESS:
        case OPERATION_COMPLETED_WITH_ERROR:
        case OPERATION_COMPLETED_WITH_RESIDUAL_DATA:
        case OPERATION_TERMINATED: {

            operationCompleted = TRUE;
        }
    }

    return operationCompleted;
}

__inline
BOOLEAN
ClasspIsTokenOperation(
    _In_ PCDB Cdb
    )
{
    BOOLEAN tokenOperation = FALSE;

    if (Cdb) {
        ULONG opCode = Cdb->AsByte[0];
        ULONG serviceAction = Cdb->AsByte[1];

        if ((opCode == SCSIOP_POPULATE_TOKEN && serviceAction == SERVICE_ACTION_POPULATE_TOKEN) ||
            (opCode == SCSIOP_WRITE_USING_TOKEN && serviceAction == SERVICE_ACTION_WRITE_USING_TOKEN)) {

            tokenOperation = TRUE;
        }
    }

    return tokenOperation;
}

__inline
BOOLEAN
ClasspIsReceiveTokenInformation(
    _In_ PCDB Cdb
    )
{
    BOOLEAN receiveTokenInformation = FALSE;

    if (Cdb) {
        ULONG opCode = Cdb->AsByte[0];
        ULONG serviceAction = Cdb->AsByte[1];

        if (opCode == SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION && serviceAction == SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION) {

            receiveTokenInformation = TRUE;
        }
    }

    return receiveTokenInformation;
}

__inline
BOOLEAN
ClasspIsOffloadDataTransferCommand(
    _In_ PCDB Cdb
    )
{
    BOOLEAN offloadCommand = (ClasspIsTokenOperation(Cdb) || ClasspIsReceiveTokenInformation(Cdb)) ? TRUE : FALSE;

    return offloadCommand;
}

extern LIST_ENTRY AllFdosList;


VOID
ClasspInitializeIdleTimer(
    PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspIsPortable(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION   FdoExtension,
    _Out_ PBOOLEAN                      IsPortable
    );

VOID
ClasspGetInquiryVpdSupportInfo(
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspGetLBProvisioningInfo(
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );


_IRQL_requires_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClassDetermineTokenOperationCommandSupport(
    _In_ PDEVICE_OBJECT DeviceObject
    );

_IRQL_requires_same_
NTSTATUS
ClasspGetBlockDeviceTokenLimitsInfo(
    _Inout_ PDEVICE_OBJECT DeviceObject
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClassDeviceProcessOffloadRead(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClassDeviceProcessOffloadWrite(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspServicePopulateTokenTransferRequest(
    _In_ PDEVICE_OBJECT Fdo,
    _In_ PIRP Irp
    );

_IRQL_requires_same_
VOID
ClasspReceivePopulateTokenInformation(
    _In_ POFFLOAD_READ_CONTEXT OffloadReadContext
    );

_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspServiceWriteUsingTokenTransferRequest(
    _In_ PDEVICE_OBJECT Fdo,
    _In_ PIRP Irp
    );

_IRQL_requires_same_
VOID
ClasspReceiveWriteUsingTokenInformation(
    _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
    );

VOID
ClasspCompleteOffloadRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ NTSTATUS CompletionStatus
    );

VOID
ClasspCleanupOffloadReadContext(
    _In_ __drv_freesMem(mem) POFFLOAD_READ_CONTEXT OffloadReadContext
    );

VOID
ClasspCompleteOffloadRead(
    _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
    _In_ NTSTATUS CompletionStatus
    );

// PCONTINUATION_ROUTINE
VOID
ClasspPopulateTokenTransferPacketDone(
    _In_ PVOID Context
    );

// PCONTINUATION_ROUTINE
VOID
ClasspReceivePopulateTokenInformationTransferPacketDone(
    _In_ PVOID Context
    );

VOID
ClasspContinueOffloadWrite(
    _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext
    );

VOID
ClasspCleanupOffloadWriteContext(
    _In_ __drv_freesMem(mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext
    );

VOID
ClasspCompleteOffloadWrite(
    _In_ __drv_freesMem(Mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
    _In_ NTSTATUS CompletionCausingStatus
    );

VOID
ClasspReceiveWriteUsingTokenInformationDone(
    _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
    _In_ NTSTATUS CompletionCausingStatus
    );

VOID
ClasspWriteUsingTokenTransferPacketDone(
    _In_ PVOID Context
    );

VOID
ClasspReceiveWriteUsingTokenInformationTransferPacketDone(
    _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
    );

NTSTATUS
ClasspRefreshFunctionSupportInfo(
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ BOOLEAN ForceQuery
    );

NTSTATUS
ClasspBlockLimitsDataSnapshot(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ BOOLEAN ForceQuery,
    _Out_ PCLASS_VPD_B0_DATA BlockLimitsData,
    _Out_ PULONG GenerationCount
    );

NTSTATUS
InterpretReadCapacity16Data (
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
    );

NTSTATUS
ClassReadCapacity16 (
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS
ClassDeviceGetLBProvisioningResources(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

_IRQL_requires_same_
NTSTATUS
ClasspStorageEventNotification(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp
    );

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClasspPowerActivateDevice(
    _In_ PDEVICE_OBJECT DeviceObject
    );

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClasspPowerIdleDevice(
    _In_ PDEVICE_OBJECT DeviceObject
    );

IO_WORKITEM_ROUTINE ClassLogThresholdEvent;

NTSTATUS
ClasspLogSystemEventWithDeviceNumber(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ NTSTATUS IoErrorCode
    );

IO_WORKITEM_ROUTINE ClassLogResourceExhaustionEvent;

NTSTATUS
ClasspEnqueueIdleRequest(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    );

VOID
ClasspCompleteIdleRequest(
    PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

NTSTATUS
ClasspPriorityHint(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    );

VOID
HistoryInitializeRetryLogs(
    _Out_ PSRB_HISTORY History,
    ULONG HistoryCount
    );
#define HISTORYINITIALIZERETRYLOGS(_packet)               \
    {                                                     \
        if (_packet->RetryHistory != NULL)                \
        {                                                 \
            HistoryInitializeRetryLogs(                   \
                _packet->RetryHistory,                    \
                _packet->RetryHistory->TotalHistoryCount  \
                );                                        \
        }                                                 \
    }

VOID
HistoryLogSendPacket(
    TRANSFER_PACKET *Pkt
    );
#define HISTORYLOGSENDPACKET(_packet)        \
    {                                        \
        if (_packet->RetryHistory != NULL) { \
            HistoryLogSendPacket(_packet);   \
        }                                    \
    }

VOID
HistoryLogReturnedPacket(
    TRANSFER_PACKET *Pkt
    );

#define HISTORYLOGRETURNEDPACKET(_packet)      \
    {                                          \
        if (_packet->RetryHistory != NULL) {   \
            HistoryLogReturnedPacket(_packet); \
        }                                      \
    }

BOOLEAN
InterpretSenseInfoWithoutHistory(
    _In_  PDEVICE_OBJECT Fdo,
    _In_opt_  PIRP OriginalRequest,
    _In_  PSCSI_REQUEST_BLOCK Srb,
          UCHAR MajorFunctionCode,
          ULONG IoDeviceCode,
          ULONG PreviousRetryCount,
    _Out_ NTSTATUS * Status,
    _Out_opt_ _Deref_out_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
          LONGLONG * RetryIn100nsUnits
    );

BOOLEAN
ClasspMyStringMatches(
    _In_opt_z_ PCHAR StringToMatch,
    _In_z_ PCHAR TargetString
    );



#define TRACKING_FORWARD_PROGRESS_PATH1                  (0x00000001)
#define TRACKING_FORWARD_PROGRESS_PATH2                  (0x00000002)
#define TRACKING_FORWARD_PROGRESS_PATH3                  (0x00000004)


VOID
ClasspInitializeRemoveTracking(
    _In_ PDEVICE_OBJECT DeviceObject
    );

VOID
ClasspUninitializeRemoveTracking(
    _In_ PDEVICE_OBJECT DeviceObject
    );

RTL_GENERIC_COMPARE_ROUTINE RemoveTrackingCompareRoutine;

RTL_GENERIC_ALLOCATE_ROUTINE RemoveTrackingAllocateRoutine;

RTL_GENERIC_FREE_ROUTINE RemoveTrackingFreeRoutine;

#if (NTDDI_VERSION >= NTDDI_WIN8)

typedef PVOID
(*PSRB_ALLOCATE_ROUTINE) (
    _In_ CLONG  ByteSize
    );

PVOID
DefaultStorageRequestBlockAllocateRoutine(
    _In_ CLONG ByteSize
    );


NTSTATUS
CreateStorageRequestBlock(
    _Inout_ PSTORAGE_REQUEST_BLOCK *Srb,
    _In_ USHORT AddressType,
    _In_opt_ PSRB_ALLOCATE_ROUTINE AllocateRoutine,
    _Inout_opt_ ULONG *ByteSize,
    _In_ ULONG NumSrbExData,
    ...
    );

NTSTATUS
InitializeStorageRequestBlock(
    _Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
    _In_ USHORT AddressType,
    _In_ ULONG ByteSize,
    _In_ ULONG NumSrbExData,
    ...
    );

VOID
ClasspConvertToScsiRequestBlock(
    _Out_ PSCSI_REQUEST_BLOCK Srb,
    _In_ PSTORAGE_REQUEST_BLOCK SrbEx
    );

__inline PCDB
ClasspTransferPacketGetCdb(
    _In_ PTRANSFER_PACKET Pkt
    )
{
    return SrbGetCdb(Pkt->Srb);
}

//
// This inline function calculates number of retries already happened till now for known operation codes
// and set the out parameter - TimesAlreadyRetried with the value,  returns True
//
// For unknown operation codes this function will return false and will set TimesAlreadyRetried with zero
//
__inline BOOLEAN
ClasspTransferPacketGetNumberOfRetriesDone(
    _In_ PTRANSFER_PACKET Pkt,
    _In_ PCDB Cdb,
    _Out_ PULONG TimesAlreadyRetried
    )
{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
    PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;

    if (Cdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL)
    {
        *TimesAlreadyRetried = NUM_LOCKMEDIAREMOVAL_RETRIES - Pkt->NumRetries;
    }
    else if ((Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
               (Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10))
    {
        *TimesAlreadyRetried = NUM_MODESENSE_RETRIES - Pkt->NumRetries;
    }
    else if ((Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
               (Cdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16))
    {
        *TimesAlreadyRetried = NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries;
    }
    else if (IS_SCSIOP_READWRITE(Cdb->CDB10.OperationCode))
    {
        *TimesAlreadyRetried = fdoData->MaxNumberOfIoRetries - Pkt->NumRetries;
    }
    else if (Cdb->TOKEN_OPERATION.OperationCode == SCSIOP_POPULATE_TOKEN &&
               Cdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_POPULATE_TOKEN)
    {
        *TimesAlreadyRetried = NUM_POPULATE_TOKEN_RETRIES - Pkt->NumRetries;
    }
    else if (Cdb->TOKEN_OPERATION.OperationCode == SCSIOP_WRITE_USING_TOKEN &&
               Cdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN)
    {
        *TimesAlreadyRetried = NUM_WRITE_USING_TOKEN_RETRIES - Pkt->NumRetries;
    }
    else if (ClasspIsReceiveTokenInformation(Cdb))
    {
        *TimesAlreadyRetried = NUM_RECEIVE_TOKEN_INFORMATION_RETRIES - Pkt->NumRetries;
    }

    else
    {
        *TimesAlreadyRetried = 0;
        return FALSE;
    }


    return TRUE;
}


__inline PVOID
ClasspTransferPacketGetSenseInfoBuffer(
    _In_ PTRANSFER_PACKET Pkt
    )
{
    return SrbGetSenseInfoBuffer(Pkt->Srb);
}

__inline UCHAR
ClasspTransferPacketGetSenseInfoBufferLength(
    _In_ PTRANSFER_PACKET Pkt
    )
{
    return SrbGetSenseInfoBufferLength(Pkt->Srb);
}


__inline VOID
ClasspSrbSetOriginalIrp(
    _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb,
    _In_ PIRP Irp
    )
{
    if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)
    {
        ((PSTORAGE_REQUEST_BLOCK)Srb)->MiniportContext = (PVOID)Irp;
    }
    else
    {
        ((PSCSI_REQUEST_BLOCK)Srb)->SrbExtension = (PVOID)Irp;
    }
}

__inline
BOOLEAN
PORT_ALLOCATED_SENSE_EX(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb
    )
{
    return ((BOOLEAN)((TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_PORT_DRIVER_ALLOCSENSE) &&
             TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_FREE_SENSE_BUFFER)) &&
            (SrbGetSenseInfoBuffer(Srb) != FdoExtension->SenseData))
            );
}

__inline
VOID
FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb
    )
{
    NT_ASSERT(TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
    NT_ASSERT(TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_FREE_SENSE_BUFFER));
    NT_ASSERT(SrbGetSenseInfoBuffer(Srb) != FdoExtension->SenseData);

    ExFreePool(SrbGetSenseInfoBuffer(Srb));
    SrbSetSenseInfoBuffer(Srb, FdoExtension->SenseData);
    SrbSetSenseInfoBufferLength(Srb, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(FdoExtension));
    SrbClearSrbFlags(Srb, SRB_FLAGS_FREE_SENSE_BUFFER);
    return;
}

#endif //NTDDI_WIN8

BOOLEAN
ClasspFailurePredictionPeriodMissed(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    );

__inline
ULONG
ClasspGetMaxUsableBufferLengthFromOffset(
    _In_ PVOID BaseAddress,
    _In_ ULONG OffsetInBytes,
    _In_ ULONG BaseStructureSizeInBytes
    )
/*++

Routine Description:

    This routine returns the maximum size of a buffer that starts at a given offset,
    based on the size of the containing structure.

Arguments:

    BaseAddress - The base address of the structure. The offset is computed relative to this.

    OffsetInBytes - (BaseAddress + OffsetInBytes) points to the beginning of the buffer.

    BaseStructureSizeInBytes - The size of the structure which contains the buffer.

Return Value:

    max(BaseStructureSizeInBytes - OffsetInBytes, 0). If any operations wrap around,
    the return value is 0.

--*/

{

    ULONG_PTR offsetAddress = ((ULONG_PTR)BaseAddress + OffsetInBytes);

    if (offsetAddress < (ULONG_PTR)BaseAddress) {
        //
        // This means BaseAddress + OffsetInBytes > ULONG_PTR_MAX.
        //
        return 0;
    }

    if (OffsetInBytes > BaseStructureSizeInBytes) {
        return 0;
    }

    return BaseStructureSizeInBytes - OffsetInBytes;
}


BOOLEAN
ClasspIsThinProvisioningError (
    _In_ PSCSI_REQUEST_BLOCK _Srb
    );

__inline
BOOLEAN
ClasspLowerLayerNotSupport (
    _In_ NTSTATUS Status
    )
{
    return ((Status == STATUS_NOT_SUPPORTED) ||
            (Status == STATUS_NOT_IMPLEMENTED) ||
            (Status == STATUS_INVALID_DEVICE_REQUEST) ||
            (Status == STATUS_INVALID_PARAMETER_1));
}

__inline
BOOLEAN
ClasspSrbTimeOutStatus (
    _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb
    )
{
    UCHAR srbStatus = SrbGetSrbStatus(Srb);
    return ((srbStatus == SRB_STATUS_BUS_RESET) ||
            (srbStatus == SRB_STATUS_TIMEOUT) ||
            (srbStatus == SRB_STATUS_COMMAND_TIMEOUT) ||
            (srbStatus == SRB_STATUS_ABORTED));
}

NTSTATUS
ClassDeviceHwFirmwareGetInfoProcess(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
    );

NTSTATUS
ClassDeviceHwFirmwareDownloadProcess(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );

NTSTATUS
ClassDeviceHwFirmwareActivateProcess(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp,
    _Inout_ PSCSI_REQUEST_BLOCK Srb
    );



<|start_filename|>audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp<|end_filename|>
// KeywordDetectorContosoAdapter.cpp : Defines the exported functions for the DLL application.
//

// ISSUE-2015/1/14-frankye review all error codes

#include "stdafx.h"

#include <strsafe.h>
#include <mfapi.h>

#include <initguid.h>
#include "KeywordDetectorContosoAdapter.h"
#include "ContosoKeywordDetector.h"
#include <wrl.h>

using namespace Microsoft::WRL;

class KeywordDetectorContosoAdapter : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IKeywordDetectorOemAdapter>
{
public:
    KeywordDetectorContosoAdapter()
    {
    }

    STDMETHODIMP KeywordDetectorContosoAdapter::GetCapabilities(
        _Out_ BOOL* SupportsUserModels,
        _Outptr_ KEYWORDID** KeywordIds,
        _Out_ ULONG* NumKeywords,
        _Outptr_ LANGID** LangIds,
        _Out_ ULONG* NumLanguages,
        _Outptr_ IMFMediaType* *ppMediaType)
    {
        HRESULT hr;
        ComPtr<IMFMediaType> spMFType;
        const WAVEFORMATEX waveFormat = { WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0 };

        *SupportsUserModels = FALSE;

        *KeywordIds = (KEYWORDID *)CoTaskMemAlloc(sizeof(KEYWORDID));
        if (*KeywordIds == nullptr)
        {
            return E_OUTOFMEMORY;
        }
        **KeywordIds = KwVoiceAssistant;
        *NumKeywords = 1;
        *LangIds = (LANGID *)CoTaskMemAlloc(sizeof(LANGID));
        if (*LangIds == nullptr)
        {
            CoTaskMemFree(*KeywordIds);
            return E_OUTOFMEMORY;
        }
        **LangIds = 0x409;
        *NumLanguages = 1;

        hr = MFCreateMediaType(spMFType.GetAddressOf());
        if (SUCCEEDED(hr))
        {
            hr = MFInitMediaTypeFromWaveFormatEx(spMFType.Get(), &waveFormat, sizeof(waveFormat));
            if (SUCCEEDED(hr))
            {
                spMFType.CopyTo(ppMediaType);
                hr = S_OK;
            }
        }

        if (*ppMediaType == nullptr)
        {
            return E_FAIL;
        }

        return hr;
    }

    STDMETHODIMP VerifyUserKeyword(
        _In_ IStream* ModelData,
        _In_ KEYWORDID KeywordId,
        _In_ LANGID LangId,
        _In_ LONG KeywordEndBytePos,
        _In_ IMFMediaBuffer* UserRecording)
    {
        UNREFERENCED_PARAMETER(ModelData);
        UNREFERENCED_PARAMETER(KeywordId);
        UNREFERENCED_PARAMETER(LangId);
        UNREFERENCED_PARAMETER(KeywordEndBytePos);
        UNREFERENCED_PARAMETER(UserRecording);

        return E_NOTIMPL;
    }

    STDMETHODIMP ComputeAndAddUserModelData(
        _Inout_ IStream* ModelData,
        _In_ KEYWORDSELECTOR KeywordSelector,
        _In_ LONG *KeywordEndBytePos,
        _In_ IMFMediaBuffer **UserRecordings,
        _In_ ULONG NumUserRecordings)
    {
        UNREFERENCED_PARAMETER(ModelData);
        UNREFERENCED_PARAMETER(KeywordSelector);
        UNREFERENCED_PARAMETER(KeywordEndBytePos);
        UNREFERENCED_PARAMETER(UserRecordings);
        UNREFERENCED_PARAMETER(NumUserRecordings);

        return E_NOTIMPL;
    }

    STDMETHODIMP BuildArmingPatternData(
        _In_ IStream *ModelData,
        _In_ KEYWORDSELECTOR *KeywordSelectors,
        _In_ ULONG NumKeywordSelectors,
        _Outptr_ SOUNDDETECTOR_PATTERNHEADER **ppPatternData)
    {
        CONTOSO_KEYWORDCONFIGURATION *pPatternData = nullptr;

        UNREFERENCED_PARAMETER(ModelData);

        if (NumKeywordSelectors > 1)
        {
            return E_INVALIDARG;
        }
        if ((KeywordSelectors[0].KeywordId != KwVoiceAssistant) ||
            (KeywordSelectors[0].LangId != 0x0409))
        {
            return E_INVALIDARG;
        }

        pPatternData = (CONTOSO_KEYWORDCONFIGURATION*)CoTaskMemAlloc(sizeof(CONTOSO_KEYWORDCONFIGURATION));
        if (pPatternData == nullptr)
        {
            return E_OUTOFMEMORY;
        }

        pPatternData->Header.Size = sizeof(*pPatternData);
        pPatternData->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER;
        pPatternData->ContosoDetectorConfigurationData = 0x12345678;

        *ppPatternData = &pPatternData->Header;
        pPatternData = nullptr;

        return S_OK;
    }

    STDMETHODIMP ParseDetectionResultData(
        _In_ IStream *ModelData, 
        _In_ SOUNDDETECTOR_PATTERNHEADER *Result,    // ISSUE-2015/1/14-frankye should be const
        _Out_ KEYWORDID* KeywordId,
        _Out_ LANGID* LangId,
        _Out_ BOOL *pIsUserMatch,                  
        _Out_ ULONG64 *KeywordStartPerformanceCounterValue,
        _Out_ ULONG64 *KeywordEndPerformanceCounterValue)
    {
        // const CONTOSO_KEYWORDDETECTIONRESULT *contosoResult;

        UNREFERENCED_PARAMETER(ModelData);

        if (Result->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER || Result->Size < sizeof(CONTOSO_KEYWORDDETECTIONRESULT))
        {
            return E_INVALIDARG;
        }

        // contosoResult = (CONTOSO_KEYWORDDETECTIONRESULT*)Result;

        // Do something with the result data to determine return parameters
        // PostProcessResult(contosoResult->ContosoDetectorResultData, contosoResult->KeywordStartTimestamp, contosoResult->KeywordStopTimestamp);

        *KeywordId = KwVoiceAssistant;
        *LangId = 0x0409;
        *pIsUserMatch = FALSE;

        *KeywordStartPerformanceCounterValue = 0;
        *KeywordEndPerformanceCounterValue = 0;

        return S_OK;
    }
};

CoCreatableClass(KeywordDetectorContosoAdapter);

class EventDetectorContosoAdapter : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IEventDetectorOemAdapter>
{
public:
    EventDetectorContosoAdapter()
    {
    }

    STDMETHODIMP GetCapabilities(
        _Out_ EVENTFEATURES* GlobalFeatureSupport,
        _Outptr_ LANGID** LangIds,
        _Out_ ULONG* NumLanguages,
        _Out_ ULONG* NumUserRecordings,
        _Outptr_ WAVEFORMATEX** ppFormat)
    {
        const WAVEFORMATEX waveFormat = { WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0 };

        *ppFormat = (WAVEFORMATEX *)CoTaskMemAlloc(sizeof(WAVEFORMATEX));
        if (*ppFormat == nullptr)
        {
            return E_OUTOFMEMORY;
        }

        memcpy(*ppFormat, &waveFormat, sizeof(WAVEFORMATEX));

        *GlobalFeatureSupport = EVENTFEATURES_NoEventFeatures;

        *LangIds = (LANGID *)CoTaskMemAlloc(sizeof(LANGID));

        if (*LangIds == nullptr)
        {
            return E_OUTOFMEMORY;
        }

        **LangIds = 0x0409;
        *NumLanguages = 1;
        *NumUserRecordings = 0;

        return S_OK;
    }

    STDMETHODIMP GetCapabilitiesForLanguage(
        _In_ LANGID LangId,
        _Outptr_ DETECTIONEVENT** EventIds,
        _Out_ ULONG* NumEvents)
    {
        if (LangId == 0x0409)
        {
                DETECTIONEVENT events[] = { { CONTOSO_KEYWORD1, EVENTFEATURES_NoEventFeatures, {0}, L"Contoso 1", TRUE },
                                            { CONTOSO_KEYWORD2, EVENTFEATURES_NoEventFeatures, {0}, L"Contoso 2", TRUE } };

                *EventIds = (DETECTIONEVENT *)CoTaskMemAlloc(sizeof(events));
                if (*EventIds == nullptr)
                {
                    return E_OUTOFMEMORY;
                }

                memcpy(*EventIds, &events, sizeof(events));
                *NumEvents = 2;
        }
        else
        {
            return E_INVALIDARG;
        }

        return S_OK;
    }

    STDMETHODIMP VerifyUserEventData(
        _In_ IStream* ModelData,
        _In_ WAVEFORMATEX* UserRecording,
        _In_ DETECTIONEVENTSELECTOR EventSelector,
        _In_ LONG EventEndBytePos)
    {
        UNREFERENCED_PARAMETER(ModelData);
        UNREFERENCED_PARAMETER(UserRecording);
        UNREFERENCED_PARAMETER(EventSelector);
        UNREFERENCED_PARAMETER(EventEndBytePos);

        return E_NOTIMPL;
    }

    STDMETHODIMP ComputeAndAddUserModelData(
        _Inout_ IStream* ModelData,
        _In_ DETECTIONEVENTSELECTOR EventSelector,
        _In_ LONG* EventEndBytePos,
        _In_ WAVEFORMATEX** UserRecordings,
        _In_ ULONG NumUserRecordings)
    {
        UNREFERENCED_PARAMETER(ModelData);
        UNREFERENCED_PARAMETER(EventSelector);
        UNREFERENCED_PARAMETER(EventEndBytePos);
        UNREFERENCED_PARAMETER(UserRecordings);
        UNREFERENCED_PARAMETER(NumUserRecordings);

        return E_NOTIMPL;
    }

    STDMETHODIMP BuildArmingPatternData(
        _In_ IStream* UserModelData,
        _In_ DETECTIONEVENTSELECTOR* EventSelectors,
        _In_ ULONG NumEventSelectors,
        _Outptr_ SOUNDDETECTOR_PATTERNHEADER** ppPatternData)
    {
        CONTOSO_KEYWORDCONFIGURATION *pPatternData = nullptr;

        UNREFERENCED_PARAMETER(UserModelData);

        if (NumEventSelectors > 2)
        {
            return E_INVALIDARG;
        }

        if ((EventSelectors[0].Event.EventId != CONTOSO_KEYWORD1 && EventSelectors[0].Event.EventId != CONTOSO_KEYWORD2) || 
            (EventSelectors[0].UserId != 0) || (EventSelectors[0].LangId != 0x0409))
        {
            return E_INVALIDARG;
        }

        pPatternData = (CONTOSO_KEYWORDCONFIGURATION*)CoTaskMemAlloc(sizeof(CONTOSO_KEYWORDCONFIGURATION));
        if (pPatternData == nullptr)
        {
            return E_OUTOFMEMORY;
        }

        pPatternData->Header.Size = sizeof(*pPatternData);
        pPatternData->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER2;
        pPatternData->ContosoDetectorConfigurationData = 0x12345678;

        *ppPatternData = &pPatternData->Header;
        pPatternData = nullptr;

        return S_OK;
    }

    STDMETHODIMP ParseDetectionResultData(
        _In_ IStream* UserModelData,
        _In_ SOUNDDETECTOR_PATTERNHEADER* Result,
        _Outptr_ SOUNDDETECTOR_PATTERNHEADER** AssistantContext,
        _Out_ DETECTIONEVENTSELECTOR* EventSelector,
        _Out_ EVENTACTION* EventAction,
        _Out_ ULONG64* EventStartPerformanceCounterValue,
        _Out_ ULONG64* EventEndPerformanceCounterValue,
        _Outptr_ WCHAR** DebugOutput)
    {
        const CONTOSO_KEYWORDDETECTIONRESULT *contosoResult;

        UNREFERENCED_PARAMETER(UserModelData);
        UNREFERENCED_PARAMETER(DebugOutput);
        UNREFERENCED_PARAMETER(AssistantContext);

        if (Result->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER2 || Result->Size < sizeof(CONTOSO_KEYWORDDETECTIONRESULT))
        {
            return E_INVALIDARG;
        }

        contosoResult = (CONTOSO_KEYWORDDETECTIONRESULT*)Result;

        if (CONTOSO_KEYWORD1 == contosoResult->EventId)
        {
            wcscpy_s(EventSelector->Event.DisplayName, L"Contoso 1");
        }
        else if (CONTOSO_KEYWORD2 == contosoResult->EventId)
        {
            wcscpy_s(EventSelector->Event.DisplayName, L"Contoso 2");
        }
        else
        {
            return E_INVALIDARG;
        }

        // Fill in event action information for the actual detection, based on what has been armed.
        EventSelector->Event.EventId = contosoResult->EventId;
        EventSelector->Armed = TRUE;
        EventSelector->UserId = 0;
        EventSelector->LangId = 0x0409;

        EventAction->EventdActionType = EVENTACTIONTYPE_Accept;
        EventAction->EventActionContextType = EVENTACTIONCONTEXTTYPE_None;

        // Retrieve the event start/stop times for the payload
        *EventStartPerformanceCounterValue = contosoResult->KeywordStartTimestamp;
        *EventEndPerformanceCounterValue = contosoResult->KeywordStopTimestamp;

        return S_OK;
    }

    STDMETHODIMP_(void) ReportOSDetectionResult(
        _In_ DETECTIONEVENTSELECTOR EventSelector,
        _In_ EVENTACTION EventAction)
    {
        UNREFERENCED_PARAMETER(EventSelector);
        UNREFERENCED_PARAMETER(EventAction);
    }
};

CoCreatableClass(EventDetectorContosoAdapter);



<|start_filename|>audio/simpleaudiosample/Source/Inc/definitions.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    Definitions.h

Abstract:

    Header file for common stuff.
--*/

#ifndef _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_
#define _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_

#include <portcls.h>
#include <stdunk.h>
#include <ksdebug.h>
#include <ntintsafe.h>
#include <wdf.h>
#include <wdfminiport.h>
#include <Ntstrsafe.h>
#include "NewDelete.h"

//=============================================================================
// Defines
//=============================================================================

// Product Id
// {836BA6D1-3FF7-4411-8BCD-469553452DCE}
#define STATIC_PID_SIMPLEAUDIOSAMPLE\
    0x836ba6d1, 0x3ff7, 0x4411, 0x8b, 0xcd, 0x46, 0x95, 0x53, 0x45, 0x2d, 0xce
DEFINE_GUIDSTRUCT("836BA6D1-3FF7-4411-8BCD-469553452DCE", PID_SIMPLEAUDIOSAMPLE);
#define PID_SIMPLEAUDIOSAMPLE DEFINE_GUIDNAMED(PID_SIMPLEAUDIOSAMPLE)

// Pool tag used for SIMPLEAUDIOSAMPLE allocations
#define SIMPLEAUDIOSAMPLE_POOLTAG               'SASM'  

// Debug module name
#define STR_MODULENAME              "SIMPLEAUDIOSAMPLE: "

// Debug utility macros
#define D_FUNC                      4
#define D_BLAB                      DEBUGLVL_BLAB
#define D_VERBOSE                   DEBUGLVL_VERBOSE        
#define D_TERSE                     DEBUGLVL_TERSE          
#define D_ERROR                     DEBUGLVL_ERROR          
#define DPF                         _DbgPrintF
#define DPF_ENTER(x)                DPF(D_FUNC, x)

// Channel orientation
#define CHAN_LEFT                   0
#define CHAN_RIGHT                  1
#define CHAN_MASTER                 (-1)

// Dma Settings.
#define DMA_BUFFER_SIZE             0x16000

#define KSPROPERTY_TYPE_ALL         KSPROPERTY_TYPE_BASICSUPPORT | \
                                    KSPROPERTY_TYPE_GET | \
                                    KSPROPERTY_TYPE_SET

// Specific node numbers
#define DEV_SPECIFIC_VT_BOOL        9
#define DEV_SPECIFIC_VT_I4          10
#define DEV_SPECIFIC_VT_UI4         11

#define _100NS_PER_MILLISECOND      10000                       // number of 100ns units per millisecond

// Default volume settings.
#define VOLUME_STEPPING_DELTA       0x8000
#define VOLUME_SIGNED_MAXIMUM       0x00000000
#define VOLUME_SIGNED_MINIMUM       (-96 * 0x10000)

// Default peak meter settings
#define PEAKMETER_STEPPING_DELTA    0x1000
#define PEAKMETER_SIGNED_MAXIMUM    LONG_MAX
#define PEAKMETER_SIGNED_MINIMUM    LONG_MIN

#define VALUE_NORMALIZE_P(v, step) \
    ((((v) + (step)/2) / (step)) * (step))

#define VALUE_NORMALIZE(v, step) \
    ((v) > 0 ? VALUE_NORMALIZE_P((v), (step)) : -(VALUE_NORMALIZE_P(-(v), (step))))

#define VALUE_NORMALIZE_IN_RANGE_EX(v, min, max, step) \
    ((v) > (max) ? (max) : \
     (v) < (min) ? (min) : \
     VALUE_NORMALIZE((v), (step)))

// to normalize volume values.
#define VOLUME_NORMALIZE_IN_RANGE(v) \
    VALUE_NORMALIZE_IN_RANGE_EX((v), VOLUME_SIGNED_MINIMUM, VOLUME_SIGNED_MAXIMUM, VOLUME_STEPPING_DELTA)

// to normalize sample peak meter.
#define PEAKMETER_NORMALIZE_IN_RANGE(v) \
    VALUE_NORMALIZE_IN_RANGE_EX((v), PEAKMETER_SIGNED_MINIMUM, PEAKMETER_SIGNED_MAXIMUM, PEAKMETER_STEPPING_DELTA)

#define ALL_CHANNELS_ID             UINT32_MAX

// Macros to assist with pin instance counting
#define VERIFY_PIN_INSTANCE_RESOURCES_AVAILABLE(status, allocated, max) \
    status = (allocated < max) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES

#define ALLOCATE_PIN_INSTANCE_RESOURCES(allocated) \
    allocated++

#define FREE_PIN_INSTANCE_RESOURCES(allocated) \
    allocated--

// Define the value data type for supported sound detector patterns. Only
// one pattern type is supported in this sample.
typedef struct {
    KSMULTIPLE_ITEM MultipleItem;
    GUID            PatternType[1];
} CONTOSO_SUPPORTEDPATTERNSVALUE;

//=============================================================================
// Typedefs
//=============================================================================

// Flags to identify stream processing mode
typedef enum {
    CONNECTIONTYPE_TOPOLOGY_OUTPUT = 0,
    CONNECTIONTYPE_WAVE_OUTPUT     = 1
} CONNECTIONTYPE;

// Connection table for registering topology/wave bridge connection
typedef struct _PHYSICALCONNECTIONTABLE
{
    ULONG            ulTopology;
    ULONG            ulWave;
    CONNECTIONTYPE   eType;
} PHYSICALCONNECTIONTABLE, *PPHYSICALCONNECTIONTABLE;

//
// This is the structure of the portclass FDO device extension Nt has created
// for us.  We keep the adapter common object here.
//
struct IAdapterCommon;
typedef struct _PortClassDeviceContext              // 32       64      Byte offsets for 32 and 64 bit architectures
{
    ULONG_PTR m_pulReserved1[2];                    // 0-7      0-15    First two pointers are reserved.
    PDEVICE_OBJECT m_DoNotUsePhysicalDeviceObject;  // 8-11     16-23   Reserved pointer to our Physical Device Object (PDO).
    PVOID m_pvReserved2;                            // 12-15    24-31   Reserved pointer to our Start Device function.
    PVOID m_pvReserved3;                            // 16-19    32-39   "Out Memory" according to DDK.  
    IAdapterCommon* m_pCommon;                      // 20-23    40-47   Pointer to our adapter common object.
    PVOID m_pvUnused1;                              // 24-27    48-55   Unused space.
    PVOID m_pvUnused2;                              // 28-31    56-63   Unused space.

    // Anything after above line should not be used.
    // This actually goes on for (64*sizeof(ULONG_PTR)) but it is all opaque.
} PortClassDeviceContext;

//
// Major/MinorTarget to object casting.
//
#define MajorTarget_to_Obj(ptr) \
    reinterpret_cast<CMiniportWaveRT*>(ptr)
    
#define MinorTarget_to_Obj(ptr) \
    static_cast<CMiniportWaveRTStream*>(reinterpret_cast<PMINIPORTWAVERTSTREAM>(ptr))

//
// Global settings.
//
extern DWORD g_DoNotCreateDataFiles;
extern DWORD g_DisableBthScoBypass;
extern UNICODE_STRING g_RegistryPath;

//=============================================================================
// Function prototypes
//=============================================================================

// Generic topology handler
NTSTATUS PropertyHandler_Topology
( 
    _In_  PPCPROPERTY_REQUEST PropertyRequest 
);

// Default WaveFilter automation table.
// Handles the GeneralComponentId request.
NTSTATUS PropertyHandler_WaveFilter
(
    _In_ PPCPROPERTY_REQUEST PropertyRequest
);

NTSTATUS PropertyHandler_GenericPin
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
);

// common.h uses some of the above definitions.
#include "common.h"
#include "kshelper.h"

#endif // _SIMPLEAUDIOSAMPLE_DEFINITIONS_H_


<|start_filename|>usb/UcmUcsiAcpiSample/UcmUcsiAcpiSample/Acpi.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:

    Acpi.cpp

Abstract:

    UCSI Acpi transport functions.

Environment:

    Kernel-mode.

--*/

#include "Pch.h"
#include "Acpi.tmh"


namespace UcmUcsiAcpiClient
{

//
// ACPI _DSM method GUID value as described in the document "BIOS Implemetation of UCSI" at
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/bios-implementation-of-ucsi.pdf
// {6F8398C2-7CA4-11E4-AD36-631042B5008F}
//
DEFINE_GUID(GUID_UCSI_DSM,
0x6f8398c2, 0x7ca4, 0x11e4, 0xad, 0x36, 0x63, 0x10, 0x42, 0xb5, 0x00, 0x8f);

const ULONG UCSI_DSM_REVISION = 0;
const ULONG UCSI_DSM_FUNCTION_SUPPORTED_FUNCTIONS_INDEX = 0x0;
const ULONG UCSI_DSM_FUNCTION_SEND_DATA_INDEX = 0x1;
const ULONG UCSI_DSM_FUNCTION_RECEIVE_DATA_INDEX = 0x2;
const ULONG UCSI_DSM_FUNCTION_USB_DEVICE_CONTROLLER_STATUS_INDEX = 0x3;
const ULONG UCSI_DSM_FUNCTION_DISABLE_DATA_ROLE_CORRECTION_POLICY = 0x4;

//
// There is no formal requirement of timeout for _DMS method. 3 second timeout
// was inherited from the previous driver ucmucsi.sys.
//
const ULONG UCSI_DSM_EXECUTION_TIMEOUT_IN_MS = 3000;

PAGED_CODE_SEG
NTSTATUS
Acpi::Initialize(
    WDFDEVICE Device
    )
{
    NTSTATUS status;
    WDF_OBJECT_ATTRIBUTES attributes;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    m_OwningDevice = Device;

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        attributes.ParentObject = Device;

    status = WdfWaitLockCreate(&attributes, &m_CallbackLock);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[WDFDEVICE: 0x%p] WdfWaitLockCreate failed - %!STATUS!",
            Device, status);
        goto Exit;
    }

    status = STATUS_SUCCESS;

Exit:

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);

    return status;
}

PAGED_CODE_SEG
NTSTATUS
Acpi::PrepareHardware(
    PCONFIG Config
    )
{
    NTSTATUS status;
    PVOID mappedMemory;
    UCSI_VERSION version;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    //
    // Map the shared memory to the virtual space so that the driver can access it.
    //
    mappedMemory = MmMapIoSpaceEx(Config->DataBlockAddress,
                                  Config->DataBlockLength,
                                  PAGE_NOCACHE | PAGE_READWRITE);

#pragma prefast(suppress: __WARNING_REDUNDANT_POINTER_TEST, "API indicates failure by returning NULL")
    if (mappedMemory == nullptr)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TRACE_ERROR(TRACE_FLAG_PPM, "[Device: 0x%p] MmMapIoSpaceEx failed", m_OwningDevice);
        goto Exit;
    }

    m_UcsiDataBlock = (PUCSI_DATA_BLOCK) mappedMemory;
    m_MappedMemoryLength = Config->DataBlockLength;

    version.AsUInt16 = READ_REGISTER_USHORT(reinterpret_cast<volatile USHORT*> (&m_UcsiDataBlock->UcsiVersion));

    TRACE_INFO(TRACE_FLAG_PPM, "[Device: 0x%p] UCSI version Major: %hx Minor: %hx SubMinor: %hx",
        m_OwningDevice, version.MajorVersion, version.MinorVersion, version.SubMinorVersion);

    //
    // Query ACPI interface so that we can register notifications with ACPI. 
    //
    if (m_InterfaceQueried != false)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] ACPI interface already queried", m_OwningDevice);
        goto Exit;
    }

    status = WdfFdoQueryForInterface(m_OwningDevice,
                                     &GUID_ACPI_INTERFACE_STANDARD2,
                                     (PINTERFACE) &m_AcpiInterface,
                                     sizeof(ACPI_INTERFACE_STANDARD2),
                                     1,
                                     NULL);

    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] WdfFdoQueryForInterface for ACPI_INTERFACE_STANDARD2 failed - %!STATUS!", 
            m_OwningDevice, status);
        goto Exit;
    }

    m_InterfaceQueried = true;
    TRACE_INFO(TRACE_FLAG_ACPI, "[Device: 0x%p] ACPI prepare hardware completed", m_OwningDevice);

Exit:
    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
VOID
Acpi::ReleaseHardware()
{

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    if (m_UcsiDataBlock != nullptr)
    {
        MmUnmapIoSpace(m_UcsiDataBlock, m_MappedMemoryLength);
        m_UcsiDataBlock = nullptr;
        m_MappedMemoryLength = 0;
    }

    if (m_InterfaceQueried == false)
    {
        goto Exit;
    }

    m_AcpiInterface.InterfaceDereference(m_AcpiInterface.Context);
    RtlZeroMemory(&m_AcpiInterface, sizeof(m_AcpiInterface));

    m_InterfaceQueried = false;

    TRACE_INFO(TRACE_FLAG_ACPI, "[Device: 0x%p] ACPI release hardware completed", m_OwningDevice);

Exit:

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
}

PAGED_CODE_SEG
NTSTATUS
Acpi::EnumChildren(
    WDFMEMORY* EnumChildrenOutput
    )
{
    NTSTATUS status;
    WDFMEMORY inputMem;
    PACPI_ENUM_CHILDREN_INPUT_BUFFER inputBuf;
    size_t inputBufSize;
    WDF_MEMORY_DESCRIPTOR inputMemDesc;
    WDFMEMORY outputMem;
    PACPI_ENUM_CHILDREN_OUTPUT_BUFFER outputBuf;
    size_t outputBufSize;
    WDF_MEMORY_DESCRIPTOR outputMemDesc;
    WDF_OBJECT_ATTRIBUTES attributes;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    inputMem = WDF_NO_HANDLE;
    outputMem = WDF_NO_HANDLE;

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_OwningDevice;

    inputBufSize = sizeof(*inputBuf);
    status = WdfMemoryCreate(&attributes,
                             NonPagedPoolNx,
                             0,
                             inputBufSize,
                             &inputMem,
                             (PVOID*) &inputBuf);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] WdfMemoryCreate for %Iu bytes failed", 
            m_OwningDevice, inputBufSize);
        goto Exit;
    }

    RtlZeroMemory(inputBuf, inputBufSize);
    inputBuf->Signature = ACPI_ENUM_CHILDREN_INPUT_BUFFER_SIGNATURE;
    inputBuf->Flags = ENUM_CHILDREN_IMMEDIATE_ONLY;

    WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&inputMemDesc, inputMem, nullptr);

    outputBufSize = sizeof(*outputBuf);

    do
    {
        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        attributes.ParentObject = m_OwningDevice;

        status = WdfMemoryCreate(&attributes,
                                 NonPagedPoolNx,
                                 0,
                                 outputBufSize,
                                 &outputMem,
                                 (PVOID*) &outputBuf);
        if (!NT_SUCCESS(status))
        {
            TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] WdfMemoryCreate for %Iu bytes failed",
                m_OwningDevice, outputBufSize);
            goto Exit;
        }

        WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputMemDesc, outputMem, nullptr);

        status = WdfIoTargetSendIoctlSynchronously(WdfDeviceGetIoTarget(m_OwningDevice),
                                                   NULL,
                                                   IOCTL_ACPI_ENUM_CHILDREN,
                                                   &inputMemDesc,
                                                   &outputMemDesc,
                                                   nullptr,
                                                   nullptr);

        if (NT_SUCCESS(status))
        {
            if (outputBuf->Signature != ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE)
            {
                status = STATUS_ACPI_INVALID_DATA;
                TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] Invalid data in ACPI_ENUM_CHILDREN_OUTPUT_BUFFER", m_OwningDevice);
                goto Exit;
            }

            // There must be atleast one, because this device is included in the list.
            if (outputBuf->NumberOfChildren < 1)
            {
                status = STATUS_ACPI_INVALID_DATA;
                TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] No child devices in ACPI_ENUM_CHILDREN_OUTPUT_BUFFER", m_OwningDevice);
                goto Exit;
            }

            break;
        }

        if (status != STATUS_BUFFER_OVERFLOW)
        {
            TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] IOCTL_ACPI_ENUM_CHILDREN failed - %!STATUS!", m_OwningDevice, status);
            goto Exit;
        }

        if (outputBuf->Signature != ACPI_ENUM_CHILDREN_OUTPUT_BUFFER_SIGNATURE)
        {
            status = STATUS_ACPI_INVALID_DATA;
            TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] Invalid data in ACPI_ENUM_CHILDREN_OUTPUT_BUFFER", m_OwningDevice);
            goto Exit;
        }

        // NOTE: The NumberOfChildren returned by ACPI actually contains the required size
        // when the status returned is STATUS_BUFFER_OVERFLOW 
        outputBufSize = outputBuf->NumberOfChildren;
        WdfObjectDelete(outputMem);
        outputMem = WDF_NO_HANDLE;

#pragma warning(suppress:4127)
    } while (true);

    *EnumChildrenOutput = outputMem;

Exit:

    if (inputMem != WDF_NO_HANDLE)
    {
        WdfObjectDelete(inputMem);
    }

    if (!NT_SUCCESS(status) && (outputMem != WDF_NO_HANDLE))
    {
        WdfObjectDelete(outputMem);
    }

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
NTSTATUS 
Acpi::EvaluatePld(
    LPCSTR DeviceName,
    PACPI_PLD_BUFFER PldBuffer
    )
{
    NTSTATUS status;
    WDF_MEMORY_DESCRIPTOR inputMemDesc;
    ACPI_EVAL_INPUT_BUFFER_EX inputBuffer;
    size_t inputBufferSize;
    WDFMEMORY outputMemory;
    WDF_MEMORY_DESCRIPTOR outputMemDesc;
    PACPI_EVAL_OUTPUT_BUFFER outputBuffer;
    size_t outputBufferSize;
    size_t outputArgumentBufferSize;
    WDF_OBJECT_ATTRIBUTES attributes;

    PAGED_CODE();

    // @@BEGIN_NOTPUBLICCODE
    // TODO: Refactor so that _DSM evaluation and _PLD evaluation use a common helper routine.
    //
    // TODO: _PLD evaluation of multiple connectors can be optimized to reduce number of memory
    //       allocations. Right now, output memory will be allocated for each request. We can
    //       reuse a single allocation for all evaluations.
    //
    // TODO: When refactoring code, must keep in mind that it would be efficient to allow the
    //       caller to specify a good initial output buffer size, which would depend on the method
    //       being evaluated. Caller may have a good estimate of the buffer size required. Otherwise
    //       we'll end up making two calls (one to figure out the size, another for actual
    //       evaluation) everytime.
    // @@END_NOTPUBLICCODE

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    outputMemory = WDF_NO_HANDLE;

    inputBufferSize = sizeof(inputBuffer);
    RtlZeroMemory(&inputBuffer, inputBufferSize);

    inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE_EX;

    status = RtlStringCchPrintfA(inputBuffer.MethodName,
                                 sizeof(inputBuffer.MethodName),
                                 "%s._PLD",
                                 DeviceName);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] RtlStringCchPrintfA for creating method name failed - %!STATUS!", m_OwningDevice, status);
        goto Exit;
    }

    // @@BEGIN_NOTPUBLICCODE
    // TODO: Pick an intelligent initial size for the buffer, and then do try-and-try-again.
    // @@END_NOTPUBLICCODE

    outputArgumentBufferSize = 1024;
    outputBufferSize =
        FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
        outputArgumentBufferSize;

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_OwningDevice;

    status = WdfMemoryCreate(&attributes,
                             NonPagedPoolNx,
                             0,
                             outputBufferSize,
                             &outputMemory,
                             (PVOID*) &outputBuffer);

    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] WdfMemoryCreate failed for %Iu bytes - %!STATUS!", m_OwningDevice, outputBufferSize, status);
        goto Exit;
    }

    RtlZeroMemory(outputBuffer, outputBufferSize);

    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemDesc, &inputBuffer, (ULONG) inputBufferSize);
    WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputMemDesc, outputMemory, NULL);

    status = WdfIoTargetSendInternalIoctlSynchronously(
                 WdfDeviceGetIoTarget(m_OwningDevice),
                 NULL,
                 IOCTL_ACPI_EVAL_METHOD_EX,
                 &inputMemDesc,
                 &outputMemDesc,
                 NULL,
                 NULL);

    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] IOCTL_ACPI_EVAL_METHOD_EX for %s failed - %!STATUS!", m_OwningDevice, inputBuffer.MethodName, status);
        goto Exit;
    }

    if (outputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] ACPI_EVAL_OUTPUT_BUFFER signature is incorrect", m_OwningDevice);
        goto Exit;
    }

    if (outputBuffer->Count < 1)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _PLD for %s didn't return anything", m_OwningDevice, inputBuffer.MethodName);
        goto Exit;
    }

    if (outputBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _PLD for %s returned an unexpected argument of type %d", m_OwningDevice, inputBuffer.MethodName, outputBuffer->Argument[0].Type);
        goto Exit;
    }

    if (outputBuffer->Argument[0].DataLength < sizeof(*PldBuffer))
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] Unexpected _PLD buffer size for %s. Expected %Iu bytes, got %Iu bytes", m_OwningDevice, inputBuffer.MethodName, sizeof(*PldBuffer), outputBuffer->Argument[0].DataLength);
        goto Exit;
    }

    *PldBuffer = *((PACPI_PLD_BUFFER) outputBuffer->Argument[0].Data);

Exit:
    if (outputMemory != WDF_NO_HANDLE)
    {
        WdfObjectDelete(outputMemory);
    }

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
NTSTATUS
Acpi::CheckIfUsbDeviceControllerIsEnabled(
    bool& IsUsbDeviceControllerEnabled
    )
{
    NTSTATUS status;
    PACPI_EVAL_OUTPUT_BUFFER output;
    UCHAR supportFunctions;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    output = nullptr;

    status = EvaluateUcsiDsm(UCSI_DSM_FUNCTION_SUPPORTED_FUNCTIONS_INDEX, &output);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for supported functions failed - %!STATUS!", m_OwningDevice, status);
        goto Exit;
    }

    if (output->Count != 1)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for supported functions returned unexpected number of arguments %lu", m_OwningDevice, output->Count);
        goto Exit;
    }

    NT_ASSERT_ASSUME(output->Length == sizeof(ACPI_EVAL_OUTPUT_BUFFER));

    if (output->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for supported functions returned an unexpected argument of type %d", m_OwningDevice, output->Argument[0].Type);
        goto Exit;
    }

    supportFunctions = output->Argument[0].Data[0];

    //
    // Free the buffer since we will reuse "output" to evaluate other UCSI DSM functions.
    //

    delete[] output;
    output = nullptr;


    //
    // First, check if the platform disables the OS policy of data role correction.
    // If it's disabled, report UsbDeviceControllerEnabled==true to UcmUcsiCx, so that
    // it will not initiate DR_SWAP when being UFP.
    //

    if (TEST_BIT(supportFunctions, UCSI_DSM_FUNCTION_DISABLE_DATA_ROLE_CORRECTION_POLICY))
    {
        status = EvaluateUcsiDsm(UCSI_DSM_FUNCTION_DISABLE_DATA_ROLE_CORRECTION_POLICY, &output);
        if (!NT_SUCCESS(status))
        {
            TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for UCSI_DSM_FUNCTION_DISABLE_DATA_ROLE_CORRECTION_POLICY failed - %!STATUS!", m_OwningDevice, status);
            goto Exit;
        }

        if (output->Count != 1)
        {
            status = STATUS_ACPI_INVALID_DATA;
            TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for UCSI_DSM_FUNCTION_DISABLE_DATA_ROLE_CORRECTION_POLICY returned unexpected number of arguments %lu", m_OwningDevice, output->Count);
            goto Exit;
        }

        NT_ASSERT_ASSUME(output->Length == sizeof(ACPI_EVAL_OUTPUT_BUFFER));

        if (output->Argument[0].Argument)
        {
            status = STATUS_SUCCESS;
            IsUsbDeviceControllerEnabled = true;
            TRACE_INFO(TRACE_FLAG_ACPI, "[Device: 0x%p] Data role correction policy disabled.", m_OwningDevice);
            goto Exit;
        }

        //
        // Free the buffer since we may reuse "output" to evaluate another UCSI DSM function.
        //

        delete[] output;
        output = nullptr;
    }

    //
    // The platform hasn't disabled the OS policy for data role correction. Now, check
    // if the platform has a USB device controller, and return the corresponding 
    // UsbDeviceControllerEnabled value for UcmUcsiCx to switch on/off the OS policy 
    // for data role correction.
    //

    if (!TEST_BIT(supportFunctions, UCSI_DSM_FUNCTION_USB_DEVICE_CONTROLLER_STATUS_INDEX))
    {
        //
        // The function to query whether the device controller is enabled is not supported. Assume
        // that the controller is enabled.
        //

        TRACE_INFO(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM to query if USB device controller is enabled is not supported. Assuming that device controller is supported",
            m_OwningDevice);

        status = STATUS_SUCCESS;
        IsUsbDeviceControllerEnabled = true;
        goto Exit;
    }

    status = EvaluateUcsiDsm(UCSI_DSM_FUNCTION_USB_DEVICE_CONTROLLER_STATUS_INDEX, &output);
    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for USB device controller status failed - %!STATUS!", m_OwningDevice, status);
        goto Exit;
    }

    if (output->Count != 1)
    {
        status = STATUS_ACPI_INVALID_DATA;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] _DSM query for USB device controller status returned unexpected number of arguments %lu", m_OwningDevice, output->Count);
        goto Exit;
    }

    NT_ASSERT_ASSUME(output->Length == sizeof(ACPI_EVAL_OUTPUT_BUFFER));

    IsUsbDeviceControllerEnabled = (output->Argument[0].Argument) ? true : false;
    status = STATUS_SUCCESS;

Exit:

    if (output)
    {
        delete[] output;
        output = nullptr;
    }

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);

    return status;
}

PAGED_CODE_SEG
NTSTATUS
Acpi::EvaluateUcsiDsm(
    ULONG FunctionIndex,
    PACPI_EVAL_OUTPUT_BUFFER* Output
    )
{
    NTSTATUS status;
    WDFMEMORY inputMemory;
    WDF_MEMORY_DESCRIPTOR inputMemDesc;
    PACPI_EVAL_INPUT_BUFFER_COMPLEX inputBuffer;
    size_t inputBufferSize;
    size_t inputArgumentBufferSize;
    PACPI_METHOD_ARGUMENT argument;
    WDF_MEMORY_DESCRIPTOR outputMemDesc;
    PACPI_EVAL_OUTPUT_BUFFER outputBuffer;
    size_t outputBufferSize;
    size_t outputArgumentBufferSize;
    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_REQUEST_SEND_OPTIONS sendOptions;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    inputMemory = WDF_NO_HANDLE;
    outputBuffer = nullptr;

    inputArgumentBufferSize =
        ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) +
        ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
        ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
        ACPI_METHOD_ARGUMENT_LENGTH(0);

    inputBufferSize =
        FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
        inputArgumentBufferSize;

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = m_OwningDevice;

    status = WdfMemoryCreate(&attributes,
                             NonPagedPoolNx,
                             0,
                             inputBufferSize,
                             &inputMemory,
                             (PVOID*) &inputBuffer);

    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] WdfMemoryCreate failed for %Iu bytes - %!STATUS!",
            m_OwningDevice, inputBufferSize, status);
        goto Exit;
    }

    RtlZeroMemory(inputBuffer, inputBufferSize);

    inputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    inputBuffer->Size = (ULONG) inputArgumentBufferSize;
    inputBuffer->ArgumentCount = 4;
    inputBuffer->MethodNameAsUlong = (ULONG) 'MSD_';

    argument = &(inputBuffer->Argument[0]);
    ACPI_METHOD_SET_ARGUMENT_BUFFER(argument,
                                    &GUID_UCSI_DSM,
                                    sizeof(GUID_UCSI_DSM));

    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, UCSI_DSM_REVISION);

    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, FunctionIndex);

    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE_EX;
    argument->DataLength = 0;

    // @@BEGIN_NOTPUBLICCODE
    // TODO: Try-and-try-again approach so that it can fetch any size output buffer.
    // @@END_NOTPUBLICCODE

    outputArgumentBufferSize = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));
    outputBufferSize =
        FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
        outputArgumentBufferSize;

    outputBuffer = reinterpret_cast<PACPI_EVAL_OUTPUT_BUFFER>(new BYTE[outputBufferSize]);

    if (outputBuffer == nullptr)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] Memory allocation failed for %Iu bytes", m_OwningDevice, outputBufferSize);
        goto Exit;
    }

    WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&inputMemDesc, inputMemory, NULL);
    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputMemDesc, outputBuffer, (ULONG) outputBufferSize);

    WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions,
                                         WDF_REL_TIMEOUT_IN_MS(UCSI_DSM_EXECUTION_TIMEOUT_IN_MS));

    status = WdfIoTargetSendInternalIoctlSynchronously(
                 WdfDeviceGetIoTarget(m_OwningDevice),
                 NULL,
                 IOCTL_ACPI_EVAL_METHOD,
                 &inputMemDesc,
                 &outputMemDesc,
                 &sendOptions,
                 NULL);

    if (!NT_SUCCESS(status))
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] IOCTL_ACPI_EVAL_METHOD for _DSM failed - %!STATUS!", m_OwningDevice, status);
        goto Exit;
    }

    if (outputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE)
    {
        TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] ACPI_EVAL_OUTPUT_BUFFER signature is incorrect", m_OwningDevice);
        status = STATUS_ACPI_INVALID_DATA;
        goto Exit;
    }

Exit:

    if (inputMemory != WDF_NO_HANDLE)
    {
        WdfObjectDelete(inputMemory);
    }

    if (!NT_SUCCESS(status) || (Output == nullptr))
    {
        if (outputBuffer)
        {
            delete[] outputBuffer;
            outputBuffer = nullptr;
        }
    }
    else
    {
        *Output = outputBuffer;
    }

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
NTSTATUS
Acpi::SendData(
    const PUCSI_DATA_BLOCK DataBlock
    )
{
    NTSTATUS status;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

#if defined (_WIN64)

    WRITE_REGISTER_ULONG64(reinterpret_cast<volatile ULONG64*> (&m_UcsiDataBlock->Control),
        DataBlock->Control.AsUInt64);

#else

    WRITE_REGISTER_BUFFER_ULONG(reinterpret_cast<volatile ULONG*> (&m_UcsiDataBlock->Control),
        reinterpret_cast<PULONG> (&DataBlock->Control),
        sizeof(m_UcsiDataBlock->Control) / sizeof(ULONG));

#endif

    status = EvaluateUcsiDsm(UCSI_DSM_FUNCTION_SEND_DATA_INDEX,
                                  nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

Exit:

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
NTSTATUS
Acpi::ReceiveData (
    PUCSI_DATA_BLOCK DataBlockOut
    )
{
    NTSTATUS status;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    status = EvaluateUcsiDsm(UCSI_DSM_FUNCTION_RECEIVE_DATA_INDEX,
                             nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    // Right now there is no use case of copying the whole block. Just copying
    // CCI as of now since all the caller would want to know is whether the last
    // command has been completed.
    DataBlockOut->CCI.AsUInt32 = READ_REGISTER_ULONG(reinterpret_cast<volatile ULONG*> (&m_UcsiDataBlock->CCI));

Exit:

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
VOID
Acpi::NotificationCallback(
    PVOID Context,
    ULONG NotifyValue
    )
{
    PAGED_CODE();
    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    //
    // Get the Acpi object from the Context passed by the notification callback.
    //
    Acpi *acpi = reinterpret_cast<Acpi*>(Context);

    acpi->AcquireCallbackLock();
    if (acpi->m_CallbackContext.m_CallbackFunction)
    {
        acpi->InvokeCallback(NotifyValue);
    }
    acpi->ReleaseCallbackLock();

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
}

PAGED_CODE_SEG
NTSTATUS
Acpi::RegisterNotificationCallbackInternal(
    _In_ PACPI_NOTIFICATION_CALLBACK_CONTEXT CallbackContext
    )
{
    NTSTATUS status;

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    status = m_AcpiInterface.RegisterForDeviceNotifications(m_AcpiInterface.Context,
                    Acpi::NotificationCallback, this);
    if (!NT_SUCCESS(status))
    {
       TRACE_ERROR(TRACE_FLAG_ACPI, "[Device: 0x%p] Registering for ACPI notifications failed - %!STATUS!",
           m_OwningDevice, status);
        goto Exit;
    }

    AcquireCallbackLock();
    
    m_CallbackContext.m_PpmObject = CallbackContext->m_PpmObject;
    m_CallbackContext.m_CallbackFunction = CallbackContext->m_CallbackFunction;

    ReleaseCallbackLock();

    status = STATUS_SUCCESS;

Exit:

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
    return status;
}

PAGED_CODE_SEG
VOID
Acpi::UnRegisterNotificationCallback()
{
    PAGED_CODE();
    TRACE_FUNC_ENTRY(TRACE_FLAG_ACPI);

    //
    // Acquire the lock to synchronize with outstanding callbacks, and then clear the callback
    // pointer to prevent making additional callbacks. The ACPI callback may still fire, because the
    // ACPI driver does not guarantee that it won't make additional calls after
    // UnregisterForDeviceNotifications has returned, so clearing the callback pointer prevents us
    // from accidentally making more callbacks after this function returns.
    //
    AcquireCallbackLock();

    m_CallbackContext.m_PpmObject = nullptr;
    m_CallbackContext.m_CallbackFunction = nullptr;

    ReleaseCallbackLock();

    m_AcpiInterface.UnregisterForDeviceNotifications(m_AcpiInterface.Context);

    TRACE_INFO(TRACE_FLAG_ACPI, "[Device: 0x%p] Unregistered ACPI notifications", m_OwningDevice);

    TRACE_FUNC_EXIT(TRACE_FLAG_ACPI);
}

}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXHelper.cpp<|end_filename|>
#pragma once

#include "CAPXHelper.h"

bool IsCapXAPO(GUID clsid)
{
    bool bIsCapXAPO = false;

    wil::com_ptr_nothrow<IAudioProcessingObject> apo;
    if(SUCCEEDED(CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IAudioProcessingObject), reinterpret_cast<void**>(&apo))))
    {
        wil::com_ptr_nothrow<IAudioSystemEffects3> capXAPO;
        bIsCapXAPO = wil::try_com_query_to(apo, &capXAPO);
    }

    return bIsCapXAPO;
}

void GetClsidsFromVar(const PROPVARIANT& var, std::vector<GUID>& guids, _Outptr_ PWSTR* ppszFormattedClsids, std::vector<GUID>& capxGuids, _Outptr_ PWSTR* ppszCapXFormattedClsids)
{
    WCHAR formattedClsids[1024] = {};
    PWSTR writeLocation = formattedClsids;
    size_t remainingChars = ARRAYSIZE(formattedClsids);
    const PWSTR formatSeparator = L"|%s";
    bool bIsCapXAPO;
    WCHAR capXformattedClsids[1024] = {};
    PWSTR capXwriteLocation = capXformattedClsids;
    size_t capXremainingChars = ARRAYSIZE(capXformattedClsids);

    for (UINT i = 0; i < var.calpwstr.cElems; i++)
    {
        // populate vector
        GUID clsid = {};
        if (FAILED(CLSIDFromString(var.calpwstr.pElems[i], &clsid)))
        {
            return;
        }

        bIsCapXAPO = IsCapXAPO(clsid);
        try
        {
            bIsCapXAPO ? capxGuids.push_back(clsid) : guids.push_back(clsid);
        }
        CATCH_LOG();

        // build string to log
        if (FAILED(bIsCapXAPO ? StringCchPrintfEx(capXwriteLocation, capXremainingChars, &capXwriteLocation, &capXremainingChars, 0, formatSeparator, var.calpwstr.pElems[i])
                              : StringCchPrintfEx(writeLocation, remainingChars, &writeLocation, &remainingChars, 0, formatSeparator, var.calpwstr.pElems[i])))
        {
            return;
        }
    }

    wil::unique_cotaskmem_string spFormattedClsids = wil::make_cotaskmem_string_nothrow(formattedClsids+1);
    if (spFormattedClsids)
    {
        *ppszFormattedClsids = spFormattedClsids.release();
    }

    wil::unique_cotaskmem_string spCapXFormattedClsids = wil::make_cotaskmem_string_nothrow(capXformattedClsids+1);
    if (spCapXFormattedClsids)
    {
        *ppszCapXFormattedClsids = spCapXFormattedClsids.release();
    }
}


bool EndpointContainsCapx(IMMDevice* pDevice)
{
    // some endpoints have an effects property store
    // grab properties from that
    
    GUID clsidSfx = {};
    bool bIsCapXSfx = false;
    GUID clsidMfx = {};
    bool bIsCapXMfx = false;
    GUID clsidEfx = {};
    bool bIsCapXEfx = false;

    // legacy drivers
    GUID clsidLfx = {};
    bool bIsCapXLfx = false;
    GUID clsidGfx = {};
    bool bIsCapXGfx = false;

    // offload pin
    GUID clsidOffloadSfx = {};
    bool bIsCapXOffloadSfx = false;
    GUID clsidOffloadMfx = {};
    bool bIsCapXOffloadMfx = false;

    // keyword spotter pin
    GUID clsidKeywordSfx = {};
    bool bIsCapXKeywordSfx = false;
    GUID clsidKeywordMfx = {};
    bool bIsCapXKeywordMfx = false;
    GUID clsidKeywordEfx = {};
    bool bIsCapXKeywordEfx = false;

    // // composite effects
    std::vector<GUID> clsidsCompositeSfx;
    wil::unique_cotaskmem_string clsidsCompositeSfxStr;
    std::vector<GUID> clsidsCompositeMfx;
    wil::unique_cotaskmem_string clsidsCompositeMfxStr;
    std::vector<GUID> clsidsCompositeEfx;
    wil::unique_cotaskmem_string clsidsCompositeEfxStr;
    // // CapX composite effects
    std::vector<GUID> clsidsCapXCompositeSfx;
    wil::unique_cotaskmem_string clsidsCapXCompositeSfxStr;
    std::vector<GUID> clsidsCapXCompositeMfx;
    wil::unique_cotaskmem_string clsidsCapXCompositeMfxStr;
    std::vector<GUID> clsidsCapXCompositeEfx;
    wil::unique_cotaskmem_string clsidsCapXCompositeEfxStr;

    wil::com_ptr_nothrow<IMMEndpointInternal> spMMEndpointInternal;
    if (wil::try_com_query_to(pDevice, &spMMEndpointInternal))
    {
        wil::com_ptr_nothrow<IPropertyStore> spFxProperties;
        if ((S_OK == spMMEndpointInternal->TryOpenFXPropertyStore(STGM_READ, &spFxProperties)) && spFxProperties)
        {
            wil::unique_prop_variant varClsidSfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_StreamEffectClsid, &varClsidSfx)) && varClsidSfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidSfx.pwszVal, &clsidSfx);
                bIsCapXSfx = IsCapXAPO(clsidSfx);
            }

            wil::unique_prop_variant varClsidMfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_ModeEffectClsid, &varClsidMfx)) && varClsidMfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidMfx.pwszVal, &clsidMfx);
                bIsCapXMfx = IsCapXAPO(clsidMfx);
            }

            wil::unique_prop_variant varClsidEfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_EndpointEffectClsid, &varClsidEfx)) && varClsidEfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidEfx.pwszVal, &clsidEfx);
                bIsCapXEfx = IsCapXAPO(clsidEfx);
            }

            // legacy drivers
            wil::unique_prop_variant varClsidLfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_PreMixEffectClsid, &varClsidLfx)) && varClsidLfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidLfx.pwszVal, &clsidLfx);
                bIsCapXLfx = IsCapXAPO(clsidLfx);
            }

            wil::unique_prop_variant varClsidGfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_PostMixEffectClsid, &varClsidGfx)) && varClsidGfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidGfx.pwszVal, &clsidGfx);
                bIsCapXGfx = IsCapXAPO(clsidGfx);
            }

            // offload
            wil::unique_prop_variant varClsidOffloadSfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_Offload_StreamEffectClsid, &varClsidOffloadSfx)) && varClsidOffloadSfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidOffloadSfx.pwszVal, &clsidOffloadSfx);
                bIsCapXOffloadSfx = IsCapXAPO(clsidOffloadSfx);
            }

            wil::unique_prop_variant varClsidOffloadMfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_Offload_ModeEffectClsid, &varClsidOffloadMfx)) && varClsidOffloadMfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidOffloadMfx.pwszVal, &clsidOffloadMfx);
                bIsCapXOffloadMfx = IsCapXAPO(clsidOffloadMfx);
            }

            // keyword
            wil::unique_prop_variant varClsidKeywordSfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_KeywordDetector_StreamEffectClsid, &varClsidKeywordSfx)) && varClsidKeywordSfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidKeywordSfx.pwszVal, &clsidKeywordSfx);
                bIsCapXKeywordSfx = IsCapXAPO(clsidKeywordSfx);
            }

            wil::unique_prop_variant varClsidKeywordMfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_KeywordDetector_ModeEffectClsid, &varClsidKeywordMfx)) && varClsidKeywordMfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidKeywordMfx.pwszVal, &clsidKeywordMfx);
                bIsCapXKeywordMfx = IsCapXAPO(clsidKeywordMfx);
            }

            wil::unique_prop_variant varClsidKeywordEfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_FX_KeywordDetector_EndpointEffectClsid, &varClsidKeywordEfx)) && varClsidKeywordEfx.vt == VT_LPWSTR)
            {
                CLSIDFromString(varClsidKeywordEfx.pwszVal, &clsidKeywordEfx);
                bIsCapXKeywordEfx = IsCapXAPO(clsidKeywordEfx);
            }

            // composite effects
            wil::unique_prop_variant varClsidsCompositeSfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_CompositeFX_StreamEffectClsid, &varClsidsCompositeSfx))
                && varClsidsCompositeSfx.vt == (VT_VECTOR | VT_LPWSTR)
                && varClsidsCompositeSfx.calpwstr.cElems > 0)
            {
                GetClsidsFromVar(varClsidsCompositeSfx, clsidsCompositeSfx, &clsidsCompositeSfxStr, clsidsCapXCompositeSfx, &clsidsCapXCompositeSfxStr);
            }

            wil::unique_prop_variant varClsidsCompositeMfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_CompositeFX_ModeEffectClsid, &varClsidsCompositeMfx))
                && varClsidsCompositeMfx.vt == (VT_VECTOR | VT_LPWSTR)
                && varClsidsCompositeMfx.calpwstr.cElems > 0)
            {
                GetClsidsFromVar(varClsidsCompositeMfx, clsidsCompositeMfx, &clsidsCompositeMfxStr, clsidsCapXCompositeMfx, &clsidsCapXCompositeMfxStr);
            }

            wil::unique_prop_variant varClsidsCompositeEfx;
            if (SUCCEEDED(spFxProperties->GetValue(PKEY_CompositeFX_EndpointEffectClsid, &varClsidsCompositeEfx))
                && varClsidsCompositeEfx.vt == (VT_VECTOR | VT_LPWSTR)
                && varClsidsCompositeEfx.calpwstr.cElems > 0)
            {
                GetClsidsFromVar(varClsidsCompositeEfx, clsidsCompositeEfx, &clsidsCompositeEfxStr, clsidsCapXCompositeEfx, &clsidsCapXCompositeEfxStr);
            }
        }
    }

    return ( bIsCapXSfx || bIsCapXMfx || bIsCapXEfx || bIsCapXLfx || bIsCapXGfx || 
             bIsCapXOffloadSfx || bIsCapXOffloadMfx || bIsCapXKeywordSfx || bIsCapXKeywordMfx || bIsCapXKeywordEfx || 
             clsidsCapXCompositeSfx.size() > 0 || clsidsCapXCompositeMfx.size() > 0 || clsidsCapXCompositeMfx.size() > 0 );
}

void EndpointsWithCapx(std::vector<wil::com_ptr<IMMDevice>>& capxEndpoints)
{
    UINT cDevices = 0;
    com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    com_ptr_nothrow<IMMDeviceCollection> spDevices;

    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));
    VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spDevices));
    VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

    for (UINT i = 0; i < cDevices; i++)
    {
        wil::com_ptr<IMMDevice> pEndpoint = nullptr;

        VERIFY_SUCCEEDED(spDevices->Item(i, &pEndpoint));

        if (EndpointContainsCapx(pEndpoint.get()))
        {
            capxEndpoints.push_back(pEndpoint.get());
        }
    }
}

<|start_filename|>audio/sysvad/TabletAudioSample/hdmiwavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    hdmiwavtable.h

Abstract:

    Declaration of wave miniport tables for the hdmi endpoint.

    Note: the HDMI in this sample shows how to do h/w loopback 
          for non-offloaded devices.

--*/

#ifndef _SYSVAD_HDMIWAVTABLE_H_
#define _SYSVAD_HDMIWAVTABLE_H_


//=============================================================================
// Defines
//=============================================================================


#define HDMI_DEVICE_MAX_CHANNELS                 2       // Max Channels.

#define HDMI_HOST_MAX_CHANNELS                   2       // Max Channels.
#define HDMI_HOST_MIN_BITS_PER_SAMPLE            16      // Min Bits Per Sample
#define HDMI_HOST_MAX_BITS_PER_SAMPLE            16      // Max Bits Per Sample
#define HDMI_HOST_MIN_SAMPLE_RATE                44100   // Min Sample Rate
#define HDMI_HOST_MAX_SAMPLE_RATE                96000   // Max Sample Rate

#define HDMI_LOOPBACK_MAX_CHANNELS               HDMI_HOST_MAX_CHANNELS          // Must be equal to host pin's Max Channels.
#define HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE        HDMI_HOST_MIN_BITS_PER_SAMPLE   // Must be equal to host pin's Min Bits Per Sample
#define HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE        HDMI_HOST_MAX_BITS_PER_SAMPLE   // Must be equal to host pin's Max Bits Per Sample
#define HDMI_LOOPBACK_MIN_SAMPLE_RATE            HDMI_HOST_MIN_SAMPLE_RATE       // Must be equal to host pin's Min Sample Rate
#define HDMI_LOOPBACK_MAX_SAMPLE_RATE            HDMI_HOST_MAX_SAMPLE_RATE       // Must be equal to host pin's Max Sample Rate

#define HDMI_DOLBY_DIGITAL_MAX_CHANNELS          2       // Max Channels.
#define HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE   16      // Min Bits Per Sample
#define HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE   16      // Max Bits Per Sample
#define HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE       44100   // Min Sample Rate
#define HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE       48000   // Max Sample Rate

#define HDMI_DOLBY_MAT_MAX_CHANNELS              8       // Max Channels.
#define HDMI_DOLBY_MAT_MIN_BITS_PER_SAMPLE       16      // Min Bits Per Sample
#define HDMI_DOLBY_MAT_MAX_BITS_PER_SAMPLE       16      // Max Bits Per Sample
#define HDMI_DOLBY_MAT_MIN_SAMPLE_RATE           192000  // Min Sample Rate
#define HDMI_DOLBY_MAT_MAX_SAMPLE_RATE           192000  // Max Sample Rate

#define HDMI_DTS_MAX_CHANNELS                    2       // Max Channels.
#define HDMI_DTS_MIN_BITS_PER_SAMPLE             16      // Min Bits Per Sample
#define HDMI_DTS_MAX_BITS_PER_SAMPLE             16      // Max Bits Per Sample
#define HDMI_DTS_MIN_SAMPLE_RATE                 48000   // Min Sample Rate
#define HDMI_DTS_MAX_SAMPLE_RATE                 48000   // Max Sample Rate

#define HDMI_DTS_HD_MAX_CHANNELS                 8       // Max Channels.
#define HDMI_DTS_HD_MIN_BITS_PER_SAMPLE          16      // Min Bits Per Sample
#define HDMI_DTS_HD_MAX_BITS_PER_SAMPLE          16      // Max Bits Per Sample
#define HDMI_DTS_HD_MIN_SAMPLE_RATE              192000  // Min Sample Rate
#define HDMI_DTS_HD_MAX_SAMPLE_RATE              192000  // Max Sample Rate

//
// Max # of pin instances.
//
#define HDMI_MAX_INPUT_SYSTEM_STREAMS            2
#define HDMI_MAX_OUTPUT_LOOPBACK_STREAMS         MAX_OUTPUT_LOOPBACK_STREAMS


//=============================================================================
static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiHostPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                88200,
                352800,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                96000,
                384000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_5POINT1_SURROUND,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL)
        }
    },
    { // 5
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_5POINT1_SURROUND,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL)
        }
    },
    { // 6
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP)
        }
    },
    { // 7
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1_SURROUND,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP)
        }
    },
    { // 8
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_5POINT1_SURROUND,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS)
        }
    },
    { // 9
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD)
        }
    },
    { // 10
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1)
        }
    },
    { // 11
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2)
        }
    },
    { // 12
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20)
        }
    },
    { // 13
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT21),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                8,
                192000,
                3072000,
                16,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_7POINT1,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT21)
        }
    }
};

//
// Supported modes (only on streaming pins).
// Note: This pin does not support KSPROPERTY_PIN_PROPOSEDATAFORMAT2 
//       since none of the modes on this endpoint have a default format.
//
static
MODE_AND_DEFAULT_FORMAT HdmiHostPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        NULL, // just an example of no default format for this endpoint/mode   
    }
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
//
static 
PIN_DEVICE_FORMATS_AND_MODES HdmiPinDeviceFormatsAndModes[] = 
{
    {
        SystemRenderPin,
        HdmiHostPinSupportedDeviceFormats,
        SIZEOF_ARRAY(HdmiHostPinSupportedDeviceFormats),
        HdmiHostPinSupportedDeviceModes,
        SIZEOF_ARRAY(HdmiHostPinSupportedDeviceModes)
    },
    {
        RenderLoopbackPin,
        HdmiHostPinSupportedDeviceFormats,   // Must support all the formats supported by host pin
        SIZEOF_ARRAY(HdmiHostPinSupportedDeviceFormats),
        NULL,   // loopback doesn't support modes.
        0
    },
    {
        BridgePin,
        NULL,
        0,
        NULL,
        0
    }
};

//=============================================================================
static
KSDATARANGE_AUDIO HdmiPinDataRangesStream[] =
{
    { // 0 - PCM host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_HOST_MAX_CHANNELS,           
        HDMI_HOST_MIN_BITS_PER_SAMPLE,    
        HDMI_HOST_MAX_BITS_PER_SAMPLE,    
        HDMI_HOST_MIN_SAMPLE_RATE,            
        HDMI_HOST_MAX_SAMPLE_RATE             
    },
    { // 1 - PCM loopback
        {
            sizeof(KSDATARANGE_AUDIO),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_LOOPBACK_MAX_CHANNELS,           
        HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE,    
        HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE,    
        HDMI_LOOPBACK_MIN_SAMPLE_RATE,
        HDMI_LOOPBACK_MAX_SAMPLE_RATE
    },
    { // 2 - DOLBY-DIGITAL host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DOLBY_DIGITAL_MAX_CHANNELS,
        HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE,
        HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE,
        HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE,
        HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE
    },
    { // 3 - DOLBY-MLP host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DOLBY_MAT_MAX_CHANNELS,
        HDMI_DOLBY_MAT_MIN_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MAX_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MIN_SAMPLE_RATE,
        HDMI_DOLBY_MAT_MAX_SAMPLE_RATE
    },
    { // 4 - DTS host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DTS_MAX_CHANNELS,
        HDMI_DTS_MIN_BITS_PER_SAMPLE,
        HDMI_DTS_MAX_BITS_PER_SAMPLE,
        HDMI_DTS_MIN_SAMPLE_RATE,
        HDMI_DTS_MAX_SAMPLE_RATE
    },
    { // 5 - DTS-HD host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DTS_HD_MAX_CHANNELS,
        HDMI_DTS_HD_MIN_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MAX_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MIN_SAMPLE_RATE,
        HDMI_DTS_HD_MAX_SAMPLE_RATE
    },
    { // 6 - DTS:X E1 host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DTS_HD_MAX_CHANNELS,
        HDMI_DTS_HD_MIN_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MAX_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MIN_SAMPLE_RATE,
        HDMI_DTS_HD_MAX_SAMPLE_RATE
    },
    { // 7 - DTS:X E2 host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DTS_HD_MAX_CHANNELS,
        HDMI_DTS_HD_MIN_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MAX_BITS_PER_SAMPLE,
        HDMI_DTS_HD_MIN_SAMPLE_RATE,
        HDMI_DTS_HD_MAX_SAMPLE_RATE
    },
    { // 8 - DOLBY-MAT20 host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DOLBY_MAT_MAX_CHANNELS,
        HDMI_DOLBY_MAT_MIN_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MAX_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MIN_SAMPLE_RATE,
        HDMI_DOLBY_MAT_MAX_SAMPLE_RATE
    },
    { // 9 - DOLBY-MAT21 host
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT21),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        HDMI_DOLBY_MAT_MAX_CHANNELS,
        HDMI_DOLBY_MAT_MIN_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MAX_BITS_PER_SAMPLE,
        HDMI_DOLBY_MAT_MIN_SAMPLE_RATE,
        HDMI_DOLBY_MAT_MAX_SAMPLE_RATE
    }
};

static
PKSDATARANGE HdmiPinDataRangePointersStream[] =
{
    PKSDATARANGE(&HdmiPinDataRangesStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[2]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[3]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[4]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[5]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[6]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[7]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[8]),
    PKSDATARANGE(&PinDataRangeAttributeList),
    PKSDATARANGE(&HdmiPinDataRangesStream[9]),
    PKSDATARANGE(&PinDataRangeAttributeList)
};

static
PKSDATARANGE HdmiPinDataRangePointersLoopbackStream[] =
{
    PKSDATARANGE(&HdmiPinDataRangesStream[1])
};

//=============================================================================
static
KSDATARANGE HdmiPinDataRangesBridge[] =
{
    { // 0
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 1
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 2
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 3
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 4
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 5
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E1),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 6
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DTSX_E2),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 7
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT20),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    },
    { // 8
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MAT21),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE HdmiPinDataRangePointersBridge[] =
{
    &HdmiPinDataRangesBridge[0],
    &HdmiPinDataRangesBridge[1],
    &HdmiPinDataRangesBridge[2],
    &HdmiPinDataRangesBridge[3],
    &HdmiPinDataRangesBridge[4],
    &HdmiPinDataRangesBridge[5],
    &HdmiPinDataRangesBridge[6],
    &HdmiPinDataRangesBridge[7],
    &HdmiPinDataRangesBridge[8]
};


//=============================================================================
static
PCPIN_DESCRIPTOR HdmiWaveMiniportPins[] =
{
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM
    {
        HDMI_MAX_INPUT_SYSTEM_STREAMS,
        HDMI_MAX_INPUT_SYSTEM_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(HdmiPinDataRangePointersStream),
            HdmiPinDataRangePointersStream,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK
    {
        HDMI_MAX_OUTPUT_LOOPBACK_STREAMS,
        HDMI_MAX_OUTPUT_LOOPBACK_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(HdmiPinDataRangePointersLoopbackStream),
            HdmiPinDataRangePointersLoopbackStream,
            KSPIN_DATAFLOW_OUT,              
            KSPIN_COMMUNICATION_SINK,
            &KSNODETYPE_AUDIO_LOOPBACK,
            NULL,
            0
        }
    },
    // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(HdmiPinDataRangePointersBridge),
            HdmiPinDataRangePointersBridge,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
};

//=============================================================================
static
PCPROPERTY_ITEM HdmiPropertiesVolume[] =
{
    {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_VOLUMELEVEL,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_WaveFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiVolume, HdmiPropertiesVolume);

//=============================================================================
static
PCPROPERTY_ITEM HdmiPropertiesMute[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_MUTE,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_WaveFilter
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiMute, HdmiPropertiesMute);

//=============================================================================
static
PCPROPERTY_ITEM HdmiPropertiesPeakMeter[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_PEAKMETER2,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_WaveFilter
  },
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_CPU_RESOURCES,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_WaveFilter
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiPeakMeter, HdmiPropertiesPeakMeter);

//=============================================================================
static
PCNODE_DESCRIPTOR HdmiWaveMiniportNodes[] =
{
    // KSNODE_WAVE_SUM
    {
        0,                          // Flags
        NULL,                       // AutomationTable
        &KSNODETYPE_SUM,            // Type
        NULL                        // Name
    },
    // KSNODE_WAVE_VOLUME
    {
      0,                            // Flags
      &AutomationHdmiVolume,        // AutomationTable
      &KSNODETYPE_VOLUME,           // Type
      &KSAUDFNAME_WAVE_VOLUME       // Name
    },
    // KSNODE_WAVE_MUTE
    {
      0,                            // Flags
      &AutomationHdmiMute,          // AutomationTable
      &KSNODETYPE_MUTE,             // Type
      &KSAUDFNAME_WAVE_MUTE         // Name
    },
    // KSNODE_WAVE_PEAKMETER
    {
      0,                            // Flags
      &AutomationHdmiPeakMeter,     // AutomationTable
      &KSNODETYPE_PEAKMETER,        // Type
      &KSAUDFNAME_PEAKMETER         // Name
    }
};

C_ASSERT( KSNODE_WAVE_SUM == 0 );
C_ASSERT( KSNODE_WAVE_VOLUME  == 1 );
C_ASSERT( KSNODE_WAVE_MUTE    == 2 );
C_ASSERT( KSNODE_WAVE_PEAKMETER == 3 );


//=============================================================================
//
// Note: 
// 1) A sum node can be used to 'sum' multiple streams associated to different
//    instances of the same pin.  
// 2) Any node output has an implicit 'tee' node associated with it. Thus in the 
//    scenario below it is ok for the peak meter node to have two identical outputs.
//
//                       ----------------------------------------------------------
//                       |                                                        |      
//    System Pin 0       |    |-----|    |--------|   |------|   |-----------|    |--> 1 Loopback Pin
//   [instances:0...n]-->| -->| Sum |--->| Volume |-->| Mute |-->| PeakMeter |--> |       
//                       |    |-----|    |--------|   |------|   |-----------|    |--> 2 KSPIN_WAVE_RENDER2_SOURCE
//                       |                                                        | 
//                       ----------------------------------------------------------

static
PCCONNECTION_DESCRIPTOR HdmiWaveMiniportConnections[] =
{
    { PCFILTER_NODE,            KSPIN_WAVE_RENDER2_SINK_SYSTEM, KSNODE_WAVE_SUM,            1 },
    { KSNODE_WAVE_SUM,          0,                              KSNODE_WAVE_VOLUME,         1 },
    { KSNODE_WAVE_VOLUME,       0,                              KSNODE_WAVE_MUTE,           1 },
    { KSNODE_WAVE_MUTE,         0,                              KSNODE_WAVE_PEAKMETER,      1 },
    { KSNODE_WAVE_PEAKMETER,    2,                              PCFILTER_NODE,              KSPIN_WAVE_RENDER2_SINK_LOOPBACK },
    { KSNODE_WAVE_PEAKMETER,    0,                              PCFILTER_NODE,              KSPIN_WAVE_RENDER2_SOURCE },
};

//=============================================================================
static
PCPROPERTY_ITEM PropertiesHdmiWaveFilter[] =
{
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT,
        KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_WaveFilter
    },
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiWaveFilter, PropertiesHdmiWaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR HdmiWaveMiniportFilterDescriptor =
{
    0,                                              // Version
    &AutomationHdmiWaveFilter,                      // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                       // PinSize
    SIZEOF_ARRAY(HdmiWaveMiniportPins),             // PinCount
    HdmiWaveMiniportPins,                           // Pins
    sizeof(PCNODE_DESCRIPTOR),                      // NodeSize
    SIZEOF_ARRAY(HdmiWaveMiniportNodes),            // NodeCount
    HdmiWaveMiniportNodes,                          // Nodes
    SIZEOF_ARRAY(HdmiWaveMiniportConnections),      // ConnectionCount
    HdmiWaveMiniportConnections,                    // Connections
    0,                                              // CategoryCount
    NULL                                            // Categories  - use defaults (audio, render, capture)
};

#endif // _SYSVAD_HDMIWAVTABLE_H_



<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXThreadingFrameworkTests.cpp<|end_filename|>
#include "CAPXThreadingFrameworkTests.h"


bool CAPOThreadingFrameworkTest::setUpMethod()
{
    SLGetWindowsInformationDWORD(POLICY_KERNEL_ONECORE_DEVICE_FAMILY_ID, &m_dwPlatform);

    EndpointsWithCapx(m_capxEndpoints);

    return SetupSkipRTHeap();
}

bool CAPOThreadingFrameworkTest::tearDownMethod()
{
    return CleanupSkipRTHeap();
}

using malloca_deleter = wil::function_deleter<decltype(&::_freea), ::_freea>;
template <typename T>
using unique_malloca_ptr = wistd::unique_ptr<T, malloca_deleter>;
int g_mmThreadCount = 0;

class CEventParser : public IEtwEventHandler
{
public:
    int GetCountOfAPOThreads() { return m_countOfAPOThreads; }

private:

    int m_countOfAPOThreads = 0;

    std::wstring QueryProcessName(const DWORD pid)
    {
        wil::unique_handle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
        if(NULL == hProcess)
        {
            return L"err " + std::to_wstring(GetLastError());
        }
        else
        {
            WCHAR szProcessName[1024];
            DWORD cchSize = ARRAYSIZE(szProcessName);
            if (0 == QueryFullProcessImageNameW(hProcess.get(), 0, szProcessName, &cchSize))
            {
                return L"err " + std::to_wstring(GetLastError());
            }

            PWSTR fileName = szProcessName + cchSize;
            while (fileName != szProcessName && *fileName != L'\\') fileName--;
            
            return (*fileName == L'\\') ? fileName + 1 : fileName;
        }
    }

    void OnEvent(PEVENT_RECORD pEventRecord)
    {
        WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
        [&]() {
            ULONG bufferSize = 0;
            if (TdhGetEventInformation(pEventRecord, 0, nullptr, nullptr, &bufferSize) == ERROR_INSUFFICIENT_BUFFER)
            {
                unique_malloca_ptr<TRACE_EVENT_INFO> eventInfo(reinterpret_cast<PTRACE_EVENT_INFO>(_malloca(bufferSize)));
                VERIFY_IS_NOT_NULL(eventInfo);
                RETURN_IF_WIN32_ERROR(TdhGetEventInformation(pEventRecord, 0, nullptr, eventInfo.get(), &bufferSize));
     
                // Threading implemented is the temporary name that the threading framework will be called until the final logs are created
                if (_wcsicmp(L"Threading implemented", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE*>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    m_countOfAPOThreads++;
                }
                auto mmThreadMock = Mock10::Mock::Function(&AvSetMmThreadCharacteristics, [](LPCWSTR taskName, LPDWORD taskIndex) {
                    g_mmThreadCount++;
                    return AvSetMmThreadCharacteristics(taskName, taskIndex);
                });
                if (_wcsicmp(L"CreateThread", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE*>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    g_mmThreadCount++;
                }
            }
            return S_OK;
        }();
    }
};

// {6e7b1892-5288-5fe5-8f34-e3b0dc671fd2}
static const GUID AudioSesTelemetryProvider =
{ 0x6e7b1892, 0x5288, 0x5fe5, 0x8f, 0x34, 0xe3, 0xb0, 0xdc, 0x67, 0x1f, 0xd2 };

void CAPOThreadingFrameworkTest::ThreadingFrameworkTest()
{
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);

    if (m_dwPlatform != DEVICEFAMILYINFOENUM_WINDOWS_CORE && m_capxEndpoints.size() == 0)
    {
        Log::Result(TestResults::Skipped, L"There are no CAPX APOs to test");
        return;
    }

    CEtwListener listener;
    CEventParser parser;

    VERIFY_SUCCEEDED(listener.StartTraceSession(L"ThreadingFrameworkTest", static_cast<IEtwEventHandler *>(&parser)));

    // Enable Microsoft.Windows.Audio.Client
    VERIFY_SUCCEEDED(listener.EnableProvider(AudioSesTelemetryProvider, TRACE_LEVEL_INFORMATION, 8));

    auto stopListener = scope_exit([&] {
        listener.StopTraceSession();
    });

    wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));

    RunStressTest(FIVE_MIN_IN_SEC, m_capxEndpoints);

    LOG_OUTPUT(L"Number of APO threads created: %d", parser.GetCountOfAPOThreads());
    VERIFY_IS_TRUE(g_mmThreadCount == 0);
}

<|start_filename|>gnss/gnssUmdf/FixSession.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    Fixsession.h

Abstract:

    This module contains the type definitions of the fix logic of the Umdf Gnss Driver.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

//
// This class implements the Fix sessions and contains logic to provide position within GNSS driver.
//
class CFixSession
{
public:
    CFixSession();
    ~CFixSession();

    void AcquireSingleFix();
    NTSTATUS Initialize(_In_ WDFQUEUE Queue);
    NTSTATUS StartAcquiringFix(_In_ PGNSS_FIXSESSION_PARAM FixSessionParameters);
    NTSTATUS StopAcquiringFix(_In_ PGNSS_STOPFIXSESSION_PARAM StopFixSessionParam);
    NTSTATUS ModifyAcquiringFix(_In_ PGNSS_FIXSESSION_PARAM ModifyFixSessionParam);
    NTSTATUS GetFix(_In_ WDFREQUEST Request);
    NTSTATUS UpdateCurrentPosition(_In_ PGNSS_FIXDATA GnssFix);

    NTSTATUS RetrievePosition(_Inout_ PGNSS_FIXDATA GnssFixData);
    NTSTATUS RetrievePositionFromPredefinedValue(_Out_ PGNSS_FIXDATA GnssFixData);

private:
    enum STATE
    {
        ACQUIRING = 0,
        STOPPED
    };

    ULONG _Id = INVALID_SESSION_ID;
    STATE _State = STOPPED;
    GNSS_FIXSESSION_PARAM _FixParameter = {};
    WDFQUEUE _FixAcquisitionRequestQueue = nullptr;
    bool _HasCachedFix = false;
    GNSS_FIXDATA _GnssPosition = {};
    HANDLE _StopFixEvent = nullptr;
    PTP_WORK _ThreadpoolWork = nullptr;
    CRITICAL_SECTION _Lock;

    NTSTATUS CompleteRequestWithGnssFix(_In_ WDFREQUEST Request, PGNSS_FIXDATA GnssFix);
    NTSTATUS ReportGnssFix(_In_ PGNSS_FIXDATA GnssFix);
};


<|start_filename|>storage/msdsm/src/utils.c<|end_filename|>

/*++

Copyright (C) 2004-2010  Microsoft Corporation

Module Name:

    utils.c

Abstract:

    This driver is the Microsoft Device Specific Module (DSM).
    It exports behaviours that mpio.sys will use to determine how to
    multipath SPC-3 compliant devices.

    This file contains utility routines.

Environment:

    kernel mode only

Notes:

--*/

#include "precomp.h"

#ifdef DEBUG_USE_WPP
#include "utils.tmh"
#endif

#pragma warning (disable:4305)

extern BOOLEAN DoAssert;

#ifdef ALLOC_PRAGMA
    #pragma alloc_text(PAGE, DsmpBuildDeviceNameLegacyPage0x80)
    #pragma alloc_text(PAGE, DsmpBuildDeviceName)
    #pragma alloc_text(PAGE, DsmpApplyDeviceNameCorrection)
    #pragma alloc_text(PAGE, DsmpOpenLoadBalanceSettingsKey)
    #pragma alloc_text(PAGE, DsmpQueryLBPolicyForDevice)
    #pragma alloc_text(PAGE, DsmpOpenTargetsLoadBalanceSettingKey)
    #pragma alloc_text(PAGE, DsmpOpenDsmServicesParametersKey)
#endif

_Success_(return != NULL)
__drv_allocatesMem(Mem)
_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL))
_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL))
_When_(((PoolType&0x2))!=0,
    __drv_reportError("Must succeed pool allocations are forbidden. "
    "Allocation failures cause a system crash"))
_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0,
    _Post_maybenull_ _Must_inspect_result_)
_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0,
    _Post_notnull_ )
_When_((PoolType&NonPagedPoolMustSucceed)!=0,
    __drv_reportError("Must succeed pool allocations are forbidden. "
                      "Allocation failures cause a system crash"))
_Post_writable_byte_size_(NumberOfBytes)
PVOID
DsmpAllocatePool(
    _In_ _Strict_type_match_ IN POOL_TYPE PoolType,
    _In_ IN SIZE_T NumberOfBytes,
    _In_ IN ULONG Tag
    )
/*+++

Routine Description :

    Allocates memory from the specified pool using the given tag.
    If the allocation is successful, the entire buffer will be zeroed.

Arguements:

    PoolType - Pool to allocate from (NonPaged, Paged, etc)
    NumberOfBytes - Size of the buffer to allocate
    Tag - Tag (DSM_TAG_XXX) to be used for this allocation.
          These tags are defined in msdsm.h

Return Value:

    Pointer to the buffer if allocation is successful
    NULL otherwise

--*/
{
    PVOID Block = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAllocatePool (Tag %u): Entering function.\n",
                Tag));

    #pragma warning(suppress: 28118) // False-positive; PoolType is simply passed through
    Block = ExAllocatePoolZero(PoolType, NumberOfBytes, Tag);

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAllocatePool (Tag %u): Exiting function with allocated block %p.\n",
                Tag,
                Block));

    return Block;
}


_Success_(return != NULL)
_Post_maybenull_
_Must_inspect_result_
__drv_allocatesMem(Mem)
_Post_writable_byte_size_(*BytesAllocated)
_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL))
_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL))
_When_((PoolType&NonPagedPoolMustSucceed)!=0,
    __drv_reportError("Must succeed pool allocations are forbidden. "
                      "Allocation failures cause a system crash"))
PVOID
#pragma warning(suppress:28195) // Allocation is not guaranteed, caller needs to check return value
DsmpAllocateAlignedPool(
    _In_ IN POOL_TYPE PoolType,
    _In_ IN SIZE_T NumberOfBytes,
    _In_ IN ULONG AlignmentMask,
    _In_ IN ULONG Tag,
    _Out_ OUT SIZE_T *BytesAllocated
    )
/*+++

Routine Description :

    Allocates memory from the specified pool using the given tag and alignment requirement.
    If the allocation is successful, the entire buffer will be zeroed.

Arguements:

    PoolType - Pool to allocate from (NonPaged, Paged, etc)
    NumberOfBytes - Size of the buffer to allocate
    AlignmentMask - Alignment requirement specified by the device
    Tag - Tag (DSM_TAG_XXX) to be used for this allocation.
          These tags are defined in msdsm.h
    BytesAllocated - Returns the number of bytes allocated, if the routine was successful

Return Value:

    Pointer to the buffer if allocation is successful
    NULL otherwise

--*/
{
    PVOID Block = NULL;
    UINT_PTR align64 = (UINT_PTR)AlignmentMask;
    ULONG totalSize = (ULONG)NumberOfBytes;
    NTSTATUS status = STATUS_SUCCESS;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAllocateAlignedPool (Tag %u): Entering function.\n",
                Tag));

    if (BytesAllocated == NULL) {

        status = STATUS_INVALID_PARAMETER;
        goto __Exit;
    }

    *BytesAllocated = 0;

    if (AlignmentMask) {

        status = RtlULongAdd((ULONG)NumberOfBytes, AlignmentMask, &totalSize);
    }

    if (NT_SUCCESS(status)) {

	#pragma warning(suppress: 6014 28118) // Block isn't leaked, this function is marked as an allocator; PoolType is simply passed through
        Block = ExAllocatePoolZero(PoolType, totalSize, Tag);

        if (Block != NULL) {

            if (AlignmentMask) {

                Block = (PVOID)(((UINT_PTR)Block + align64) & ~align64);
            }
        } else {

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

__Exit:

    if (NT_SUCCESS(status)) {

        *BytesAllocated = totalSize;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAllocateAlignedPool (Tag %u): Exiting function with allocated block %p.\n",
                Tag,
                Block));

    return Block;
}


_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
DsmpFreePool(
    _In_opt_ __drv_freesMem(Mem) IN PVOID Block
    )
/*+++

Routine Description :

    Frees the block passed in.

Arguements:

    Block - pointer to the memory to free.

Return Value:

    Nothing

--*/
{
    PVOID tempAddress = Block;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpFreePool (Block %p): Entering function.\n",
                Block));

    if (Block) {

        ExFreePool(Block);
        Block = NULL;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpFreePool (Block %p): Exiting function.\n",
                tempAddress));

    return;
}


NTSTATUS
DsmpGetStatsGatheringChoice(
    _In_ IN PDSM_CONTEXT Context,
    _Out_ OUT PULONG StatsGatherChoice
    )
/*++

Routine Description:

    This routine is used to determine if the Admin wants statitics to be collected
    on every IO. It queries the the services key for the value under
    "msdsm\Parameters\DsmDisableStatistics"

Arguments:

    Context - The DSM Context value.
    StatsGatherChoice - Returns the choice of whether or not to gather statistics

Return Value:

    Status of the RtlQueryRegistryValues call.

--*/
{
    RTL_QUERY_REGISTRY_TABLE queryTable[2];
    WCHAR registryKeyName[56] = {0};
    NTSTATUS status = STATUS_INVALID_PARAMETER;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetStatsGatherChoice (DsmCtxt %p): Entering function.\n",
                Context));

    if (!StatsGatherChoice) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_INIT,
                    "DsmpGetStatsGatherChoice (DsmCtxt %p): Invalid parameter - StatsGatherChoice is NULL.\n",
                    Context));

        goto __Exit_DsmpGetStatsGatherChoice;
    }

    RtlZeroMemory(queryTable, sizeof(queryTable));

    //
    // Build the key value name that we want as the base of the query.
    //
    RtlStringCbPrintfW(registryKeyName,
                       sizeof(registryKeyName),
                       DSM_PARAMETER_PATH_W);

    //
    // The query table has two entries. One for the supporteddeviceList and
    // the second which is the 'NULL' terminator.
    //
    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK;
    queryTable[0].Name = DSM_DISABLE_STATISTICS;
    queryTable[0].EntryContext = StatsGatherChoice;
    queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

    status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                    registryKeyName,
                                    queryTable,
                                    registryKeyName,
                                    NULL);

__Exit_DsmpGetStatsGatherChoice:

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n",
                Context,
                status));

    return status;
}


NTSTATUS
DsmpSetStatsGatheringChoice(
    _In_ IN PDSM_CONTEXT Context,
    _In_ IN ULONG StatsGatherChoice
    )
/*++

Routine Description:

    This routine is used to set the value that indicates whether statistics will
    be gathered on every IO. It updates the services key for the value under
    "msdsm\Parameters\DsmDisableStatistics"

Arguments:

    Context - The DSM Context value.
    StatsGatherChoice - Value indicating whether to gather statistics (TRUE) or not (FALSE)

Return Value:

    Status of the RtlWriteRegistryValue call.

--*/
{
    WCHAR registryKeyName[56] = {0};
    NTSTATUS status = STATUS_SUCCESS;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetStatsGatherChoice (DsmCtxt %p): Entering function.\n",
                Context));

    //
    // Build the key value name that we want as the base of the query.
    //
    RtlStringCbPrintfW(registryKeyName,
                       sizeof(registryKeyName),
                       DSM_PARAMETER_PATH_W);


    status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
                                   registryKeyName,
                                   DSM_DISABLE_STATISTICS,
                                   REG_DWORD,
                                   &StatsGatherChoice,
                                   sizeof(ULONG));

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n",
                Context,
                status));

    return status;
}



NTSTATUS
DsmpGetDeviceList(
    _In_ IN PDSM_CONTEXT Context
    )
/*++

Routine Description:

    This routine is used to build the supported device list by querying the services
    key for the values under "msdsm\Parameters\DsmSupportedDeviceList"

Arguments:

    Context - The DSM Context value. It contains storage for the multi_sz string that may
              be built.

Return Value:

    Status of the RtlQueryRegistryValues call.

--*/
{
    RTL_QUERY_REGISTRY_TABLE queryTable[2];
    WCHAR registryKeyName[56] = {0};
    UNICODE_STRING inquiryStrings;
    WCHAR defaultIDs[] = { L"\0" };
    NTSTATUS status;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDeviceList (DsmCtxt %p): Entering function.\n",
                Context));

    RtlZeroMemory(queryTable, sizeof(queryTable));
    RtlInitUnicodeString(&inquiryStrings, NULL);

    //
    // Build the key value name that we want as the base of the query.
    //
    RtlStringCbPrintfW(registryKeyName,
                       sizeof(registryKeyName),
                       DSM_PARAMETER_PATH_W);

    //
    // The query table has two entries. One for the supporteddeviceList and
    // the second which is the 'NULL' terminator.
    //
    // Indicate that there is NO call-back routine, and to give back the MULTI_SZ as
    // one blob, as opposed to individual unicode strings.
    //
    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_TYPECHECK;

    //
    // The value to query.
    //
    queryTable[0].Name = DSM_SUPPORTED_DEVICELIST_VALUE_NAME;

    //
    // Where to put the strings. Note that we need to use an empty unicode_string
    // for the query or else RtlQueryRegistryValues will only fill in enough
    // entries as specified by the size of the unicode string's buffer, which
    // is why we can't use Context->SupportedDevices directly in the call.
    //
    queryTable[0].EntryContext = &inquiryStrings;
    queryTable[0].DefaultType  = (REG_MULTI_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_MULTI_SZ;
    queryTable[0].DefaultData = defaultIDs;
    queryTable[0].DefaultLength = sizeof(defaultIDs);

    status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                    registryKeyName,
                                    queryTable,
                                    registryKeyName,
                                    NULL);

    //
    // If we successfully queried for the supported device list, we need to delete
    // our cached list and update it with this new one.
    //
    if (NT_SUCCESS(status)) {

        KIRQL oldIrql;
        PWCHAR tempBuffer = NULL;

        tempBuffer = DsmpAllocatePool(NonPagedPoolNx, inquiryStrings.MaximumLength, DSM_TAG_REG_VALUE_RELATED);

        //
        // This is a "best effort" operation. If we are unable to allocate a
        // buffer for the strings, we just continue using our old cached list.
        // We do NOT fall back to using inquiryStrings's buffer as we want to
        // be able to work with the supported devices list at raised IRQL.
        //
        if (tempBuffer) {

            RtlCopyMemory(tempBuffer, inquiryStrings.Buffer, inquiryStrings.Length);

            KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql);
            DsmpFreePool(Context->SupportedDevices.Buffer);
            Context->SupportedDevices.Buffer = tempBuffer;
            Context->SupportedDevices.Length = inquiryStrings.Length;
            Context->SupportedDevices.MaximumLength = inquiryStrings.MaximumLength;
            KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql);

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpGetDeviceList (DsmCtxt %p): Failed to allocate supported device list's buffer.\n",
                        Context));

            status =  STATUS_INSUFFICIENT_RESOURCES;
        }

        ExFreePool(inquiryStrings.Buffer);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDeviceList (DsmCtxt %p): Exiting function with status %x.\n",
                Context,
                status));

    return status;
}


_Success_(return==0)
NTSTATUS
DsmpGetStandardInquiryData(
    _In_ IN PDEVICE_OBJECT DeviceObject,
    _Out_ OUT PINQUIRYDATA InquiryData
    )
/*++

Routine Description:

    Helper routine to send an inquiry with EVPD cleared to get the standard inquiry data.

Arguments:

    DeviceObject - The port PDO to which the command should be sent.
    InquiryData - Pointer to inquiry data that will be returned to caller.

Return Value:

    STATUS_SUCCESS or failure NTSTATUS code.

--*/
{
    PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL;
    PCDB cdb;
    IO_STATUS_BLOCK ioStatus;
    ULONG length;
    NTSTATUS status = STATUS_SUCCESS;
    PINQUIRYDATA inquiryData;
    PSENSE_DATA senseData;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetStandardInquiryData (DevObj %p): Entering function.\n",
                DeviceObject));

    if (InquiryData == NULL) {
        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpGetStandardInquiryData;
    }

    //
    // Build a standard inquiry command.
    //
    length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

    passThrough = DsmpAllocatePool(NonPagedPoolNx,
                                   length,
                                   DSM_TAG_PASS_THRU);
    if (!passThrough) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetStandardInquiryData (DevObj %p): Failed to allocate mem for passthrough.\n",
                    DeviceObject));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpGetStandardInquiryData;
    }

__Retry_Request:

    //
    // Build the cdb for SCSI-3 standard inquiry.
    //
    cdb = (PCDB)passThrough->ScsiPassThrough.Cdb;
    cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
    cdb->CDB6INQUIRY3.EnableVitalProductData = 0;
    cdb->CDB6INQUIRY3.AllocationLength = sizeof(INQUIRYDATA);

    passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH);
    passThrough->ScsiPassThrough.CdbLength = 6;
    passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH;
    passThrough->ScsiPassThrough.DataIn = 1;
    passThrough->ScsiPassThrough.DataTransferLength = sizeof(INQUIRYDATA);
    passThrough->ScsiPassThrough.TimeOutValue = 20;
    passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer);
    passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer);

    DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH,
                                      DeviceObject,
                                      passThrough,
                                      passThrough,
                                      length,
                                      length,
                                      FALSE,
                                      &ioStatus);

    status = ioStatus.Status;
    senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer);

    if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) {

        //
        // Get the returned data.
        //
        inquiryData = (PINQUIRYDATA)(passThrough->DataBuffer);

        RtlCopyMemory(InquiryData, inquiryData, sizeof(INQUIRYDATA));

    } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) &&
               (NT_SUCCESS(ioStatus.Status)) &&
               (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) {

        length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

        //
        // Retry the request
        //
        RtlZeroMemory(passThrough, length);
        goto __Retry_Request;

    } else {

        // Failed to get inquiry data
        // Here it is possible that status is success, but scsi status is not.
        // If so, set status to unsuccessful.
        if (NT_SUCCESS(status)){
            status = STATUS_UNSUCCESSFUL;
        }

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetStandardInquiryData (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n",
                    DeviceObject,
                    status,
                    passThrough->ScsiPassThrough.ScsiStatus));
    }

__Exit_DsmpGetStandardInquiryData:

    //
    // Free the passthrough + data buffer.
    //
    if (passThrough) {
        DsmpFreePool(passThrough);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetStandardInquiryData (DevObj %p): Exiting function with status %x.\n",
                DeviceObject,
                status));

    return status;
}


BOOLEAN
DsmpCheckScsiCompliance(
    _In_ IN PDEVICE_OBJECT TargetObject,
    _In_ IN PINQUIRYDATA InquiryData,
    _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
    _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList
    )
/*++

Routine Description:

    Helper routine to determine if the device is SPC-3 compliant.

Arguments:

    DeviceObject - The port PDO that we're determining compliance for.
    InquiryData - Pointer to its inquiry data.
    Descriptor - Pointer to its VPD page 0x80 data
    DeviceIdList - Pointer to its VPD page 0x83 data

Return Value:

    TRUE if compliant, else FALSE.

--*/
{
    BOOLEAN supported = FALSE /* TRUE */;
    UCHAR deviceType;
    UCHAR qualifier;

    UNREFERENCED_PARAMETER(DeviceIdList);
    UNREFERENCED_PARAMETER(Descriptor);

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpCheckScsiCompliance (DevObj %p): Entering function.\n",
                TargetObject));

    deviceType = InquiryData->DeviceType & 0x1F;
    qualifier = (InquiryData->DeviceTypeQualifier >> 0x5) & 0x7;

    if ((deviceType | qualifier) == 0x7F) {

        supported = FALSE;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpCheckScsiCompliance (DevObj %p): Exiting function with Supported = %u.\n",
                TargetObject,
                supported));

    return supported;
}


BOOLEAN
DsmpDeviceSupported(
    _In_ IN PDSM_CONTEXT Context,
    _In_ IN PCSTR VendorId,
    _In_ IN PCSTR ProductId
    )
/*++

Routine Description:

    This routine determines whether the device is supported by traversing the SupportedDevice
    list and comparing to the VendorId/ProductId values passed in.

Arguments:

    Context   - Context value given to the multipath driver during registration.
    VendorId - Pointer to the inquiry data VendorId.
    ProductId - Pointer to the inquiry data ProductId.

Return Value:

    TRUE - If VendorId/ProductId is found.

--*/
{
    UNICODE_STRING deviceName;
    UNICODE_STRING productName;
    ANSI_STRING ansiVendor;
    ANSI_STRING ansiProduct;
    NTSTATUS status;
    BOOLEAN supported = FALSE;
    KIRQL oldIrql;
    UNICODE_STRING tempStrings;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpDeviceSupported (DsmCtxt %p): Entering function.\n",
                Context));

    KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql);

    RtlInitUnicodeString(&tempStrings, NULL);
    tempStrings.Buffer = DsmpAllocatePool(NonPagedPoolNx, Context->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED);

    if (tempStrings.Buffer) {

        RtlCopyMemory(tempStrings.Buffer, Context->SupportedDevices.Buffer, Context->SupportedDevices.Length);
        tempStrings.Length = Context->SupportedDevices.Length;
        tempStrings.MaximumLength = Context->SupportedDevices.MaximumLength;

    } else {

        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_WMI,
                    "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate temporary list (error %x).\n",
                    Context,
                    status));

        KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql);

        goto __Exit_DsmpDeviceSupported;
    }

    KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql);

    //
    // The SupportedDevice list was built in DriverEntry from the services key.
    //
    if (tempStrings.MaximumLength == 0) {

        //
        // List is empty.
        //
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpDeviceSupported (DsmCtxt %p): No supported Device in the list.\n",
                    Context));

        goto __Exit_DsmpDeviceSupported;
    }

    RtlInitUnicodeString(&productName, NULL);

    //
    // Convert the inquiry fields into ansi strings.
    //
    RtlInitAnsiString(&ansiVendor, VendorId);
    RtlInitAnsiString(&ansiProduct, ProductId);

    //
    // Allocate the deviceName buffer. Needs to be 8+16 plus NULL.
    // (productId length + vendorId length + NULL).
    //
    deviceName.MaximumLength = 25 * sizeof(WCHAR);
    deviceName.Buffer = DsmpAllocatePool(PagedPool, deviceName.MaximumLength, DSM_TAG_SUPPORTED_DEV);

    if (deviceName.Buffer) {

        //
        // Convert the vendorId to unicode.
        //
        status = RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE);
        if (NT_SUCCESS(status)) {

            //
            // Convert the productId to unicode.
            //
            status = RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE);

            if (NT_SUCCESS(status)) {

                //
                // 'cat' them.
                //
                status = RtlAppendUnicodeStringToString(&deviceName, &productName);

                if (NT_SUCCESS(status)) {

                    //
                    // Run the list of supported devices that was captured from the registry
                    // and see if this one is in the list.
                    //
                    supported = DsmpFindSupportedDevice(&deviceName,
                                                        &tempStrings);
                }
            } else {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpDeviceSupported (DsmCtxt %p): Failed to append product name. Status %x.\n",
                            Context,
                            status));
            }
        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpDeviceSupported (DsmCtxt %p): Failed to convert ansi vendor string to unicode. Status %x\n",
                        Context,
                        status));
        }

        DsmpFreePool(deviceName.Buffer);

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate device name buffer.\n",
                    Context));
    }

__Exit_DsmpDeviceSupported:

    if (tempStrings.Buffer) {
        DsmpFreePool(tempStrings.Buffer);
    }
    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpDeviceSupported (DsmCtxt %p): Exiting function with supported = %u.\n",
                Context,
                supported));

    return supported;
}


BOOLEAN
DsmpFindSupportedDevice(
    _In_ IN PUNICODE_STRING DeviceName,
    _In_ IN PUNICODE_STRING SupportedDevices
    )
/*++

Routine Description:

    This routine compares the two unicode strings for a match.

Arguments:

    DeviceName - String built from the current device's inquiry data.
    SupportedDevices - MULTI_SZ of devices that are supported.

Return Value:

    TRUE - If VendorId/ProductId is found.

--*/
{
    PWSTR devices = SupportedDevices->Buffer;
    ULONG bufferLengthLeft = SupportedDevices->MaximumLength / sizeof(WCHAR);
    UNICODE_STRING unicodeString;
    USHORT originalLength = DeviceName->Length;
    LONG compare;
    BOOLEAN supported = FALSE;
    WCHAR tempString[32];

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpFindSupportedDevice (DevName %ws): Entering function.\n",
                DeviceName->Buffer));

    //
    // 'devices' is the current buffer in the MULTI_SZ built from
    // the registry.
    //
    while (devices[0]) {

        RtlZeroMemory(tempString, sizeof(tempString));

        if (!NT_SUCCESS(RtlStringCchCopyNW(tempString, sizeof(tempString) / sizeof(tempString[0]), devices, bufferLengthLeft))) {

            tempString[(sizeof(tempString) / sizeof(tempString)) - 1] = L'\0';
        }

        //
        // Make the current entry into a unicode string.
        //
        RtlInitUnicodeString(&unicodeString, tempString);

        //
        // Compare this one with the current device.
        // However, for storages that make up the product id on-the-fly, MPIO
        // allows for matching based just on substring (product-id-prefix so to
        // speak).
        //
        if (unicodeString.Length < DeviceName->Length) {
            DeviceName->Length = unicodeString.Length;
        }

        compare = RtlCompareUnicodeStrings(unicodeString.Buffer,
                                           unicodeString.Length / sizeof(WCHAR),
                                           DeviceName->Buffer,
                                           DeviceName->Length / sizeof(WCHAR),
                                           TRUE);

        DeviceName->Length = originalLength;

        if (compare == 0) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpFindSupportedDevice (DevName %ws): Device support found in the registry.\n",
                        DeviceName->Buffer));

            supported = TRUE;
            break;
        }

        //
        // Advance to next entry in the MULTI_SZ.
        //
        devices += (unicodeString.MaximumLength / sizeof(WCHAR));

        bufferLengthLeft -= (unicodeString.MaximumLength / sizeof(WCHAR));
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpFindSupportedDevice (DevName %ws): Exiting function with Supported = %u.\n",
                DeviceName->Buffer,
                supported));

    return supported;
}

_Success_(return!=0)
PVOID
DsmpParseDeviceID(
    _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID,
    _In_ IN DSM_DEVID_TYPE DeviceIdType,
    _In_opt_ IN PULONG IdNumber,
    _Out_opt_ OUT PSTORAGE_IDENTIFIER_CODE_SET CodeSet,
    _In_ IN BOOLEAN Legacy
    )
/*++

Routine Description:

    This routine builds a serial number string based on the information
    in the VPD page 0x83 data if serial number is requested, else it
    returns the appropriate identifier requested.

    Caller must free the buffer.

Arguments:

    DeviceIdList - VPD Page 0x83 information.
    DeviceIdType - Type of identifier that the DeviceID is being parsed for
    IdNumber - If there are multiple identifiers of type DeviceIdType, this parameter
                   determines which among them to actually return.
                   IMPORTANT: This number is one-based (not zero-based).
    CodeSet - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. This
                   returns the code set that was used when building the serial number.
    Legacy - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. If the
                   code set of the identifier is StorageIdCodeSetBinary, this determines
                   whether to use the legacy method of binary to ascii conversion.

Return Value:

    Requested Device identifier.

--*/
{
    PSTORAGE_IDENTIFIER identifier;
    STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; // Preload with a bogus value.
    STORAGE_IDENTIFIER_TYPE type = 0xF;
    STORAGE_ASSOCIATION_TYPE association = 0xF;
    ULONG numberIds;
    ULONG i;
    ULONG identifierSize = 0;
    PUCHAR bytes = NULL;
    PVOID buffer = NULL;
    BOOLEAN done = FALSE;
    ULONG idNumber = MAXULONG;
    ULONG matches = 0;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpParseDeviceID (DevIdDesc %p): Entering function - IdType %x.\n",
                DeviceID,
                DeviceIdType));

    if (IdNumber) {
        idNumber = *IdNumber;
    }

    //
    // Get the number of encapsulated identifiers.
    //
    numberIds = DeviceID->NumberOfIdentifiers;

    if (idNumber != MAXULONG && idNumber > numberIds) {
        goto __Exit_DsmpParseDeviceID;
    }

    //
    // Get a pointer to the first one.
    //
    identifier = (PSTORAGE_IDENTIFIER)(DeviceID->Identifiers);

    for (i = 0; i < numberIds && !done; i++) {

        switch (DeviceIdType) {

            case DSM_DEVID_SERIAL_NUMBER: {

                //
                // The way this works is that we will go through all the identifiers
                // Order of preference will be LUN-associated over Target-associated.
                // Further, upon same association, preference will be based on type as
                // follows: 0x8, 0x3, 0x2, 0x1, 0x0.
                // So an existing identifier will be discarded if a better one is found.
                // If two identifiers have the same type, we will prefer the one will
                // the larger length.
                //

                //
                // 1. Ensure that the association is for either the LUN or target. (If neither, ignore id).
                // 2. If association is with target, don't it consider if current candidate has assocation with LUN.
                // 3. If considering this identifier, order of preference is 8 > 3 > 2 > 1 > 0.
                // 4. If this id type is same as current candidate, consider it only if it is of greater length.
                //
                if (((identifier->Association == StorageIdAssocDevice) ||
                     (identifier->Association == 0x2 && association != StorageIdAssocDevice)) &&
                    ((type == identifier->Type && identifierSize < identifier->IdentifierSize) ||
                     (type != identifier->Type && DsmpIsPreferredDeviceId(type, identifier->Type)))) {

                    //
                    // Get a pointer to the id itself.
                    //
                    bytes = identifier->Identifier;

                    //
                    // The id's size.
                    //
                    identifierSize = identifier->IdentifierSize;

                    //
                    // Get the type, code set, and association.
                    //
                    type = identifier->Type;
                    codeSet = identifier->CodeSet;
                    association = identifier->Association;

                    matches++;
                }

                break;
            }

            case DSM_DEVID_RELATIVE_TARGET_PORT: {

                //
                // Ensure that the association is for the target port.
                //
                if (identifier->Association != StorageIdAssocPort) {

                    if ((i + 1) < numberIds) {
                        identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset);
                    }

                    continue;
                }

                if (identifier->Type == StorageIdTypePortRelative) {

                    //
                    // Get a pointer to the id itself.
                    //
                    bytes = identifier->Identifier;

                    //
                    // The id's size.
                    //
                    identifierSize = identifier->IdentifierSize;

                    type = identifier->Type;
                    codeSet = identifier->CodeSet;
                    association = identifier->Association;

                    matches++;
                }

                break;
            }

            case DSM_DEVID_TARGET_PORT_GROUP: {

                //
                // Ensure that the association is for the target port.
                //
                if (identifier->Association != StorageIdAssocPort) {

                    if ((i + 1) < numberIds) {
                        identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset);
                    }

                    continue;
                }

                if (identifier->Type == 0x5) {

                    //
                    // Get a pointer to the id itself.
                    //
                    bytes = identifier->Identifier;

                    //
                    // Move this by two bytes because first two bytes are reservered
                    //
                    bytes += sizeof(USHORT);

                    //
                    // The id's size. Reduce the size by 2 bytes (to account
                    // for the reservered bytes)
                    //
                    identifierSize = identifier->IdentifierSize - sizeof(USHORT);

                    type = identifier->Type;
                    codeSet = identifier->CodeSet;
                    association = identifier->Association;

                    matches++;
                }

                break;
            }

            default: break;
        }


        if (idNumber != MAXULONG && idNumber == matches) {
            done = TRUE;
        }

        //
        // Advance to the next identifier in the buffer.
        //
        if ((i + 1) < numberIds) {
            identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset);
        }
    }

    if (idNumber != MAXULONG && idNumber > matches) {
        goto __Exit_DsmpParseDeviceID;
    }

    if (DeviceIdType == DSM_DEVID_SERIAL_NUMBER) {

        if (type != StorageIdTypeScsiNameString &&
            type != StorageIdTypeFCPHName &&
            type != StorageIdTypeEUI64 &&
            type != StorageIdTypeVendorId &&
            type != StorageIdTypeVendorSpecific) {

            DSM_ASSERT(FALSE);
            bytes = NULL;
            identifierSize = 0;
            type = association = 0xF;
            codeSet = StorageIdCodeSetReserved;
        }

        TracePrint((TRACE_LEVEL_INFORMATION,
                    TRACE_FLAG_PNP,
                    "DsmpParseDeviceID (DevIdDesc %p): IdentifierSize = %u, Type = %u, Association = %u, CodeSet = %u.\n",
                    DeviceID,
                    identifierSize,
                    type,
                    association,
                    codeSet));

        if (!bytes) {
            goto __Exit_DsmpParseDeviceID;
        }

        if (codeSet == StorageIdCodeSetBinary) {

            //
            // Need to convert to ascii.
            //
            buffer = DsmpBinaryToAscii(bytes,
                                       identifierSize,
                                       &identifierSize,
                                       Legacy);

        } else {

            if (identifierSize) {
                //
                // Allocate a buffer that is the size of the data, plus one for NULL.
                //
                buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize + 1, DSM_TAG_DEV_ID);
                DSM_ASSERT(buffer);

                if (buffer) {

                    //
                    // Copy over the id.
                    //
                    RtlCopyMemory(buffer, bytes, identifierSize);
                }
            }
        }

        if (CodeSet) {
            *CodeSet = codeSet;
        }

    } else {

        if (identifierSize) {

            DSM_ASSERT((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) ||
                       (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT)));

            _Analysis_assume_((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) ||
                              (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT)));

            buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize, DSM_TAG_DEV_ID);

            if (buffer) {

                if (DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT) {

                    GetUlongFrom4ByteArray(bytes, *((PULONG)buffer));

                } else if (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP) {

                    *((PUSHORT)buffer) = (bytes[0] << 8) | (bytes[1]);
                }
            }
        }
    }

__Exit_DsmpParseDeviceID:

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpParseDeviceID (DevIdDesc %p): Exiting function with buffer %p.\n",
                DeviceID,
                buffer));

    return buffer;
}


PUCHAR
DsmpBinaryToAscii(
    _In_reads_(Length) IN PUCHAR HexBuffer,
    _In_ IN ULONG Length,
    _Inout_ IN OUT PULONG UpdateLength,
    _In_ IN BOOLEAN Legacy
    )
/*++

Routine Description:

    This routine will convert HexBuffer into an ascii NULL-terminated string.

    Note: This routine will allocate memory for storing the ascii string. It is
          the responsibility of the caller to free this buffer.

Arguments:

    HexBuffer - Pointer to the binary data.
    Length - Length, in bytes, of HexBuffer.
    UpdateLength - Storage to place the actual length of the returned string.
    Legacy - Use the legacy method for the conversion.

Return Value:

    Serial Number string, or NULL if an error occurred.

--*/
{
    static UCHAR IntegerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    ULONG i;
    ULONG j;
    ULONG actualLength;
    PUCHAR buffer = NULL;
    UCHAR highWord;
    UCHAR lowWord;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBinaryToAscii (HexBuff %p): Entering function.\n",
                HexBuffer));

    if (Length == 0) {
        *UpdateLength = 0;
        goto __Exit_DsmpBinaryToAscii;
    }

    if (Legacy) {
        //
        // Do a pre-test on the buffer to determine the length actually needed.
        //
        for (i = 0, actualLength = 0; i < Length; i++) {

            if (HexBuffer[i] < 0x10) {
                actualLength++;
            } else {
                actualLength += 2;
            }
        }

        //
        // Add room for a terminating NULL.
        //
        actualLength++;
    } else {
        //
        // We need one character for each nibble, plus one for the terminating NULL.
        //
        actualLength = (Length * 2) + 1;
    }

    //
    // Allocate the buffer.
    //
    buffer = DsmpAllocatePool(NonPagedPoolNx,
                              actualLength,
                              DSM_TAG_BIN_TO_ASCII);
    if (!buffer) {
        *UpdateLength = 0;
        goto __Exit_DsmpBinaryToAscii;
    }

    for (i = 0, j = 0; i < Length && j < actualLength; i++) {

        if (Legacy && (HexBuffer[i] < 0x10)) {

            //
            // If legacy is mentioned and it's 0x0F or less,
            // just convert the entire byte.
            //
            buffer[j++] = IntegerTable[HexBuffer[i]];
        } else {

            //
            // Split out each nibble from the binary byte.
            //
            highWord = HexBuffer[i] >> 4;
            lowWord = HexBuffer[i] & 0x0F;

            //
            // Using the lookup table, convert and stuff into
            // the ascii buffer.
            //
            buffer[j++] = IntegerTable[highWord];
            buffer[j++] = IntegerTable[lowWord];
        }
    }

    //
    // Update the caller's length field.
    //
    *UpdateLength = actualLength;

__Exit_DsmpBinaryToAscii:

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n",
                HexBuffer,
                (const char*) buffer));

    return buffer;
}


PSTR
DsmpGetSerialNumber(
    _In_ IN PDEVICE_OBJECT DeviceObject
    )
/*++

Routine Description:

    Helper routine to send an inquiry with EVPD set to get the serial number page.
    Used if the serial number is not embedded in the device descriptor (this device probably
    doesn't support VPD page 0x00).

    Note: This routine will allocate memory for storing the serial number. It is
          the responsibility of the caller to free this buffer.

Arguments:

    DeviceObject - The port PDO to which the command should be sent.

Return Value:

    The serial number (null-terminated string) or NULL if the call fails.

--*/
{
    PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL;
    PVPD_SERIAL_NUMBER_PAGE serialPage;
    PCDB cdb;
    PSTR serialNumber = NULL;
    IO_STATUS_BLOCK ioStatus;
    ULONG length;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetSerialNumber (DevObj %p): Entering function.\n",
                DeviceObject));

    //
    // Build an inquiry command with EVPD and pagecode of 0x80 (serial number).
    //
    length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

    passThrough = DsmpAllocatePool(NonPagedPoolNx,
                                   length,
                                   DSM_TAG_PASS_THRU);
    if (!passThrough) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for passthrough.\n",
                    DeviceObject));

        goto __Exit_DsmpGetSerialNumber;
    }

    //
    // Build the cdb.
    //
    cdb = (PCDB)passThrough->ScsiPassThrough.Cdb;
    cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
    cdb->CDB6INQUIRY.Reserved1 = 1;
    cdb->CDB6INQUIRY.PageCode = VPD_SERIAL_NUMBER;
    cdb->CDB6INQUIRY.AllocationLength = DSM_SERIAL_NUMBER_BUFFER_SIZE;

    passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH);
    passThrough->ScsiPassThrough.CdbLength = 6;
    passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH;
    passThrough->ScsiPassThrough.DataIn = 1;
    passThrough->ScsiPassThrough.DataTransferLength = DSM_SERIAL_NUMBER_BUFFER_SIZE;
    passThrough->ScsiPassThrough.TimeOutValue = 20;
    passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer);
    passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer);

    DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH,
                                      DeviceObject,
                                      passThrough,
                                      passThrough,
                                      length,
                                      length,
                                      FALSE,
                                      &ioStatus);
    if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) &&
        (NT_SUCCESS(ioStatus.Status))) {

        ULONG inx;

        //
        // Get the returned data.
        //
        serialPage = (PVPD_SERIAL_NUMBER_PAGE)(passThrough->DataBuffer);

        //
        // Allocate a buffer to hold just the serial number plus a null terminator
        //
        serialNumber = DsmpAllocatePool(NonPagedPoolNx,
                                        serialPage->PageLength + 1,
                                        DSM_TAG_SERIAL_NUM);
        if (serialNumber) {

            //
            // Copy it over.
            //
            RtlCopyMemory(serialNumber, serialPage->SerialNumber, serialPage->PageLength);

            //
            // Some devices return binary data for the serial number.
            // Convert to a more ascii-ish format so that other routines don't have a problem.
            //
            for (inx = 0; inx < serialPage->PageLength; inx++) {
                if (serialNumber[inx] == '\0') {
                    serialNumber[inx] = ' ';
                }
            }
        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for serialnumber.\n",
                        DeviceObject));
        }
    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetSerialNumber (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n",
                    DeviceObject,
                    ioStatus.Status,
                    passThrough->ScsiPassThrough.ScsiStatus));
    }

__Exit_DsmpGetSerialNumber:

    //
    // Free the passthrough + data buffer.
    //
    if (passThrough) {

        DsmpFreePool(passThrough);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetSerialNumber (DevObj %p): Exiting function with serial number %s.\n",
                DeviceObject,
                (const char*)serialNumber));

    //
    // Return the sn.
    //
    return serialNumber;
}


NTSTATUS
DsmpDisableImplicitStateTransition(
    _In_ IN PDEVICE_OBJECT TargetDevice,
    _Out_ OUT PBOOLEAN DisableImplicit
    )
/*++

Routine Description:

    Send down request to disable implicit ALUA state transition.
    The function first sends down a mode sense to get the control extension mode
    sense data. It then clears the IALUAE bit and sends down a mode select.

Arguements:

    TargetDevice - Device object that will be target of this command.
    DisableImplicit - Flag returned to the caller to indicate whether or not
                      implicit transitions are disabled.

Return Value :

    STATUS_SUCCESS if the command succeeds.
    Appropriate NTSTATUS code on failure

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL;
    PCDB cdb;
    IO_STATUS_BLOCK ioStatus;
    ULONG length;
    PSPC3_CONTROL_EXTENSION_MODE_PAGE controlExtensionPage = NULL;
    PSENSE_DATA senseData = NULL;
    BOOLEAN implicitDisabled = FALSE;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpDisableImplicitStateTransition (DevObj %p): Entering function.\n",
                TargetDevice));

    //
    // First build the mode sense command to get the control extension parameters.
    //
    length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

    passThrough = DsmpAllocatePool(NonPagedPoolNx,
                                   length,
                                   DSM_TAG_PASS_THRU);
    if (!passThrough) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpDisableImplicitStateTransition (DevObj %p): Failed to allocate mem for passthrough.\n",
                    TargetDevice));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpDisableImplicitStateTransition;
    }

__Retry_ModeSense:

    passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH);
    passThrough->ScsiPassThrough.CdbLength = 6;
    passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH;
    passThrough->ScsiPassThrough.DataIn = 1;
    passThrough->ScsiPassThrough.DataTransferLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE);
    passThrough->ScsiPassThrough.TimeOutValue = 20;
    passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer);
    passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer);

    //
    // Build the cdb for mode sense.
    //
    cdb = (PCDB)passThrough->ScsiPassThrough.Cdb;
    cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
    cdb->MODE_SENSE.Dbd = 1;
    cdb->MODE_SENSE.PageCode = 0xA;
    cdb->MODE_SENSE.SubPageCode = 0x01;
    cdb->MODE_SENSE.AllocationLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE);

    DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH,
                                      TargetDevice,
                                      passThrough,
                                      passThrough,
                                      length,
                                      length,
                                      FALSE,
                                      &ioStatus);

    status = ioStatus.Status;
    senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer);

    if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) {

        controlExtensionPage = (PSPC3_CONTROL_EXTENSION_MODE_PAGE)(passThrough->DataBuffer);

        if (controlExtensionPage->ImplicitALUAEnable) {

            controlExtensionPage->ImplicitALUAEnable = 0;

__Retry_ModeSelect:

            RtlZeroMemory(passThrough->SenseInfoBuffer, passThrough->ScsiPassThrough.SenseInfoLength);

            passThrough->ScsiPassThrough.DataIn = 0;

            //
            // Build the cdb for mode select.
            //
            RtlZeroMemory(cdb, 6);
            cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
            cdb->MODE_SELECT.SPBit = 0;
            cdb->MODE_SELECT.PFBit = 1;
            cdb->MODE_SELECT.ParameterListLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE);

            length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

            DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH,
                                              TargetDevice,
                                              passThrough,
                                              passThrough,
                                              length,
                                              length,
                                              FALSE,
                                              &ioStatus);

            status = ioStatus.Status;
            senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer);

            if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) {

                implicitDisabled = TRUE;

                TracePrint((TRACE_LEVEL_INFORMATION,
                            TRACE_FLAG_PNP,
                            "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions turned off successfully.\n",
                            TargetDevice));

            } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) &&
                       (NT_SUCCESS(status)) &&
                       (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) {

                //
                // Retry the request
                //
                goto __Retry_ModeSelect;

            } else {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpDisableImplicitStateTransition (DevObj %p): ModeSelect failed - NTStatus 0x%x, ScsiStatus 0x%x.\n",
                            TargetDevice,
                            status,
                            passThrough->ScsiPassThrough.ScsiStatus));
            }
        } else {

            implicitDisabled = TRUE;

            TracePrint((TRACE_LEVEL_INFORMATION,
                        TRACE_FLAG_PNP,
                        "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions already turned OFF.\n",
                        TargetDevice));
        }
    } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) &&
               (NT_SUCCESS(status)) &&
               (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) {

        length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS);

        //
        // Retry the request
        //
        RtlZeroMemory(passThrough, length);
        goto __Retry_ModeSense;

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpDisableImplicitStateTransition (DevObj %p): ModeSense failed - NTStatus 0x%x, ScsiStatus 0x%x.\n",
                    TargetDevice,
                    status,
                    passThrough->ScsiPassThrough.ScsiStatus));
    }

__Exit_DsmpDisableImplicitStateTransition:

    //
    // Free the passthrough + data buffer.
    //
    if (passThrough) {
        DsmpFreePool(passThrough);
    }

    //
    // Return whether IALUAE is set to 0.
    //
    if (DisableImplicit) {

        *DisableImplicit = implicitDisabled;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpDisableImplicitStateTransition (DevObj %p): Exiting function with status %x.\n",
                TargetDevice,
                status));

    return status;
}


PWSTR
DsmpBuildHardwareId(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo
    )
/*++

Routine Description:

    Construct a string concatinating VendorId with ProductId.

Arguements:

    DeviceInfo   - Device Extension

Return Value :

    NULL terminated hardware id if it was built successfully.
    NULL in case of failure.

--*/
{
    PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
    PWSTR hardwareId = NULL;
    SIZE_T vendorIDLength = 0;
    SIZE_T productIDLength = 0;
    PCSZ vendorIdOffset;
    PCSZ productIdOffset;
    SIZE_T sizeNeeded;
    NTSTATUS status = STATUS_SUCCESS;
    ANSI_STRING ansiString;
    UNICODE_STRING unicodeString;
    ULONG offset;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildHardwareId (DevInfo %p): Entering function.\n",
                DeviceInfo));

    deviceDescriptor = &(DeviceInfo->Descriptor);

    //
    // Save the vendorid and productid offset in Device Descriptor
    //
    offset = deviceDescriptor->ProductIdOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    offset = deviceDescriptor->VendorIdOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    if (!vendorIDLength || !productIDLength) {

        status = STATUS_UNSUCCESSFUL;

        TracePrint((TRACE_LEVEL_WARNING,
                    TRACE_FLAG_PNP,
                    "DsmpBuildHardwareId (DevInfo %p): Invalid vendor and/or product id.\n",
                    DeviceInfo));

        goto __Exit_DsmpBuildHardwareId;
    }

    sizeNeeded = vendorIDLength + productIDLength;
    hardwareId = DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_HARDWARE_ID);
    if (!hardwareId) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildHardwareId (DevInfo %p): Failed to allocate memory for device name.\n",
                    DeviceInfo));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpBuildHardwareId;
    }

    //
    // Build the NULL terminated hardwareId whose format is :
    //
    //        VendorIdProductId
    //
    vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->VendorIdOffset);
    RtlInitAnsiString(&ansiString, vendorIdOffset);
    unicodeString.Length = 0;
    unicodeString.MaximumLength = (USHORT)vendorIDLength;
    unicodeString.Buffer = hardwareId;

    status = RtlAnsiStringToUnicodeString(&unicodeString,
                                          &ansiString,
                                          FALSE);
    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildHardwareId (DevInfo %p): Failed to convert vendor id to unicode string.\n",
                    DeviceInfo));

        goto __Exit_DsmpBuildHardwareId;
    }

    productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset);
    RtlInitAnsiString(&ansiString, productIdOffset);
    unicodeString.Length = 0;
    unicodeString.MaximumLength = (USHORT)productIDLength;
    unicodeString.Buffer = hardwareId + strlen(((PCHAR)deviceDescriptor) + offset);

    status = RtlAnsiStringToUnicodeString(&unicodeString,
                                          &ansiString,
                                          FALSE);
    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildHardwareId (DevInfo %p): Failed to convert product id to unicode string.\n",
                    DeviceInfo));

        goto __Exit_DsmpBuildHardwareId;
    }

    TracePrint((TRACE_LEVEL_ERROR,
                TRACE_FLAG_PNP,
                "DsmpBuildHardwareId (DevInfo %p): HardwareId is %ws.\n",
                DeviceInfo,
                hardwareId));

__Exit_DsmpBuildHardwareId:

    if (hardwareId && !NT_SUCCESS(status)) {
        DsmpFreePool(hardwareId);
        hardwareId = NULL;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildHardwareId (DevInfo %p): Exiting function with deviceName %ws.\n",
                DeviceInfo,
                hardwareId));

    return hardwareId;
}


PWSTR
DsmpBuildDeviceNameLegacyPage0x80(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo
    )
/*++

Routine Description:

    Construct a string from VendorId, ProductId, and SerialNumber (page 0x80
    info) of the device.

Arguements:

    DeviceInfo - Device Extension

Return Value :

    STATUS_SUCCESS if the device name was built successfully.

    Appropriate NTSTATUS code on failure

--*/
{
    PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
    PWCHAR deviceName = NULL;
    PWCHAR tmpPtr;
    PWCHAR vendorID = NULL;
    PWCHAR productID = NULL;
    PWCHAR serialID = NULL;
    ANSI_STRING ansiString;
    UNICODE_STRING unicodeString;
    UNICODE_STRING unicodeDeviceName;
    SIZE_T vendorIDLength = 0;
    SIZE_T productIDLength = 0;
    SIZE_T serialIDLength = 0;
    ULONG offset;
    SIZE_T sizeNeeded;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Entering function.\n",
                DeviceInfo));

    deviceDescriptor = &(DeviceInfo->Descriptor);

    //
    // Save the vendorid, productid, and serialnumber offset
    // in Device Descriptor
    //
    offset = deviceDescriptor->VendorIdOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    offset = deviceDescriptor->ProductIdOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    offset = deviceDescriptor->SerialNumberOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        serialIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    //
    // Allocate buffers to use to convert the IDs from ANSI to Unicode and
    // eventually build the device name.
    //
    if (vendorIDLength > 0) {
        vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME);
        if (!vendorID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for vendor ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (productIDLength > 0) {
        productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME);
        if (!productID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for product ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (serialIDLength > 0) {
        serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME);
        if (!serialID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for serial ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    sizeNeeded = vendorIDLength + productIDLength + serialIDLength;
    if (sizeNeeded > 0) {

        //
        // Account for the terminating NULL if serial id is empty.
        //

        sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE);

        deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME);
        if (!deviceName) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for device name.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    } else {

        status = STATUS_UNSUCCESSFUL;
    }

    if (!NT_SUCCESS(status)) {
        goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
    }

    //
    // Build the NULL terminated device name whose format is :
    //
    //        VendorId_ProductId_SerialNumber
    //

    unicodeDeviceName.Length = 0;
    unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded;
    unicodeDeviceName.Buffer = deviceName;

    if (vendorIDLength) {

        PCSZ vendorIdOffset;

        vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor +
                         deviceDescriptor->VendorIdOffset);

        RtlInitAnsiString(&ansiString, vendorIdOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) vendorIDLength;
        unicodeString.Buffer = vendorID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);
        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert vendor id to unicode string.\n",
                    DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(vendorID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate vendor ID to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }

        RtlUnicodeStringCatString(&unicodeDeviceName, L"_");
    }

    if (productIDLength) {

        PCSZ productIdOffset;

        productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor +
                          deviceDescriptor->ProductIdOffset);

        RtlInitAnsiString(&ansiString, productIdOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) productIDLength;
        unicodeString.Buffer = productID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert product id to unicode string.\n",
                    DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(productID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, productID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate product ID to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }

        RtlUnicodeStringCatString(&unicodeDeviceName, L"_");
    }

    //
    // Serial number
    //
    if (serialIDLength) {

        PCSZ serialNumberOffset;

        serialNumberOffset = (PCSZ)((PUCHAR)deviceDescriptor +
                              deviceDescriptor->SerialNumberOffset);

        RtlInitAnsiString(&ansiString, serialNumberOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) serialIDLength;
        unicodeString.Buffer = serialID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert serial number to unicode string.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(serialID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate serial number to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceNameLegacyPage0x80;
        }
    }

    TracePrint((TRACE_LEVEL_ERROR,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Device Name is %ws.\n",
                DeviceInfo,
                deviceName));

__Exit_DsmpBuildDeviceNameLegacyPage0x80:

    if (vendorID) {
        DsmpFreePool(vendorID);
    }

    if (productID) {
        DsmpFreePool(productID);
    }

    if (serialID) {
        DsmpFreePool(serialID);
    }

    if (deviceName && !NT_SUCCESS(status)) {
        DsmpFreePool(deviceName);
        deviceName = NULL;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Exiting function with deviceName %ws.\n",
                DeviceInfo,
                deviceName));

    return deviceName;
}



PWSTR
DsmpBuildDeviceName(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_reads_(SerialNumberLength) IN PSTR SerialNumber,
    _In_ IN SIZE_T SerialNumberLength
    )
/*++

Routine Description:

    Construct a string from VendorId, ProductId, and SerialNumber (page 0x83
    identifiers) of the device.

Arguements:

    DeviceInfo   - Device Extension
    SerialNumber - Device serial number built from appropriate page 0x83 identifier
    SerialNumberLength - Length (in chars) of the passed in serial number buffer

Return Value :

    Device name if it was built successfully.
    NULL in case of failure.

--*/
{
    PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
    PWCHAR deviceName = NULL;
    PWCHAR tmpPtr;
    PWCHAR vendorID = NULL;
    PWCHAR productID = NULL;
    PWCHAR serialID = NULL;
    ANSI_STRING ansiString;
    UNICODE_STRING unicodeString;
    UNICODE_STRING unicodeDeviceName;
    SIZE_T vendorIDLength = 0;
    SIZE_T productIDLength = 0;
    SIZE_T serialIDLength = 0;
    ULONG offset;
    SIZE_T sizeNeeded;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceName (DevInfo %p): Entering function.\n",
                DeviceInfo));

    deviceDescriptor = &(DeviceInfo->Descriptor);

    //
    // Save the vendorid, productid, and serialnumber offset
    // in Device Descriptor
    //
    offset = deviceDescriptor->VendorIdOffset;
    if ((offset != 0) && (offset != MAXULONG)) {

        vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    offset = deviceDescriptor->ProductIdOffset;
    if ((offset != 0) && (offset != -1)) {

        productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE;
    }

    if (SerialNumber) {

        serialIDLength = (SerialNumberLength * sizeof(WCHAR)) + WNULL_SIZE;
    }

    //
    // Allocate buffers to use to convert the IDs from ANSI to Unicode and
    // eventually build the device name.
    //
    if (vendorIDLength > 0) {
        vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME);
        if (!vendorID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for vendor ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (productIDLength > 0) {
        productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME);
        if (!productID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for product ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (serialIDLength > 0) {
        serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME);
        if (!serialID) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for serial ID.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    sizeNeeded = vendorIDLength + productIDLength + serialIDLength;
    if (sizeNeeded > 0) {

        //
        // Account for the terminating NULL if serial id is empty.
        //

        sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE);

        deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME);
        if (!deviceName) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for device name.\n",
                        DeviceInfo));

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    } else {

        status = STATUS_UNSUCCESSFUL;
    }

    if (!NT_SUCCESS(status)) {
        goto __Exit_DsmpBuildDeviceName;
    }

    //
    // Build the NULL terminated device name whose format is :
    //
    //        VendorId_ProductId_SerialNumber
    //

    unicodeDeviceName.Length = 0;
    unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded;
    unicodeDeviceName.Buffer = deviceName;

    if (vendorIDLength) {

        PCSZ vendorIdOffset;

        vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor +
                         deviceDescriptor->VendorIdOffset);

        RtlInitAnsiString(&ansiString, vendorIdOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) vendorIDLength;
        unicodeString.Buffer = vendorID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);
        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildDeviceName (DevInfo %p): Failed to convert vendor id to unicode string.\n",
                    DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(vendorID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate vendor ID to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }

        RtlUnicodeStringCatString(&unicodeDeviceName, L"_");
    }

    if (productIDLength) {

        PCSZ productIdOffset;

        productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor +
                          deviceDescriptor->ProductIdOffset);

        RtlInitAnsiString(&ansiString, productIdOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) productIDLength;
        unicodeString.Buffer = productID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpBuildDeviceName (DevInfo %p): Failed to convert product id to unicode string.\n",
                    DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(productID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, productID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate product ID to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }

        RtlUnicodeStringCatString(&unicodeDeviceName, L"_");
    }

    //
    // Serial number
    //
    if (serialIDLength) {

        PSTR serialNumberOffset;

        serialNumberOffset = SerialNumber;

        RtlInitAnsiString(&ansiString, serialNumberOffset);

        unicodeString.Length = 0;
        unicodeString.MaximumLength = (USHORT) serialIDLength;
        unicodeString.Buffer = serialID;

        status = RtlAnsiStringToUnicodeString(&unicodeString,
                                              &ansiString,
                                              FALSE);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to convert serial number to unicode string.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }

        //
        // If there are spaces in the id, set NULL at the first space.
        //
        tmpPtr = wcschr(serialID, L' ');
        if (tmpPtr != NULL) {
            *tmpPtr = WNULL;
        }

        status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate serial number to device name.\n",
                        DeviceInfo));

            goto __Exit_DsmpBuildDeviceName;
        }
    }

    TracePrint((TRACE_LEVEL_ERROR,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceName (DevInfo %p): Device Name is %ws.\n",
                DeviceInfo,
                deviceName));

__Exit_DsmpBuildDeviceName:

    if (vendorID) {
        DsmpFreePool(vendorID);
    }

    if (productID) {
        DsmpFreePool(productID);
    }

    if (serialID) {
        DsmpFreePool(serialID);
    }

    if (deviceName && !NT_SUCCESS(status)) {
        DsmpFreePool(deviceName);
        deviceName = NULL;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpBuildDeviceName (DevInfo %p): Exiting function with deviceName %ws.\n",
                DeviceInfo,
                deviceName));

    return deviceName;
}


NTSTATUS
DsmpApplyDeviceNameCorrection(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy,
    _In_ IN SIZE_T DeviceNameLegacyLen,
    _In_reads_(DeviceNameLen) PWSTR DeviceName,
    _In_ IN SIZE_T DeviceNameLen
    )
/*++

Routine Description:

    If the registry has a key name built with a legacy device name, this
    function updates the key name with the current device name.

Arguements:

    DeviceInfo          - Device instance
    DeviceNameLegacy    - Device name built using legacy methods.
    DeviceNameLegacyLen - Number of chars (including NULL) of the DeviceNameLegacy buffer.
    DeviceName          - Device name built using current methods.
    DeviceNameLen       - Number of chars (including NULL) of the DeviceName buffer.

Return Value :

    STATUS_SUCCESS if the device's key was updated successfully.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE lbSettingsKey = NULL;
    HANDLE deviceKeyLegacy = NULL;
    HANDLE deviceKey = NULL;
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    UNICODE_STRING deviceNameLegacy;
    UNICODE_STRING deviceName;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(DeviceNameLen);
    UNREFERENCED_PARAMETER(DeviceNameLegacyLen);

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpApplyDeviceNameCorrection (DevInfo %p): Entering function.\n",
                DeviceInfo));

    //
    // First open LoadBalanceSettings key under the service key.
    //
    status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to open LB Settings key. Status %x.\n",
                    DeviceInfo,
                    status));

        goto __Exit_DsmpApplyDeviceNameCorrection;
    }

    RtlInitUnicodeString(&deviceNameLegacy, DeviceNameLegacy);

    InitializeObjectAttributes(&objectAttributes,
                               &deviceNameLegacy,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               lbSettingsKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    //
    // Open the old device key under DsmLoadBalanceSettings key.
    // The name of this key is the one built using legacy methods - either a
    // serial number from VPD page 0x80 or an aliased serial number from VPD
    // page 0x83.
    //
    status = ZwOpenKey(&deviceKeyLegacy,
                       KEY_ALL_ACCESS,
                       &objectAttributes);

    if (NT_SUCCESS(status)) {

        ULONG disposition;

        TracePrint((TRACE_LEVEL_INFORMATION,
                    TRACE_FLAG_PNP,
                    "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name exists.\n",
                    DeviceInfo));

        RtlInitUnicodeString(&deviceName, DeviceName);

        InitializeObjectAttributes(&objectAttributes,
                                   &deviceName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   lbSettingsKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        //
        // Since the old name key exists, create one with the new name.
        //
        status = ZwCreateKey(&deviceKey,
                             KEY_ALL_ACCESS,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             &disposition);

        if (NT_SUCCESS(status)) {

            //
            // The new key shouldn't exist if the old one does.
            // If it does, it indicates a error occured the previous time
            // this was tried, so just copy over the old subtree anyways now.
            //
            DSM_ASSERT(disposition == REG_CREATED_NEW_KEY);

            //
            // Copy over the entire subtree of the old key over to the new key.
            //
            status = DsmpRegCopyTree(deviceKeyLegacy, deviceKey);

            if (!NT_SUCCESS(status)) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to copy over the old device key's subtree. Status %x.\n",
                            DeviceInfo,
                            status));

                goto __Exit_DsmpApplyDeviceNameCorrection;
            }

            //
            // Delete the old key name.
            //
            status = DsmpRegDeleteTree(deviceKeyLegacy);
            if (!NT_SUCCESS(status)) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to delete the old device key's subtree. Status %x.\n",
                            DeviceInfo,
                            status));

                goto __Exit_DsmpApplyDeviceNameCorrection;
            }

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to create the new device key. Status %x.\n",
                        DeviceInfo,
                        status));

            goto __Exit_DsmpApplyDeviceNameCorrection;
        }

    } else if (status == STATUS_INVALID_HANDLE ||
               status == STATUS_OBJECT_NAME_NOT_FOUND) {

        TracePrint((TRACE_LEVEL_INFORMATION,
                    TRACE_FLAG_PNP,
                    "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name does not exist.\n",
                    DeviceInfo));

        status = STATUS_SUCCESS;

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to query key with old device name. Status %x\n",
                    DeviceInfo,
                    status));
    }

__Exit_DsmpApplyDeviceNameCorrection:

    if (deviceKey) {
        ZwClose(deviceKey);
    }

    if (deviceKeyLegacy) {
        ZwClose(deviceKeyLegacy);
    }

    if (lbSettingsKey) {
        ZwClose(lbSettingsKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpApplyDeviceNameCorrection (DevInfo %p): Exiting function with status %x\n",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpQueryDeviceLBPolicyFromRegistry(
    _In_ PDSM_DEVICE_INFO DeviceInfo,
    _In_ PWSTR RegistryKeyName,
    _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _Inout_ PULONGLONG PreferredPath,
    _Inout_ PUCHAR ExplicitlySet
    )
/*++

Routine Description:

    Query the saved load balance policy and preferred path for this device from
    the registry.
    Also returns whether this setting was explicitly set via WMI call to SetLBPolicy,
    (as opposed to the settings being made based on defaults determined through the
    storage's ALUA capabilities).

Arguements:

    DeviceInfo - The instance of the LUN through a paricular path
    RegistryKeyName - DeviceName representing this LUN
    LoadBalanceType - Type of LB policy.
    PreferredPath - The preferred path for the device.
    ExplicitlySet - Flag reflecting if LB policy was explicitly set.

Return Value :

    STATUS_SUCCESS if we were able to successfully query the registry for the info.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE lbSettingsKey = NULL;
    HANDLE deviceKey = NULL;
    UNICODE_STRING subKeyName;
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    UNICODE_STRING keyValueName;
    ULONG length;
    struct _explicitSet {
        KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
        UCHAR Data;
    } explicitSet;
    struct _preferredPath {
        KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
        ULONGLONG Data;
    } preferredPath;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Entering function.\n",
                DeviceInfo));

    //
    // Query the Load Balance settings for the given device from the registry.
    // First open LoadBalanceSettings key under the service key.
    //
    status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to open LB Settings key. Status %x.\n",
                    DeviceInfo,
                    status));

        goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry;
    }

    RtlInitUnicodeString(&subKeyName, RegistryKeyName);

    InitializeObjectAttributes(&objectAttributes,
                               &subKeyName,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               lbSettingsKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    //
    // Create or Open the device key under DsmLoadBalanceSettings key.
    // The name of this key is the one built in DsmpBuildDeviceName
    //
    status = ZwCreateKey(&deviceKey,
                         KEY_ALL_ACCESS,
                         &objectAttributes,
                         0,
                         NULL,
                         REG_OPTION_NON_VOLATILE,
                         NULL);

    if (NT_SUCCESS(status)) {

        RTL_QUERY_REGISTRY_TABLE queryTable[2];

        RtlZeroMemory(queryTable, sizeof(queryTable));

        queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                              RTL_QUERY_REGISTRY_REQUIRED | 
                              RTL_QUERY_REGISTRY_TYPECHECK;
        queryTable[0].Name = DSM_LOAD_BALANCE_POLICY;
        queryTable[0].EntryContext = LoadBalanceType;
        queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

        status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                        deviceKey,
                                        queryTable,
                                        deviceKey,
                                        NULL);
        if (NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n",
                        DeviceInfo,
                        *LoadBalanceType));

        } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {

            //
            // The device key must have been newly created.
            // Set the default load balance policy for this device
            //

            status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                           deviceKey,
                                           DSM_LOAD_BALANCE_POLICY,
                                           REG_DWORD,
                                           LoadBalanceType,
                                           sizeof(ULONG));
            if (!NT_SUCCESS(status)) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write LB policy. Status %x.\n",
                            DeviceInfo,
                            status));

                goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry;
            }
        }

        if (NT_SUCCESS(status)) {

            RtlInitUnicodeString(&keyValueName, DSM_POLICY_EXPLICITLY_SET);
            status = ZwQueryValueKey(deviceKey,
                                     &keyValueName,
                                     KeyValuePartialInformation,
                                     &explicitSet,
                                     sizeof(explicitSet),
                                     &length);

            if (NT_SUCCESS(status)) {

                NT_ASSERT(explicitSet.KeyValueInfo.DataLength == sizeof(UCHAR));

                *ExplicitlySet = *((UCHAR UNALIGNED *)&(explicitSet.KeyValueInfo.Data));

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): ExplicitlySet is %!bool!.\n",
                            DeviceInfo,
                            *ExplicitlySet));

            } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {

                *ExplicitlySet = FALSE;

                //
                // The device key must have been newly created.
                // Set ExplicitlySet to 0 to indicate that the default was used.
                //
                status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                               deviceKey,
                                               DSM_POLICY_EXPLICITLY_SET,
                                               REG_BINARY,
                                               ExplicitlySet,
                                               sizeof(UCHAR));
                if (!NT_SUCCESS(status)) {

                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_PNP,
                                "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write ExplicitlySet. Status %x.\n",
                                DeviceInfo,
                                status));
                }
            }

            if (NT_SUCCESS(status)) {

                RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH);
                status = ZwQueryValueKey(deviceKey,
                                         &keyValueName,
                                         KeyValuePartialInformation,
                                         &preferredPath,
                                         sizeof(preferredPath),
                                         &length);

                if (NT_SUCCESS(status)) {

                    NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG));

                    *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data));

                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_PNP,
                                "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n",
                                DeviceInfo,
                                *PreferredPath));

                } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {

                    *PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG);

                    //
                    // The device key must have been newly created.
                    // Set a bogus preferred path as default.
                    //
                    status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                   deviceKey,
                                                   DSM_PREFERRED_PATH,
                                                   REG_BINARY,
                                                   PreferredPath,
                                                   sizeof(ULONGLONG));
                    if (!NT_SUCCESS(status)) {

                        TracePrint((TRACE_LEVEL_ERROR,
                                    TRACE_FLAG_PNP,
                                    "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write PreferredPath. Status %x.\n",
                                    DeviceInfo,
                                    status));
                    }
                }
            }
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to create LB policy registry key. Status %x.\n",
                    DeviceInfo,
                    status));

        deviceKey = NULL;
    }

__Exit_DsmpQueryDeviceLBPolicyFromRegistry:

    if (deviceKey) {
        ZwClose(deviceKey);
    }

    if (lbSettingsKey) {
        ZwClose(lbSettingsKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpQueryTargetLBPolicyFromRegistry(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _Out_ OUT PULONGLONG PreferredPath
    )
/*++

Routine Description:

    Query the load balance policy for the VID/PID of the passed in device from
    the registry if it has been set.

Arguements:

    DeviceInfo - Device's whose VID/PID we need to compare against.
    LoadBalanceType - Type of LB policy.
    PreferredPath - The preferred path for the device.

Return Value :

    STATUS_SUCCESS if we were able to successfully query the registry for the info.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE targetsLBSettingKey = NULL;
    HANDLE targetKey = NULL;
    UNICODE_STRING subKeyName;
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status = STATUS_INVALID_PARAMETER;
    UNICODE_STRING keyValueName;
    ULONG length;
    struct _preferredPath {
        KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
        ULONGLONG Data;
    } preferredPath;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Entering function.\n",
                DeviceInfo));

    if (!LoadBalanceType || !PreferredPath) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Invalid parameter.\n",
                    DeviceInfo));

        goto __Exit_DsmpQueryTargetLBPolicyFromRegistry;
    }

    if (!DeviceInfo->Group->HardwareId) {

        status = STATUS_UNSUCCESSFUL;

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Couldn't build hardware id for passed in device.\n",
                    DeviceInfo));

        goto __Exit_DsmpQueryTargetLBPolicyFromRegistry;
    }

    //
    // Query the Load Balance settings for the given target from the registry.
    // First open TargetsLoadBalanceSetting key under the service key.
    //
    status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open Targets LB Setting key. Status %x.\n",
                    DeviceInfo,
                    status));

        goto __Exit_DsmpQueryTargetLBPolicyFromRegistry;
    }

    RtlInitUnicodeString(&subKeyName, DeviceInfo->Group->HardwareId);

    InitializeObjectAttributes(&objectAttributes,
                               &subKeyName,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               targetsLBSettingKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    //
    // Open the VID/PID key under DsmTargetsLoadBalanceSetting key.
    //
    status = ZwOpenKey(&targetKey, KEY_ALL_ACCESS, &objectAttributes);

    if (NT_SUCCESS(status)) {

        RTL_QUERY_REGISTRY_TABLE queryTable[2];

        RtlZeroMemory(queryTable, sizeof(queryTable));

        queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                              RTL_QUERY_REGISTRY_REQUIRED | 
                              RTL_QUERY_REGISTRY_TYPECHECK;
        queryTable[0].Name = DSM_LOAD_BALANCE_POLICY;
        queryTable[0].EntryContext = LoadBalanceType;
        queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

        status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                        targetKey,
                                        queryTable,
                                        targetKey,
                                        NULL);
        if (NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n",
                        DeviceInfo,
                        *LoadBalanceType));
        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query LB Policy - error %x.\n",
                        DeviceInfo,
                        status));
        }

        if (NT_SUCCESS(status)) {

            RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH);
            status = ZwQueryValueKey(targetKey,
                                     &keyValueName,
                                     KeyValuePartialInformation,
                                     &preferredPath,
                                     sizeof(preferredPath),
                                     &length);

            if (NT_SUCCESS(status)) {

                NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG));

                *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data));

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n",
                            DeviceInfo,
                            *PreferredPath));

            } else {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query PreferredPath. Status %x.\n",
                            DeviceInfo,
                            status));
            }
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open LB policy registry key. Status %x.\n",
                    DeviceInfo,
                    status));

        targetKey = NULL;
    }

__Exit_DsmpQueryTargetLBPolicyFromRegistry:

    if (targetKey) {
        ZwClose(targetKey);
    }

    if (targetsLBSettingKey) {
        ZwClose(targetsLBSettingKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpQueryDsmLBPolicyFromRegistry(
    _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _Out_ OUT PULONGLONG PreferredPath
    )
/*++

Routine Description:

    Query the overall load balance policy for MSDSM controlled devices from
    the registry if it has been set.

Arguements:

    LoadBalanceType - Type of LB policy.
    PreferredPath - The preferred path for the device.

Return Value :

    STATUS_SUCCESS if we were able to successfully query the registry for the info.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE parametersKey = NULL;
    NTSTATUS status = STATUS_INVALID_PARAMETER;
    UNICODE_STRING keyValueName;
    RTL_QUERY_REGISTRY_TABLE queryTable[2];
    ULONG length;
    struct _preferredPath {
        KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
        ULONGLONG Data;
    } preferredPath;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryDsmLBPolicyFromRegistry: Entering function.\n"));

    if (!LoadBalanceType || !PreferredPath) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDsmLBPolicyFromRegistry: Invalid parameter.\n"));

        goto __Exit_DsmpQueryDsmLBPolicyFromRegistry;
    }

    //
    // Query the overall default Load Balance settings for MSDSM from the registry.
    // First open the Parameters key under the service key.
    //
    status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, &parametersKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDsmLBPolicyFromRegistry: Failed to open Parameters key. Status %x.\n",
                    status));

        goto __Exit_DsmpQueryDsmLBPolicyFromRegistry;
    }

    RtlZeroMemory(queryTable, sizeof(queryTable));

    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                          RTL_QUERY_REGISTRY_REQUIRED | 
                          RTL_QUERY_REGISTRY_TYPECHECK;
    queryTable[0].Name = DSM_LOAD_BALANCE_POLICY;
    queryTable[0].EntryContext = LoadBalanceType;
    queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

    status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                    parametersKey,
                                    queryTable,
                                    parametersKey,
                                    NULL);
    if (NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDsmLBPolicyFromRegistry: LB Policy is %d.\n",
                    *LoadBalanceType));

    } else {

        TracePrint((TRACE_LEVEL_INFORMATION,
                    TRACE_FLAG_PNP,
                    "DsmpQueryDsmLBPolicyFromRegistry: Failed to query LB policy. Status %x.\n",
                    status));

        goto __Exit_DsmpQueryDsmLBPolicyFromRegistry;
    }

    if (NT_SUCCESS(status)) {

        RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH);
        status = ZwQueryValueKey(parametersKey,
                                 &keyValueName,
                                 KeyValuePartialInformation,
                                 &preferredPath,
                                 sizeof(preferredPath),
                                 &length);

        if (NT_SUCCESS(status)) {

            NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG));

            *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data));

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpQueryDsmLBPolicyFromRegistry: PreferredPath is %I64x.\n",
                        *PreferredPath));

        } else {

            TracePrint((TRACE_LEVEL_INFORMATION,
                        TRACE_FLAG_PNP,
                        "DsmpQueryDsmLBPolicyFromRegistry: Failed to query PreferredPath. Status %x.\n",
                        status));
        }
    }

__Exit_DsmpQueryDsmLBPolicyFromRegistry:

    if (parametersKey) {
        ZwClose(parametersKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryDsmLBPolicyFromRegistry: Exiting function with status %x.\n",
                status));

    return status;
}


NTSTATUS
DsmpSetDsmLBPolicyInRegistry(
    _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _In_ IN ULONGLONG PreferredPath
    )
/*++

Routine Description:

    Set the overall load balance policy for MSDSM controlled devices in
    the registry.
    Note: If the policy specified is 0, remove the currently set values
          for policy and preferred path.

Arguements:

    LoadBalanceType - Type of LB policy.
    PreferredPath - The preferred path for devices controlled by DSM.

Return Value :

    STATUS_SUCCESS if we were able to successfully set the info in the registry.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE parametersKey = NULL;
    NTSTATUS status;
    UNICODE_STRING lbPolicyValueName;
    UNICODE_STRING preferredPathValueName;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetDsmLBPolicyInRegistry: Entering function.\n"));

    //
    // First open the Parameters key under the service key.
    //
    status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, &parametersKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpSetDsmLBPolicyInRegistry: Failed to open Parameters key. Status %x.\n",
                    status));

        goto __Exit_DsmpSetDsmLBPolicyInRegistry;
    }

    RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY);
    RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH);

    //
    // If the LB policy is specified as 0, we need to delete the values.
    //
    if (LoadBalanceType < DSM_LB_FAILOVER) {

        status = ZwDeleteValueKey(parametersKey, &preferredPathValueName);

        if (NT_SUCCESS(status) || status == STATUS_OBJECT_NAME_NOT_FOUND) {

            status = ZwDeleteValueKey(parametersKey, &lbPolicyValueName);
        }

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetDsmLBPolicyInRegistry: Failed to delete either preferredPath or lbPolicy. Status %x.\n",
                        status));
        }
    } else {

        status = ZwSetValueKey(parametersKey,
                               &lbPolicyValueName,
                               0,
                               REG_DWORD,
                               &LoadBalanceType,
                               sizeof(ULONG));

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetDsmLBPolicyInRegistry: Failed to set LB policy in registry. Status %x.\n",
                        status));

            goto __Exit_DsmpSetDsmLBPolicyInRegistry;
        }

        status = ZwSetValueKey(parametersKey,
                               &preferredPathValueName,
                               0,
                               REG_BINARY,
                               &PreferredPath,
                               sizeof(ULONGLONG));

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetDsmLBPolicyInRegistry: Failed to set preferred path in registry. Status %x.\n",
                        status));
        }
    }

__Exit_DsmpSetDsmLBPolicyInRegistry:

    if (parametersKey) {
        ZwClose(parametersKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetDsmLBPolicyInRegistry: Exiting function with status %x.\n",
                status));

    return status;
}


NTSTATUS
DsmpSetVidPidLBPolicyInRegistry(
    _In_ IN PWSTR TargetHardwareId,
    _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _In_ IN ULONGLONG PreferredPath
    )
/*++

Routine Description:

    Set the default load balance policy for MSDSM controlled devices for
    a particular target VID/PID in the registry.
    Note: If the policy specified is 0, remove the subkey that matches
          the passed in TargetHardwareId.

Arguements:

    TargetHardwareId - The VID/PID for which a default LB policy is being set.
    LoadBalanceType - Type of LB policy.
    PreferredPath - The preferred path for devices controlled by DSM.

Return Value :

    STATUS_SUCCESS if we were able to successfully set the info in the registry.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE targetsLBSettingKey = NULL;
    HANDLE targetSubKey = NULL;
    NTSTATUS status;
    UNICODE_STRING vidPidKeyName;
    UNICODE_STRING lbPolicyValueName;
    UNICODE_STRING preferredPathValueName;
    OBJECT_ATTRIBUTES objectAttributes;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetVidPidLBPolicyInRegistry (%ws): Entering function.\n",
                TargetHardwareId));

    //
    // First open the DsmTargetsLoadBalanceSetting key under the service's parameters key.
    //
    status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open Targets Policy settings key. Status %x.\n",
                    TargetHardwareId,
                    status));

        goto __Exit_DsmpSetVidPidLBPolicyInRegistry;
    }

    RtlInitUnicodeString(&vidPidKeyName, TargetHardwareId);
    InitializeObjectAttributes(&objectAttributes,
                               &vidPidKeyName,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               targetsLBSettingKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    //
    // If the LB policy is specified as 0, we need to delete the values.
    //
    if (LoadBalanceType < DSM_LB_FAILOVER) {

        //
        // Open the VID/PID key under DsmTargetsLoadBalanceSetting key.
        //
        status = ZwOpenKey(&targetSubKey, KEY_ALL_ACCESS, &objectAttributes);

        if (NT_SUCCESS(status)) {

            status = ZwDeleteKey(targetSubKey);
        }

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to either open or delete. Status %x.\n",
                        TargetHardwareId,
                        status));
        }
    } else {

        RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY);
        RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH);

        status = ZwCreateKey(&targetSubKey,
                             KEY_ALL_ACCESS,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             NULL);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open/create key in registry. Status %x.\n",
                        TargetHardwareId,
                        status));

            goto __Exit_DsmpSetVidPidLBPolicyInRegistry;
        }

        status = ZwSetValueKey(targetSubKey,
                               &lbPolicyValueName,
                               0,
                               REG_DWORD,
                               &LoadBalanceType,
                               sizeof(ULONG));

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set LB policy in registry. Status %x.\n",
                        TargetHardwareId,
                        status));

            goto __Exit_DsmpSetVidPidLBPolicyInRegistry;
        }

        status = ZwSetValueKey(targetSubKey,
                               &preferredPathValueName,
                               0,
                               REG_BINARY,
                               &PreferredPath,
                               sizeof(ULONGLONG));

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set preferred path in registry. Status %x.\n",
                        TargetHardwareId,
                        status));
        }
    }

__Exit_DsmpSetVidPidLBPolicyInRegistry:

    if (targetSubKey) {
        ZwClose(targetSubKey);
    }

    if (targetsLBSettingKey) {
        ZwClose(targetsLBSettingKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpSetVidPidLBPolicyInRegistry (%ws): Exiting function with status %x.\n",
                TargetHardwareId,
                status));

    return status;
}


NTSTATUS
DsmpOpenLoadBalanceSettingsKey(
    _In_ IN  ACCESS_MASK    AccessMask,
    _Out_ OUT PHANDLE LoadBalanceSettingsKey
    )
/*++

Routine Description:

    Open the device key in the registry.

    NOTE: It is the responsibility of the caller to close the returned handle.

Arguements:

    AccessMask - Requested access with which to open key
    LoadBalanceSettingsKey - handle of the key that is returned to the caller

Return Value :

    STATUS_SUCCESS if we were able to successfully open the registry key.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE serviceKey = NULL;
    HANDLE parametersKey = NULL;
    PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath);
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING parametersKeyName;
    UNICODE_STRING subKeyName;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Entering function.\n",
                registryPath));

    *LoadBalanceSettingsKey = NULL;

    //
    // First check if registry path is available for msdsm.
    //
    if (!registryPath->Buffer) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Registry Path not set.\n",
                    registryPath));

        goto __Exit_DsmpOpenLoadBalanceSettingsKey;
    }

    //
    // Open the service key first
    //
    InitializeObjectAttributes(&objectAttributes,
                               registryPath,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               NULL,
                               NULL);

    status = ZwOpenKey(&serviceKey,
                       AccessMask,
                       &objectAttributes);
    if (NT_SUCCESS(status)) {

        //
        // Open Parameters key under the Service key
        //
        RtlInitUnicodeString(&parametersKeyName, DSM_SERVICE_PARAMETERS);

        RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

        InitializeObjectAttributes(&objectAttributes,
                                   &parametersKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   serviceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwOpenKey(&parametersKey,
                           AccessMask,
                           &objectAttributes);

        if (NT_SUCCESS(status)) {

            //
            // Open LoadBalanceSettings key under the Parameters key
            //
            RtlInitUnicodeString(&subKeyName, DSM_LOAD_BALANCE_SETTINGS);

            RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

            InitializeObjectAttributes(&objectAttributes,
                                       &subKeyName,
                                       (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                       parametersKey,
                                       (PSECURITY_DESCRIPTOR) NULL);

            status = ZwCreateKey(LoadBalanceSettingsKey,
                                 AccessMask,
                                 &objectAttributes,
                                 0,
                                 NULL,
                                 REG_OPTION_NON_VOLATILE,
                                 NULL);

            if (!NT_SUCCESS(status)) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open/create LBSettings key. Status %x.\n",
                            registryPath,
                            status));

                *LoadBalanceSettingsKey = NULL;
            }

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open parameters key. Status %x.\n",
                        registryPath,
                        status));
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open service key %ws. Status %x.\n",
                    registryPath,
                    registryPath->Buffer,
                    status));
    }

__Exit_DsmpOpenLoadBalanceSettingsKey:

    if (parametersKey) {
        ZwClose(parametersKey);
    }

    if (serviceKey) {
        ZwClose(serviceKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Exiting function with status %x.\n",
                registryPath,
                status));

    return status;
}


NTSTATUS
DsmpOpenTargetsLoadBalanceSettingKey(
    _In_ IN  ACCESS_MASK    AccessMask,
    _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey
    )
/*++

Routine Description:

    Open the target key in the registry.

    NOTE: It is the responsibility of the caller to close the returned handle.

Arguements:

    AccessMask - Requested access with which to open key
    LoadBalanceSettingsKey - handle of the key that is returned to the caller

Return Value :

    STATUS_SUCCESS if we were able to successfully open the registry key.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE serviceKey = NULL;
    HANDLE parametersKey = NULL;
    PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath);
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING parametersKeyName;
    UNICODE_STRING subKeyName;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Entering function.\n",
                registryPath));

    if (!TargetsLoadBalanceSettingKey) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Invalid parameter.\n",
                    registryPath));

        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey;
    }

    *TargetsLoadBalanceSettingKey = NULL;

    //
    // First check if registry path is available for msdsm.
    //
    if (!registryPath->Buffer) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Registry Path not set.\n",
                    registryPath));

        goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey;
    }

    //
    // Open the service key first
    //
    InitializeObjectAttributes(&objectAttributes,
                               registryPath,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               NULL,
                               NULL);

    status = ZwOpenKey(&serviceKey,
                       AccessMask,
                       &objectAttributes);
    if (NT_SUCCESS(status)) {

        //
        // Open Parameters key under the Service key
        //
        RtlInitUnicodeString(&parametersKeyName, DSM_SERVICE_PARAMETERS);

        RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

        InitializeObjectAttributes(&objectAttributes,
                                   &parametersKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   serviceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwOpenKey(&parametersKey,
                           AccessMask,
                           &objectAttributes);

        if (NT_SUCCESS(status)) {

            //
            // Open LoadBalanceSettings key under the Parameters key
            //
            RtlInitUnicodeString(&subKeyName, DSM_TARGETS_LOAD_BALANCE_SETTING);

            RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

            InitializeObjectAttributes(&objectAttributes,
                                       &subKeyName,
                                       (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                       parametersKey,
                                       (PSECURITY_DESCRIPTOR) NULL);

            status = ZwCreateKey(TargetsLoadBalanceSettingKey,
                                 AccessMask,
                                 &objectAttributes,
                                 0,
                                 NULL,
                                 REG_OPTION_NON_VOLATILE,
                                 NULL);

            if (!NT_SUCCESS(status)) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open/create TargetsLBSetting key. Status %x.\n",
                            registryPath,
                            status));

                *TargetsLoadBalanceSettingKey = NULL;
            }

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open parameters key. Status %x.\n",
                        registryPath,
                        status));
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open service key %ws. Status %x.\n",
                    registryPath,
                    registryPath->Buffer,
                    status));
    }

__Exit_DsmpOpenTargetsLoadBalanceSettingKey:

    if (parametersKey) {
        ZwClose(parametersKey);
    }

    if (serviceKey) {
        ZwClose(serviceKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Exiting function with status %x.\n",
                registryPath,
                status));

    return status;
}


NTSTATUS
DsmpOpenDsmServicesParametersKey(
    _In_ IN  ACCESS_MASK AccessMask,
    _Out_ OUT PHANDLE ParametersKey
    )
/*++

Routine Description:

    Open the DSM's Parameters key in the registry.

    NOTE: It is the responsibility of the caller to close the returned handle.

Arguements:

    AccessMask - Requested access with which to open key
    ParametersKey - handle of the key that is returned to the caller

Return Value :

    STATUS_SUCCESS if we were able to successfully open the registry key.

    Appropriate NTSTATUS code on failure

--*/
{
    HANDLE serviceKey = NULL;
    PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath);
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING parametersKeyName;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenDsmServicesParametersKey (RegPath %p): Entering function.\n",
                registryPath));

    if (!ParametersKey) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenDsmServicesParametersKey (RegPath %p): Invalid parameter.\n",
                    registryPath));

        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpOpenDsmServicesParametersKey;
    }

    *ParametersKey = NULL;

    //
    // First check if registry path is available for msdsm.
    //
    if (!registryPath->Buffer) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenDsmServicesParametersKey (RegPath %p): Registry Path not set.\n",
                    registryPath));

        goto __Exit_DsmpOpenDsmServicesParametersKey;
    }

    //
    // Open the service key first
    //
    InitializeObjectAttributes(&objectAttributes,
                               registryPath,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               NULL,
                               NULL);

    status = ZwOpenKey(&serviceKey,
                       AccessMask,
                       &objectAttributes);
    if (NT_SUCCESS(status)) {

        //
        // Open Parameters key under the Service key
        //
        RtlInitUnicodeString(&parametersKeyName, DSM_SERVICE_PARAMETERS);

        RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

        InitializeObjectAttributes(&objectAttributes,
                                   &parametersKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   serviceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwOpenKey(ParametersKey,
                           AccessMask,
                           &objectAttributes);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open parameters key. Status %x.\n",
                        registryPath,
                        status));

            *ParametersKey = NULL;
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open service key %ws. Status %x.\n",
                    registryPath,
                    registryPath->Buffer,
                    status));
    }

__Exit_DsmpOpenDsmServicesParametersKey:

    if (serviceKey) {
        ZwClose(serviceKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpOpenDsmServicesParametersKey (RegPath %p): Exiting function with status %x.\n",
                registryPath,
                status));

    return status;
}

NTSTATUS
DsmpReportTargetPortGroupsSyncCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
{
    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Context);

    TracePrint((TRACE_LEVEL_INFORMATION,
                TRACE_FLAG_GENERAL,
                "DsmpReportTargetPortGroupsSyncCompletion: IRP %p, Context %p\n",
                Irp, Context));

    KeSetEvent(Irp->UserEvent, 0, FALSE);

    return STATUS_MORE_PROCESSING_REQUIRED;
}

_Success_(return==0)
NTSTATUS
DsmpReportTargetPortGroups(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo,
    _Out_ PULONG TargetPortGroupsInfoLength
    )
/*++

Routine Description:

    Helper routine to send down ReportTargetPortGroups request synchronously.
    Used if device supports ALUA.

    Note: This routine will allocate memory for the TPG info. It is the
    responsibility of the caller to free this buffer, but only if the function
    returns STATUS_SUCCESS.

Arguments:

    DeviceObject - The port PDO to which the command should be sent.
    TargetPortGroupsInfo - buffer containing the returned data.
    TargetPortGroupsInfoLength - size of the returned buffer.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb;
    NTSTATUS status = STATUS_SUCCESS;
    PIRP irp = NULL;
    PMDL mdl = NULL;
    PSCSI_REQUEST_BLOCK srb = NULL;
    PSENSE_DATA_EX senseInfoBuffer = NULL;
    UCHAR senseInfoBufferLength = 0;
    KEVENT completionEvent;
    ULONG targetPortGroupsInfoLength = 0;
    PUCHAR targetPortGroupsInfo = NULL;
    PIO_STACK_LOCATION irpStack = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpReportTargetPortGroups (DevObj %p): Entering function.\n",
                DeviceObject));

    if (TargetPortGroupsInfoLength == NULL ||
        TargetPortGroupsInfo == NULL) {
        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpReportTargetPortGroups;
    }

    *TargetPortGroupsInfoLength = 0;
    *TargetPortGroupsInfo = NULL;

    senseInfoBuffer = (PSENSE_DATA_EX)DsmpAllocatePool(NonPagedPoolNx,
                                                       SENSE_BUFFER_SIZE_EX,
                                                       DSM_TAG_SCSI_SENSE_INFO);
    if (senseInfoBuffer != NULL) {

        senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;

        srb = (PSCSI_REQUEST_BLOCK)DsmpAllocatePool(NonPagedPoolNx,
                                                    sizeof(SCSI_REQUEST_BLOCK),
                                                    DSM_TAG_SCSI_REQUEST_BLOCK);
        if (srb != NULL) {

            SrbSetSenseInfoBufferLength(srb, senseInfoBufferLength);
            SrbSetSenseInfoBuffer(srb, senseInfoBuffer);

            //
            // Take care of worst case scenario, which is:
            // 1. 4-byte header (for allocation length)
            // 2. 32 8-byte descriptors (for TPGs)
            // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG)
            //
            targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE +
                                         (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) +
                                                           DSM_MAX_PATHS * sizeof(ULONG)));

            targetPortGroupsInfo = (PUCHAR)DsmpAllocatePool(NonPagedPoolNx,
                                                            targetPortGroupsInfoLength,
                                                            DSM_TAG_TARGET_PORT_GROUPS);

            if (targetPortGroupsInfo == NULL) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_GENERAL,
                            "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate TPG info.\n",
                            DeviceObject));
                goto __Exit_DsmpReportTargetPortGroups;
            }

        } else {
            status = STATUS_INSUFFICIENT_RESOURCES;
            TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate SRB.\n",
                    DeviceObject));
            goto __Exit_DsmpReportTargetPortGroups;
        }

    } else {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate Sense Info Buffer.\n",
                    DeviceObject));
        goto __Exit_DsmpReportTargetPortGroups;
    }

    irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE);
    if (irp == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate IRP.\n",
                    DeviceObject));
        goto __Exit_DsmpReportTargetPortGroups;
    }

    mdl = IoAllocateMdl(targetPortGroupsInfo,
                        targetPortGroupsInfoLength,
                        FALSE,
                        FALSE,
                        irp);

    if (mdl == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                TRACE_FLAG_GENERAL,
                "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate MDL.\n",
                DeviceObject));
        goto __Exit_DsmpReportTargetPortGroups;
    }

    MmBuildMdlForNonPagedPool(mdl);

__Retry_DsmpReportTargetPortGroups:

    irp->MdlAddress = mdl;

    //
    // Set up SRB for execute scsi request. Save SRB address in next stack
    // for the port driver.
    //
    irpStack = IoGetNextIrpStackLocation(irp);
    irpStack->MajorFunction = IRP_MJ_SCSI;
    irpStack->MinorFunction = IRP_MN_SCSI_CLASS;
    irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
    irpStack->DeviceObject = DeviceObject;

    //
    // Set the completion event and the completion routine.
    //
    KeInitializeEvent(&completionEvent, NotificationEvent, FALSE);
    irp->UserEvent = &completionEvent;
    IoSetCompletionRoutine(irp,
                           DsmpReportTargetPortGroupsSyncCompletion,
                           srb,
                           TRUE,
                           TRUE,
                           TRUE);

    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
    srb->Length = sizeof(SCSI_REQUEST_BLOCK);

    SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS));
    cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb);
    cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS;
    cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS;
    REVERSE_BYTES(&(cdb->AllocationLength), &targetPortGroupsInfoLength);

    SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT);
    SrbSetDataTransferLength(srb, targetPortGroupsInfoLength);
    SrbSetDataBuffer(srb, targetPortGroupsInfo);
    srb->SrbStatus = 0;
    SrbSetScsiStatus(srb, 0);
    SrbSetNextSrb(srb, NULL);
    SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE |
                        SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
                        SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE);
    SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
    SrbSetOriginalRequest(srb, irp);

    ObReferenceObject(DeviceObject);

    //
    // Finally, send the IRP down and wait for its completion.
    //
    status = IoCallDriver(DeviceObject, irp);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&completionEvent,
                              Executive,
                              KernelMode,
                              FALSE,
                              NULL);
        status = irp->IoStatus.Status;
    }

    ObDereferenceObject(DeviceObject);

    if ((status == STATUS_BUFFER_OVERFLOW) ||
        (NT_SUCCESS(status) && (SrbGetScsiStatus(srb) == SCSISTAT_GOOD))) {

        //
        // The first 4 bytes of the returned data are the Returned Data Length
        // field of the RTPG header.
        //
        ULONG returnedDataLength = 0;
        REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo);

        status = STATUS_SUCCESS;
        if (returnedDataLength > SrbGetDataTransferLength(srb)) {

            status = STATUS_BUFFER_OVERFLOW;
        }
    }

    if (NT_SUCCESS(status) && SrbGetScsiStatus(srb) == SCSISTAT_GOOD) {

        //
        // RTPG was successful so return the TPG info to the caller.
        //

        //
        // The first 4 bytes of the returned data are the Returned Data Length
        // field of the RTPG header.  We need to return this value plus the header size.
        //
        ULONG returnedDataLength = 0;
        REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo);
        *TargetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength;

        *TargetPortGroupsInfo = targetPortGroupsInfo;

    } else if (SrbGetScsiStatus(srb) == SCSISTAT_CHECK_CONDITION) {

        if (DsmpShouldRetryTPGRequest(senseInfoBuffer, senseInfoBufferLength)) {

            TracePrint((TRACE_LEVEL_INFORMATION,
                        TRACE_FLAG_GENERAL,
                        "DsmpReportTargetPortGroups (DevObj %p): Retrying request.\n",
                        DeviceObject));

            IoReuseIrp(irp, STATUS_SUCCESS);

            RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength);

            goto __Retry_DsmpReportTargetPortGroups;
        }

        if (DsmpIsDeviceRemoved(senseInfoBuffer, senseInfoBufferLength)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_GENERAL,
                        "DsmpReportTargetPortGroups (DevObj %p): Device not available.\n",
                        DeviceObject));

            //
            // Sense key was illegal request. SPC 6.25 says response to TPG should follow Test Unit Ready responses
            //
            status = STATUS_NO_SUCH_DEVICE;

        }

        // RTPG was unsuccessful
        // Here it is possible that status is success, but scsi status is not.
        // and there was no RTPG retry. If so, set status to unsuccessful.
        if (NT_SUCCESS(status)) {
            status = STATUS_UNSUCCESSFUL;
        }

        //
        // TPG resulted HW to respond with Check Condition but Sense Key indicates it is not for retry or illegal request
        //
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpReportTargetPortGroups (DevObj %p): TPG returned Check Condition, NTStatus 0x%x, ScsiStatus 0x%x.\n",
                    DeviceObject,
                    status,
                    SrbGetScsiStatus(srb)));
    } else {

        // RTPG was unsuccessful
        // Here it is possible that status is success, but scsi status is not.
        // If so, set status to unsuccessful.
        if (NT_SUCCESS(status)) {
            status = STATUS_UNSUCCESSFUL;
        }

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpReportTargetPortGroups (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n",
                    DeviceObject,
                    status,
                    SrbGetScsiStatus(srb)));
    }

__Exit_DsmpReportTargetPortGroups:

    //
    // The port driver may have allocated its own sense buffer so we need to
    // make sure we free that here.
    //
    if (srb != NULL &&
        SrbGetSrbFlags(srb) & SRB_FLAGS_PORT_DRIVER_ALLOCSENSE &&
        SrbGetSrbFlags(srb) & SRB_FLAGS_FREE_SENSE_BUFFER &&
        SrbGetSenseInfoBuffer(srb) != NULL) {
        DsmpFreePool(SrbGetSenseInfoBuffer(srb));
    }

    if (senseInfoBuffer) {
        DsmpFreePool(senseInfoBuffer);
    }

    if (srb) {
        DsmpFreePool(srb);
    }

    if (irp) {
        if (irp->MdlAddress) {
            IoFreeMdl(irp->MdlAddress);
        }
        IoFreeIrp(irp);
    }

    if (!NT_SUCCESS(status) && targetPortGroupsInfo) {
        DsmpFreePool(targetPortGroupsInfo);
        *TargetPortGroupsInfoLength = 0;
        *TargetPortGroupsInfo = NULL;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpReportTargetPortGroups (DevObj %p): Exiting function with status %x.\n",
                DeviceObject,
                status));

    return status;
}

NTSTATUS
DsmpReportTargetPortGroupsAsync(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine,
    _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext,
    _In_ IN ULONG TargetPortGroupsInfoLength,
    _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo
    )
/*++

Routine Description:

    Helper routine to send down ReportTargetPortGroups request asynchronously.
    Used if device supports ALUA.

    NOTE: Caller needs to free Irp, system buffer, and passThrough buffer.

Arguments:

    DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to.
    CompletionRoutine - completion routine passed in by the caller.
    CompletionContext - context to be passed to be completion routine.
    TargetPortGroupsInfoLength - size of the returned buffer.
    TargetPortGroupsInfo - preallocated (by caller) buffer that'll contain the returned data.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext;
    PSCSI_REQUEST_BLOCK srb = NULL;
    PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb;
    NTSTATUS status;
    PIRP irp = NULL;
    PIO_STACK_LOCATION irpStack;
    PMDL mdl = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_RW,
                "DsmpReportTargetPortGroupsAsync (DevInfo %p): Entering function.\n",
                DeviceInfo));

    srb = tpgCompletionContext->Srb;

    SrbZeroSrb(srb);

    //
    // Allocate an irp.
    //
    irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE);
    if (!irp) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_RW,
                    "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n",
                    DeviceInfo));
        goto __Exit_DsmpReportTargetPortGroupsAsync;
    }

    mdl = IoAllocateMdl(TargetPortGroupsInfo,
                        TargetPortGroupsInfoLength,
                        FALSE,
                        FALSE,
                        irp);
    if (!mdl) {

        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_RW,
                    "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n",
                    DeviceInfo));
        goto __Exit_DsmpReportTargetPortGroupsAsync;
    }

    MmBuildMdlForNonPagedPool(irp->MdlAddress);

    //
    // It is possible that if an implicit access state transition took place,
    // each I_T nexus will return UA for asymmetric access state changed. So
    // set the number of retries to be one more than the total number of paths.
    // Worst case scenario is the it is sent down each path once (assuming every
    // is a different I_T nexus) and then one more for a retry one one of the
    // paths.
    //
    tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1;

    //
    // Set-up the completion routine.
    //
    IoSetCompletionRoutine(irp,
                           CompletionRoutine,
                           (PVOID)CompletionContext,
                           TRUE,
                           TRUE,
                           TRUE);

    //
    // Get the recipient's irpstack location.
    //
    irpStack = IoGetNextIrpStackLocation(irp);

    irpStack->Parameters.Scsi.Srb = srb;
    irpStack->DeviceObject = DeviceInfo->TargetObject;

    //
    // Set the major function code to IRP_MJ_SCSI.
    //
    irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    //
    // Set the minor function, or many requests will get kicked by by port.
    //
    irpStack->MinorFunction = IRP_MN_SCSI_CLASS;

    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
    srb->Length = sizeof(SCSI_REQUEST_BLOCK);

    SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS));
    cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb);
    cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS;
    cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS;
    Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->AllocationLength);

    SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT);
    SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer);
    SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength);
    SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength);
    SrbSetDataBuffer(srb, TargetPortGroupsInfo);
    srb->SrbStatus = 0;
    SrbSetScsiStatus(srb, 0);
    SrbSetNextSrb(srb, NULL);
    SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE |
                        SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
                        SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE);
    SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
    SrbSetOriginalRequest(srb, irp);

    irp->UserBuffer = TargetPortGroupsInfo;
    irp->Tail.Overlay.Thread = PsGetCurrentThread();

    //
    // Send the IRP asynchronously
    //
    DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext,
                     DeviceInfo->TargetObject,
                     irp,
                     (PVOID)DeviceInfo,
                     DSM_CALL_COMPLETION_ON_MPIO_ERROR);

    //
    // We know that the completion routine will always be called.
    //
    status = STATUS_PENDING;

__Exit_DsmpReportTargetPortGroupsAsync:

    if (status != STATUS_PENDING) {

        //
        // This indicates Irp was never sent down to stack (completion routine was never called).
        // We need to clean up.
        //
        if (irp) {

            if (irp->MdlAddress) {
                IoFreeMdl(irp->MdlAddress);
            }

            IoFreeIrp(irp);
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_RW,
                "DsmpReportTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x\n.",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpQueryLBPolicyForDevice(
    _In_ IN PWSTR RegistryKeyName,
    _In_ IN  ULONGLONG PathId,
    _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType,
    _Out_ OUT PULONG PrimaryPath,
    _Out_ OUT PULONG OptimizedPath,
    _Out_ OUT PULONG PathWeight
    )
/*++

Routine Description:

    This routine opens the device's registry subkey, builds the path subkey from
    the passed in PathId, then queries that subkey for the value of PrimaryPath,
    OptimizedPath and PathWeight.

Arguments:

    RegistryKeyName - The device's registry subkey name.
    PathId - The pathId for this instance of the device.
    LoadBalanceType - The current load balance policy.
    PrimaryPath - Output of the queried PrimaryPath value.
    OptimizedPath - Output of the queried OptimizedPath value.
    PathWeight - Output of the queried PathWeight value.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    HANDLE lbSettingsKey = NULL;
    HANDLE deviceKey = NULL;
    HANDLE dsmPathKey = NULL;
    UNICODE_STRING subKeyName;
    WCHAR dsmPathName[128] = {0};
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    NTSTATUS pathWeightQueryStatus = STATUS_SUCCESS;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryLBPolicyForDevice (DevName %ws): Entering function.\n",
                RegistryKeyName));

    //
    // Query PrimaryPath and PathWeight for the given path.
    // These values are stored under DsmPath#Suffix key for
    // this path. If this key doesn't exist create it and
    // create PrimaryPath and PathWeight values - use the
    // values passed in PrimaryPath and PathWeight in this case.
    //
    status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey);
    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open LB Settings key. Status %x.\n",
                    RegistryKeyName,
                    status));

        goto __Exit_DsmpQueryLBPolicyForDevice;
    }

    RtlInitUnicodeString(&subKeyName, RegistryKeyName);

    InitializeObjectAttributes(&objectAttributes,
                               &subKeyName,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               lbSettingsKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(status)) {

        //
        // Create or open DsmPath#Suffix key for this path
        //
        DsmpGetDSMPathKeyName(PathId, dsmPathName, 128);

        TracePrint((TRACE_LEVEL_INFORMATION,
                    TRACE_FLAG_PNP,
                    "DsmpQueryLBPolicyForDevice (DevName %ws): Will query %ws for PrimaryPath, OptimizedPath and PathWeight.\n",
                    RegistryKeyName,
                    dsmPathName));

        RtlInitUnicodeString(&subKeyName, dsmPathName);

        RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

        InitializeObjectAttributes(&objectAttributes,
                                   &subKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   deviceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwCreateKey(&dsmPathKey,
                             KEY_ALL_ACCESS,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             NULL);

        if (NT_SUCCESS(status)) {

            RTL_QUERY_REGISTRY_TABLE queryTable[2];

            //
            // Query the Path Weight value.
            //

            RtlZeroMemory(queryTable, sizeof(queryTable));

            queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                                  RTL_QUERY_REGISTRY_REQUIRED | 
                                  RTL_QUERY_REGISTRY_TYPECHECK;
            queryTable[0].Name = DSM_PATH_WEIGHT;
            queryTable[0].EntryContext = PathWeight;
            queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

            pathWeightQueryStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                                           dsmPathKey,
                                                           queryTable,
                                                           dsmPathKey,
                                                           NULL);

            if (!NT_SUCCESS(pathWeightQueryStatus)) {
                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PathWeight. Status %x.\n",
                            RegistryKeyName,
                            pathWeightQueryStatus));
            }

            //
            // Query the Primary Path value.
            //

            RtlZeroMemory(queryTable, sizeof(queryTable));

            queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                                    RTL_QUERY_REGISTRY_REQUIRED | 
                                    RTL_QUERY_REGISTRY_TYPECHECK;
            queryTable[0].Name = DSM_PRIMARY_PATH;
            queryTable[0].EntryContext = PrimaryPath;
            queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

            status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                            dsmPathKey,
                                            queryTable,
                                            dsmPathKey,
                                            NULL);
            if (NT_SUCCESS(status)) {

                //
                // Query the Optimized Path value.
                //

                RtlZeroMemory(queryTable, sizeof(queryTable));

                queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
                                        RTL_QUERY_REGISTRY_REQUIRED | 
                                        RTL_QUERY_REGISTRY_TYPECHECK;
                queryTable[0].Name = DSM_OPTIMIZED_PATH;
                queryTable[0].EntryContext = OptimizedPath;
                queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

                status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                                dsmPathKey,
                                                queryTable,
                                                dsmPathKey,
                                                NULL);
                if (!NT_SUCCESS(status)) {
                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_PNP,
                                "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query OptimizedPath. Status %x.\n",
                                RegistryKeyName,
                                status));
                }
            } else {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PrimaryPath. Status %x.\n",
                            RegistryKeyName,
                            status));
            }

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to create DSM Path key %ws. Status %x.\n",
                        RegistryKeyName,
                        dsmPathName,
                        status));
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open key. Status %x.\n",
                    RegistryKeyName,
                    status));
    }

    if (NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpQueryLBPolicyForDevice (DevName %ws): PrimaryPath %d, OptmizedPath %d, PathWeight %d.\n",
                    RegistryKeyName,
                    *PrimaryPath,
                    *OptimizedPath,
                    *PathWeight));
    }

__Exit_DsmpQueryLBPolicyForDevice:

    if (dsmPathKey) {
        ZwClose(dsmPathKey);
    }

    if (deviceKey) {
        ZwClose(deviceKey);
    }

    if (lbSettingsKey) {
        ZwClose(lbSettingsKey);
    }

    //
    // If the load balance policy is Weighted Paths and we failed to read in
    // the path weight value, we need to return the failure status from the
    // path weight value query.
    //
    if (LoadBalanceType == DSM_LB_WEIGHTED_PATHS && !NT_SUCCESS(pathWeightQueryStatus)) {
        status = pathWeightQueryStatus;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryLBPolicyForDevice (DevName %ws): Exiting function with status %x.\n",
                RegistryKeyName,
                status));

    return status;
}


VOID
DsmpGetDSMPathKeyName(
    _In_ ULONGLONG DSMPathId,
    _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName,
    _In_ ULONG  DsmPathKeyNameSize
    )
/*++

Routine Description:

    This routine builds the string that corresponds to the device's Path subkey
    name in the registry.

Arguments:

    DSMPathId - The pathId of this instance of the device.
    DsmPathKeyName - Output buffer in which the subkey name for path is returned.
    DsmPathKeyNameSize - size  of the output buffer in WCHARs.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PWCHAR pathPtr;
    SIZE_T wcharsLeft;
    SIZE_T size;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDSMPathKeyName (PathId %I64x): Entering function.\n",
                DSMPathId));

    //
    // This routine will build a name for a given DSM Path.
    // The name is of the format DsmPath#Suffix, where Suffix
    // is derived from the PathId
    //
    pathPtr = DsmPathKeyName;

    wcharsLeft = DsmPathKeyNameSize;

    size = wcslen(DSM_PATH);

    if (size < wcharsLeft) {

        //
        // First copy the string DsmPath#
        //
        if (NT_SUCCESS(RtlStringCchCopyNW(pathPtr, wcharsLeft, DSM_PATH, wcslen(DSM_PATH)))) {

            wcharsLeft -= size;
            pathPtr += size;

            if (wcharsLeft > 2) {

                RtlStringCchCatW(pathPtr, wcharsLeft, L"#");
                wcharsLeft--;
                pathPtr++;

                //
                // Each nibble in the path id would need 1 WCHAR
                // upon conversion to WCHAR string. So we'll need
                // 2 WCHARs for each byte. Include the NULL char also
                //
                size = (sizeof(PVOID) + 1) * 2;
                if (size <= wcharsLeft) {

                    PVOID pathId;
                    PUCHAR pathIdPtr;
                    ULONG inx;
                    UCHAR tmpChar;

                    //
                    // Convert the ULONGLONG path id to a string and
                    // append that to DsmPath#
                    //
                    pathId = (PVOID) DSMPathId;

                    pathIdPtr = (PUCHAR) &pathId;

                    for (inx = 0; inx < sizeof(PVOID); inx++) {

                        tmpChar = (*pathIdPtr & 0xF0) >> 4;
                        *pathPtr++ = DsmpGetAsciiForBinary(tmpChar);

                        tmpChar = (*pathIdPtr & 0x0F);
                        *pathPtr++ = DsmpGetAsciiForBinary(tmpChar);

                        pathIdPtr++;
                    }

                    *pathPtr = WNULL;
                }
            }
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDSMPathKeyName (PathId %I64x): Exiting function.\n",
                DSMPathId));

    return;
}


UCHAR
DsmpGetAsciiForBinary(
    _In_ UCHAR BinaryChar
    )
/*++

Routine Description:

    This routine converts the passed in binary value into ASCII equivalent.

Arguments:

    BinaryChar - The binary value that needs to be converted.

Return Value:

    Corresponding ASCII value.

--*/
{
    UCHAR outChar = 0;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpGetAsciiForBinary (BinaryChar %d): Entering function.\n",
                BinaryChar));

    //
    // Convert a binary nibble into an ASCII character.
    //
    if ((BinaryChar >= 0) && (BinaryChar <= 9)) {
        outChar = BinaryChar + '0';
    } else {
        outChar = BinaryChar + 'A' - 10;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpGetAsciiForBinary (BinaryChar %d): Exiting function with outChar %c.\n",
                BinaryChar,
                outChar));

    return outChar;
}


NTSTATUS
DsmpGetDeviceIdList(
    _In_ IN PDEVICE_OBJECT DeviceObject,
    _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
    )
/*++

Routine Description:

    This routine will perform a query for the StorageDeviceIdProperty and will
    allocate a non-paged buffer to store the data in.
    IMPORTANT: It is the responsibility of the caller to ensure that this buffer is freed.

Arguments:

    DeviceObject - the device to query
    Descriptor - a location to store a pointer to the buffer we allocate

Return Value:

    status.

--*/
{
    STORAGE_PROPERTY_QUERY query;
    PIO_STATUS_BLOCK ioStatus = NULL;
    PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
    ULONG length;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDeviceIdList (DevObj %p): Entering function.\n",
                DeviceObject));

    if (!DeviceObject) {

        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpGetDeviceIdList;
    }

    //
    // Poison the passed in descriptor.
    //
    *Descriptor = NULL;

    //
    // Setup the query buffer.
    //
    query.PropertyId = StorageDeviceIdProperty;
    query.QueryType = PropertyStandardQuery;
    query.AdditionalParameters[0] = 0;

    ioStatus = DsmpAllocatePool(NonPagedPoolNx, sizeof(IO_STATUS_BLOCK), DSM_TAG_IO_STATUS_BLOCK);

    if (!ioStatus) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetDeviceIdList (DevObj %p): Failed to allocate an IO_STATUS_BLOCK.\n",
                    DeviceObject));
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpGetDeviceIdList;
    }

    ioStatus->Status = 0;
    ioStatus->Information = 0;

    //
    // On the first call, just need to get the length of the descriptor.
    //
    descriptor = (PVOID)&query;
    DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY,
                                      DeviceObject,
                                      &query,
                                      &query,
                                      sizeof(STORAGE_PROPERTY_QUERY),
                                      sizeof(STORAGE_DESCRIPTOR_HEADER),
                                      FALSE,
                                      ioStatus);

    status = ioStatus->Status;

    if(!NT_SUCCESS(status)) {

        descriptor = NULL;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetDeviceIdList (DevObj %p): Query failed (%x) on attempt 1.\n",
                    DeviceObject,
                    ioStatus->Status));

        goto __Exit_DsmpGetDeviceIdList;
    }

    NT_ASSERT(descriptor->Size);
    if (descriptor->Size == 0) {
        status = STATUS_UNSUCCESSFUL;
        goto __Exit_DsmpGetDeviceIdList;
    }

    //
    // This time we know how much data there is so we can
    // allocate a buffer of the correct size
    //
    length = descriptor->Size;

    descriptor = DsmpAllocatePool(NonPagedPoolNx, length, DSM_TAG_DEVICE_ID_LIST);

    if(!descriptor) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetDeviceIdList (DevObj %p): Couldn't allocate descriptor of %ld.\n",
                    DeviceObject,
                    length));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpGetDeviceIdList;
    }

    //
    // setup the query again.
    //
    query.PropertyId = StorageDeviceIdProperty;
    query.QueryType = PropertyStandardQuery;
    query.AdditionalParameters[0] = 0;

    //
    // copy the input to the new outputbuffer
    //
    RtlCopyMemory(descriptor,
                  &query,
                  sizeof(STORAGE_PROPERTY_QUERY));

    DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY,
                                      DeviceObject,
                                      descriptor,
                                      descriptor,
                                      sizeof(STORAGE_PROPERTY_QUERY),
                                      length,
                                      0,
                                      ioStatus);

    status = ioStatus->Status;

    if(!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpGetDeviceIdList (DevObj %p): Query Failed (%x) on attempt 2.\n",
                    DeviceObject,
                    ioStatus->Status));

        goto __Exit_DsmpGetDeviceIdList;
    }

__Exit_DsmpGetDeviceIdList:

    if (ioStatus) {
        DsmpFreePool(ioStatus);
    }

    if (!NT_SUCCESS(status)) {

        if (descriptor) {
            DsmpFreePool(descriptor);
        }

    } else {
        *Descriptor = descriptor;
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetDeviceIdList (DevObj %p): Exiting function with status %x.\n",
                DeviceObject,
                status));

    return status;
}


NTSTATUS
DsmpSetTargetPortGroups(
    _In_ IN PDEVICE_OBJECT DeviceObject,
    _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo,
    _In_ IN ULONG TargetPortGroupsInfoLength
    )
/*++

Routine Description:

    Helper routine to send down SetTargetPortGroups request.

Arguments:

    DeviceObject - The port PDO to which the command should be sent.
    TargetPortGroupsInfo - buffer containing the TPG data.
    TargetPortGroupsInfoLength - size of the TPG buffer.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER passThrough;
    PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb;
    IO_STATUS_BLOCK ioStatus;
    ULONG alignmentMask = DeviceObject->AlignmentRequirement;
    PUCHAR dataBuffer = NULL;
    SIZE_T allocatedLength = 0;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpSetTargetPortGroups (DevObj %p): Entering function.\n",
                DeviceObject));

    NT_ASSERT(TargetPortGroupsInfoLength && TargetPortGroupsInfo);

    //
    // Build request.
    //
    RtlZeroMemory(&passThrough, sizeof(passThrough));

    dataBuffer = DsmpAllocateAlignedPool(NonPagedPoolNx,
                                         TargetPortGroupsInfoLength,
                                         alignmentMask,
                                         DSM_TAG_PASS_THRU,
                                         &allocatedLength);
    if (!dataBuffer) {

        status = STATUS_INSUFFICIENT_RESOURCES;

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpSetTargetPortGroups (DevObj %p): Failed to allocate mem for passthrough's databuffer.\n",
                    DeviceObject));
        goto __Exit_DsmpSetTargetPortGroups;
    }

__Retry_Request:

    //
    // Build the cdb.
    //
    cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)passThrough.ScsiPassThroughDirect.Cdb;

    cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS;
    cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS;
    Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength);

    passThrough.ScsiPassThroughDirect.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    passThrough.ScsiPassThroughDirect.CdbLength = 12;
    passThrough.ScsiPassThroughDirect.SenseInfoLength = SPTWB_SENSE_LENGTH;
    passThrough.ScsiPassThroughDirect.DataIn = 0;
    passThrough.ScsiPassThroughDirect.DataTransferLength = TargetPortGroupsInfoLength;
    passThrough.ScsiPassThroughDirect.TimeOutValue = 20;
    passThrough.ScsiPassThroughDirect.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseInfoBuffer);
    passThrough.ScsiPassThroughDirect.DataBuffer = dataBuffer;
    RtlCopyMemory(dataBuffer,
                  TargetPortGroupsInfo,
                  TargetPortGroupsInfoLength);

    DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT,
                                      DeviceObject,
                                      &passThrough,
                                      &passThrough,
                                      sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
                                      sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
                                      FALSE,
                                      &ioStatus);

    if ((passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_GOOD) &&
        (NT_SUCCESS(ioStatus.Status))) {

        status = STATUS_SUCCESS;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpSetTargetPortGroups (DevObj %p): STPG succeeded.\n",
                    DeviceObject));

    } else if (NT_SUCCESS(ioStatus.Status) &&
               passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_CHECK_CONDITION &&
               DsmpShouldRetryTPGRequest((PSENSE_DATA)&passThrough.SenseInfoBuffer, passThrough.ScsiPassThroughDirect.SenseInfoLength)) {

        //
        // Retry the request
        //
        RtlZeroMemory(dataBuffer, TargetPortGroupsInfoLength);
        goto __Retry_Request;

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpSetTargetPortGroups (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n",
                    DeviceObject,
                    ioStatus.Status,
                    passThrough.ScsiPassThroughDirect.ScsiStatus));

        status = ioStatus.Status;
    }

__Exit_DsmpSetTargetPortGroups:

    //
    // Free the passthrough + data buffer.
    //
    if (dataBuffer) {
        DsmpFreePool(dataBuffer);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpSetTargetPortGroups (DevObj %p): Exiting function with status %x.\n",
                DeviceObject,
                status));

    return status;
}


NTSTATUS
DsmpSetTargetPortGroupsAsync(
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine,
    _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext,
    _In_ IN ULONG TargetPortGroupsInfoLength,
    _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo
    )
/*++

Routine Description:

    Helper routine to send down SetTargetPortGroups request asynchronously.

    IMPORTANT: Caller needs to free the IRP and allocated system buffer.

Arguments:

    DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to.
    CompletionRoutine - completion routine provided by the caller.
    CompletionContext - context passed into the completion routine.
    TargetPortGroupsInfoLength - size of the TPG buffer.
    TargetPortGroupsInfo - buffer containing the TPG data.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext;
    PSCSI_REQUEST_BLOCK srb;
    PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb;
    NTSTATUS status;
    PIRP irp = NULL;
    PIO_STACK_LOCATION irpStack;
    PMDL mdl = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_RW,
                "DsmpSetTargetPortGroupsAsync (DevInfo %p): Entering function.\n",
                DeviceInfo));

    srb = tpgCompletionContext->Srb;

    SrbZeroSrb(srb);

    //
    // Allocate an irp.
    //
    irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE);
    if (!irp) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_RW,
                    "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n",
                    DeviceInfo));
        goto __Exit_DsmpSetTargetPortGroupsAsync;
    }

    mdl = IoAllocateMdl(TargetPortGroupsInfo,
                        TargetPortGroupsInfoLength,
                        FALSE,
                        FALSE,
                        irp);
    if (!mdl) {

        status = STATUS_INSUFFICIENT_RESOURCES;
        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_RW,
                    "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n",
                    DeviceInfo));
        goto __Exit_DsmpSetTargetPortGroupsAsync;
    }

    MmBuildMdlForNonPagedPool(irp->MdlAddress);

    //
    // It is possible that an implicit state transition may have occurred which
    // will cause every I_T nexus to return an UA (for asymmetric access state
    // changed). So set the number of retries to number of paths (worst case of
    // every path being a separate I_T nexus) plus one for a retry down one of
    // paths.
    //
    tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1;

    //
    // Set-up the completion routine.
    //
    IoSetCompletionRoutine(irp,
                           CompletionRoutine,
                           (PVOID)CompletionContext,
                           TRUE,
                           TRUE,
                           TRUE);

    //
    // Get the recipient's irpstack location.
    //
    irpStack = IoGetNextIrpStackLocation(irp);

    irpStack->Parameters.Scsi.Srb = srb;
    irpStack->DeviceObject = DeviceInfo->TargetObject;

    //
    // Set the major function code to IRP_MJ_SCSI.
    //
    irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    //
    // Set the minor function, or many requests will get kicked by by port.
    //
    irpStack->MinorFunction = IRP_MN_SCSI_CLASS;

    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
    srb->Length = sizeof(SCSI_REQUEST_BLOCK);

    SrbSetCdbLength(srb, sizeof(SPC3_CDB_SET_TARGET_PORT_GROUPS));
    cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)SrbGetCdb(srb);
    cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS;
    cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS;
    Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength);

    SrbSetTimeOutValue(srb, SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT);
    SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer);
    SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength);
    SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength);
    SrbSetDataBuffer(srb, TargetPortGroupsInfo);
    srb->SrbStatus = 0;
    SrbSetScsiStatus(srb, 0);
    SrbSetNextSrb(srb, NULL);
    SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE |
                     SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
                     SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE);
    SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
    SrbSetOriginalRequest(srb, irp);

    irp->UserBuffer = TargetPortGroupsInfo;
    irp->Tail.Overlay.Thread = PsGetCurrentThread();

    //
    // Send the IRP asynchronously
    //
    DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext,
                     DeviceInfo->TargetObject,
                     irp,
                     DeviceInfo,
                     DSM_CALL_COMPLETION_ON_MPIO_ERROR);

    //
    // We know that the completion routine will always be called.
    //
    status = STATUS_PENDING;


__Exit_DsmpSetTargetPortGroupsAsync:

    if (status != STATUS_PENDING) {

        //
        // This indicates Irp was never sent down to stack (completion routine was never called).
        // We need to clean up.
        //
        if (irp) {

            if (irp->MdlAddress) {
                IoFreeMdl(irp->MdlAddress);
            }

            IoFreeIrp(irp);
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_RW,
                "DsmpSetTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x.\n",
                DeviceInfo,
                status));

    return status;
}


PDSM_LOAD_BALANCE_POLICY_SETTINGS
DsmpCopyLoadBalancePolicies(
    _In_ IN PDSM_GROUP_ENTRY GroupEntry,
    _In_ IN ULONG DsmWmiVersion,
    _In_ IN PVOID SupportedLBPolicies
    )
/*+++

Routine Description:

    This routine copies the LB Policies that needs to be persisted in registry.
    This is done because registry routines can be called at PASSIVE IRQL only.
    So a spinlock cannot be held while accessing registry. So hold a spinlock,
    save the values in a temp buffer, release spinlock, and save data to registry
    from the temp buffer.

    NOTE: This routine MUST be called with DSM_CONTEXT lock held.

Arguements:

    GroupEntry - Group entry
    DsmWmiVersion - version of the MPIO_DSM_Path class to use
    SupportedLBPolicies - LB policy for the group

 Return Value:

    Pointer to  LOAD_BALANCE_POLICY_SETTINGS if successful. Else, NULL
--*/
{
    PDSM_LOAD_BALANCE_POLICY_SETTINGS lbSettings = NULL;
    ULONG sizeNeeded;
    ULONG inx;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_WMI,
                "DsmpCopyLoadBalancePolicies (Group %p): Entering function.\n",
                GroupEntry));

    if (((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount == 0) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_WMI,
                    "DsmpCopyLoadBalancePolicies (Group %p): No paths specified in Set LB policies.\n",
                    GroupEntry));

        goto __Exit_DsmpCopyLoadBalancePolicies;
    }

    sizeNeeded = sizeof(DSM_LOAD_BALANCE_POLICY_SETTINGS) +
                 ((((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount - 1) * sizeof(MPIO_DSM_Path_V2));;

    lbSettings = DsmpAllocatePool(NonPagedPoolNx,
                                  sizeNeeded,
                                  DSM_TAG_LB_POLICY);

    if (!lbSettings) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_WMI,
                    "DsmpCopyLoadBalancePolicies (Group %p): Failed to allocate memory for LBSettings.\n",
                    GroupEntry));
        goto __Exit_DsmpCopyLoadBalancePolicies;
    }

    //
    // Copy the registry key name used to store the LB policies.
    //
    RtlStringCchCopyNW(lbSettings->RegistryKeyName,
                       sizeof(lbSettings->RegistryKeyName) / sizeof(lbSettings->RegistryKeyName[0]),
                       GroupEntry->RegistryKeyName,
                       ((sizeof(lbSettings->RegistryKeyName) - sizeof(WCHAR))/sizeof(WCHAR)));

    //
    // Copy the Load Balance settings for this group
    //
    lbSettings->LoadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->LoadBalancePolicy;

    lbSettings->PathCount = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount;

    for (inx = 0; inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; inx++) {

        if (DsmWmiVersion == DSM_WMI_VERSION_1) {

            RtlCopyMemory(&(lbSettings->DsmPath[inx]),
                          &(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]),
                          sizeof(MPIO_DSM_Path));

            //
            // DSM_WMI_VERSION_1 supports only active and standby states
            //
            (lbSettings->DsmPath[inx]).OptimizedPath = TRUE;

        } else {

            RtlCopyMemory(&(lbSettings->DsmPath[inx]),
                          &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]),
                          sizeof(MPIO_DSM_Path_V2));
        }
    }

__Exit_DsmpCopyLoadBalancePolicies:

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_WMI,
                "DsmpCopyLoadBalancePolicies (Group %p): Exiting function with lbSettings %p.\n",
                GroupEntry,
                lbSettings));

    return lbSettings;
}


NTSTATUS
DsmpPersistLBSettings(
    _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings
    )
/*+++

Routine Description:

    This routine will save the Load Balance settings from LoadBalanceSettings
    to registry.

    NOTE: This routine MUST be called at PASSIVE IRQL

    The format of the registry tree is :

      Services\MSDSM\LoadBalanceSettings ->

               DeviceName -> LoadBalancePolicy REG_DWORD  <LB Value>

                  DsmPath#Suffix -> PrimaryPath     REG_DWORD <Value>
                                    OptimizedPath   REG_DWORD <Value>
                                    PathWeight      REG_DWORD <Value>

      The device name is the one built in DsmpBuildDeviceName

      The Suffix in DsmPath#Suffix is built from the PathId. It is built in
      the routine DsmpGetDSMPathKeyName.

Arguements:

     LoadBalanceSettings - Load Balance settings to be persisted in registry

Return Value:

    STATUS_SUCCESS if the data could be successfully stored in the registry
    Appropriate NT Status code on failure.
--*/
{
    PMPIO_DSM_Path_V2 dsmPath;
    HANDLE lbSettingsKey = NULL;
    HANDLE deviceKey = NULL;
    HANDLE dsmPathKey = NULL;
    UNICODE_STRING subKeyName;
    WCHAR dsmPathName[128];
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    ULONG inx;
    PMPIO_DSM_Path_V2 preferredPath = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_WMI,
                "DsmpPersistLBSettings (DevName %ws): Entering function.\n",
                LoadBalanceSettings->RegistryKeyName));

    //
    // First open LoadBalanceSettings key under the Service key
    //
    status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey);
    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_WMI,
                    "DsmpPersistLBSettings (DevName %ws): Failed to open LB Settings key. Status %x.\n",
                    LoadBalanceSettings->RegistryKeyName,
                    status));

        goto __Exit_DsmpPersistLBSettings;
    }

    //
    // Now open the key under which the LB settings for the given device is stored
    //
    RtlInitUnicodeString(&subKeyName, LoadBalanceSettings->RegistryKeyName);

    InitializeObjectAttributes(&objectAttributes,
                               &subKeyName,
                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                               lbSettingsKey,
                               (PSECURITY_DESCRIPTOR) NULL);

    status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes);

    if (NT_SUCCESS(status)) {

        //
        // Remove all LB policy information as we are going to rewrite it.
        // We do this in case there is stale information about a path that
        // no longer exists
        //
        status = DsmpRegDeleteTree(deviceKey);

        if (NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_INFORMATION,
                        TRACE_FLAG_WMI,
                        "DsmpPersistLBSettings (DevName %ws): Deleted key along with its subkeys.\n",
                        LoadBalanceSettings->RegistryKeyName));
        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_WMI,
                        "DsmpPersistLBSettings (DevName %ws): Failed to delete key. Status %x\n",
                        LoadBalanceSettings->RegistryKeyName,
                        status));

        }

        ZwClose(deviceKey);
        deviceKey = NULL;

    }

    status = ZwCreateKey(&deviceKey,
                         KEY_ALL_ACCESS,
                         &objectAttributes,
                         0,
                         NULL,
                         REG_OPTION_NON_VOLATILE,
                         NULL);

    if (NT_SUCCESS(status)) {

        PDSM_DEVICE_INFO devInfo;

        for (inx = 0; inx < LoadBalanceSettings->PathCount; inx++) {

            dsmPath = &(LoadBalanceSettings->DsmPath[inx]);

            if (dsmPath->DsmPathId == 0) {

                continue;
            }

            RtlZeroMemory(dsmPathName, sizeof(dsmPathName));

            //
            // Get the sub key name under which the LB settings for
            // the given path is stored.
            //
            DsmpGetDSMPathKeyName(dsmPath->DsmPathId, dsmPathName, 128);

            TracePrint((TRACE_LEVEL_INFORMATION,
                        TRACE_FLAG_WMI,
                        "DsmpPersistLBSettings (DevName %ws): Will open subkey %ws.\n",
                        LoadBalanceSettings->RegistryKeyName,
                        dsmPathName));

            RtlInitUnicodeString(&subKeyName, dsmPathName);

            RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

            InitializeObjectAttributes(&objectAttributes,
                                       &subKeyName,
                                       (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                       deviceKey,
                                       (PSECURITY_DESCRIPTOR) NULL);

            status = ZwCreateKey(&dsmPathKey,
                                 KEY_ALL_ACCESS,
                                 &objectAttributes,
                                 0,
                                 NULL,
                                 REG_OPTION_NON_VOLATILE,
                                 NULL);

            if (NT_SUCCESS(status)) {

                if (dsmPath->PreferredPath) {

                    preferredPath = dsmPath;
                }

                devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved;

                //
                // Save PrimaryPath, PathWeight and OptimizedPath values for this path
                //
                if (devInfo->DesiredState != DSM_DEV_UNDETERMINED) {

                    status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                   dsmPathKey,
                                                   DSM_PRIMARY_PATH,
                                                   REG_DWORD,
                                                   &(dsmPath->PrimaryPath),
                                                   sizeof(ULONG));

                    if (NT_SUCCESS(status)) {

                        status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                       dsmPathKey,
                                                       DSM_OPTIMIZED_PATH,
                                                       REG_DWORD,
                                                       &(dsmPath->OptimizedPath),
                                                       sizeof(ULONG));

                        if (!NT_SUCCESS(status)) {

                            TracePrint((TRACE_LEVEL_ERROR,
                                        TRACE_FLAG_WMI,
                                        "DsmpPersistLBSettings (DevName %ws): Failed to save OptimizedPath. Status %x.\n",
                                        LoadBalanceSettings->RegistryKeyName,
                                        status));
                        }
                    } else {

                        TracePrint((TRACE_LEVEL_ERROR,
                                    TRACE_FLAG_WMI,
                                    "DsmpPersistLBSettings (DevName %ws): Failed to save Primary Path. Status %x.\n",
                                    LoadBalanceSettings->RegistryKeyName,
                                    status));
                    }
                }

                if (NT_SUCCESS(status)) {

                    if (LoadBalanceSettings->LoadBalancePolicy == DSM_LB_WEIGHTED_PATHS) {

                        status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                       dsmPathKey,
                                                       DSM_PATH_WEIGHT,
                                                       REG_DWORD,
                                                       &(dsmPath->PathWeight),
                                                       sizeof(ULONG));

                        if (!NT_SUCCESS(status)) {

                            TracePrint((TRACE_LEVEL_ERROR,
                                        TRACE_FLAG_WMI,
                                        "DsmpPersistLBSettings (DevName %ws): Failed to save PathWeight. Status %x.\n",
                                        LoadBalanceSettings->RegistryKeyName,
                                        status));
                        }
                    }
                }

                ZwClose(dsmPathKey);
                dsmPathKey = NULL;
            } else {
                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_WMI,
                            "DsmpPersistLBSettings (DevName %ws): Failed to open DSM Path key. Status %x.\n",
                            LoadBalanceSettings->RegistryKeyName,
                            status));
            }

            if (!NT_SUCCESS(status)) {
                break;
            }
        }

        if (NT_SUCCESS(status)) {

            //
            // Save the new Load Balance Policy value,
            //
            status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                           deviceKey,
                                           DSM_LOAD_BALANCE_POLICY,
                                           REG_DWORD,
                                           &(LoadBalanceSettings->LoadBalancePolicy),
                                           sizeof(ULONG));
            if (NT_SUCCESS(status)) {

                UCHAR explicitlySet = TRUE;

                //
                // Write out that the policy has been explicitly set
                //
                status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                               deviceKey,
                                               DSM_POLICY_EXPLICITLY_SET,
                                               REG_BINARY,
                                               &explicitlySet,
                                               sizeof(UCHAR));

                if (NT_SUCCESS(status)) {

                    //
                    // If FailOver-Only policy, set the PreferredPath, if specified
                    //
                    if (preferredPath) {

                        status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                       deviceKey,
                                                       DSM_PREFERRED_PATH,
                                                       REG_BINARY,
                                                       &(preferredPath->DsmPathId),
                                                       sizeof(ULONGLONG));
                    }
                } else {

                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_WMI,
                                "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (ES).\n",
                                LoadBalanceSettings->RegistryKeyName));
                }
            } else {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_WMI,
                            "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (LBP).\n",
                            LoadBalanceSettings->RegistryKeyName));
            }
        }
    }

__Exit_DsmpPersistLBSettings:

    if (dsmPathKey) {
        ZwClose(dsmPathKey);
    }

    if (deviceKey) {
        ZwClose(deviceKey);
    }

    if (lbSettingsKey) {
        ZwClose(lbSettingsKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_WMI,
                "DsmpPersistLBSettings (DevName %ws): Exiting function with status %x.\n",
                LoadBalanceSettings->RegistryKeyName,
                status));

    return status;
}


NTSTATUS
DsmpSetDeviceALUAState(
    _In_ IN PDSM_CONTEXT DsmContext,
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_ IN DSM_DEVICE_STATE DevState
    )
/*++

Routine Description:

    Helper routine to build the STPG info and send it down to modify the passed in
    devInfo's state.

Arguments:

    DsmContext - DSM context.
    DeviceInfo - DevInfo whose state needs to be changed.
    DevState - New state to be set.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PUCHAR targetPortGroupsInfo = NULL;
    ULONG targetPortGroupsInfoLength;
    PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL;
    NTSTATUS status;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpSetDeviceALUAState (DevInfo %p): Entering function.\n",
                DeviceInfo));

    //
    // Send down SetTPG to set the appropriate access state
    // (The TPG block will contain the header and a SetTPG descriptor).
    //
    targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE +
                                 sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR);

    targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx,
                                            targetPortGroupsInfoLength,
                                            DSM_TAG_TARGET_PORT_GROUPS);

    if (targetPortGroupsInfo) {

        tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE);
        tpgDescriptor->AsymmetricAccessState = DevState;
        REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &DeviceInfo->TargetPortGroup->Identifier);

        status = DsmpSetTargetPortGroups(DeviceInfo->TargetObject,
                                         targetPortGroupsInfo,
                                         targetPortGroupsInfoLength);

        if (NT_SUCCESS(status)) {

            //
            // An explicit transition may cause changes to some other TPGs.
            // So we need to query for the states of all the TPGs and update
            // our internal list and its elements.
            //
            status = DsmpGetDeviceALUAState(DsmContext,
                                            DeviceInfo,
                                            NULL);

        } else {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_GENERAL,
                        "DsmpSetDeviceALUAState (DevInfo %p): Failed to SetTPG with %x.\n",
                        DeviceInfo,
                        status));
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpSetDeviceALUAState (DevInfo %p): Failed to allocate TPG.\n",
                    DeviceInfo));
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (targetPortGroupsInfo) {
        DsmpFreePool(targetPortGroupsInfo);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpSetDeviceALUAState (DevInfo %p): Exiting function with status %x\n",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpAdjustDeviceStatesALUA(
    _In_ IN PDSM_GROUP_ENTRY Group,
    _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo,
    _In_ IN ULONG SpecialHandlingFlag
    )
/*++

Routine Description:

    Helper routine to build the adjust every device state in the group taking
    the following into consideration:
    1. PreferredActiveDeviceInfo
    2. DeviceInfo's TPG state
    3. Preferred Path
    4. LB Policy

Arguments:

    Group - Pseudo-LUN whose path states need to be adjusted.
    PreferredActiveDeviceInfo - DevInfo whose state needs to preferrably made
                                A/O, if possible. This parameter is optional.

    SpecialHandlingFlag - Flags to indicate any special handling requirement

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    ULONG index;
    PDSM_DEVICE_INFO deviceInfo;
    PDSM_DEVICE_INFO activeDevice = NULL;
    DSM_DEVICE_STATE devState;
    NTSTATUS status = STATUS_SUCCESS;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAdjustDeviceStatesALUA (Group %p): Entering function with preferred active devInfo %p.\n",
                Group,
                PreferredActiveDeviceInfo));

    //
    // Ensure that:
    // 1. All devices match their ALUA state.
    // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it.
    // 3. For FOO there must be only one A/O device. Preferably the preferred path.
    //
    for (index = 0; index < DSM_MAX_PATHS; index++) {

        deviceInfo = Group->DeviceList[index];

        if (deviceInfo) {

            devState = deviceInfo->State;

            if (!DsmpIsDeviceFailedState(deviceInfo->State) &&
                DsmpIsDeviceInitialized(deviceInfo) &&
                DsmpIsDeviceUsable(deviceInfo) &&
                DsmpIsDeviceUsablePR(deviceInfo)) {


                deviceInfo->PreviousState = deviceInfo->State;
                deviceInfo->State = deviceInfo->ALUAState;

                TracePrint((TRACE_LEVEL_INFORMATION,
                            TRACE_FLAG_GENERAL,
                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (its ALUA state).\n",
                            Group,
                            deviceInfo,
                            deviceInfo->PreviousState,
                            deviceInfo->State));

                if (deviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) {

                    //
                    // In FOO and RRWS, we need to mask states.
                    //
                    switch (Group->LoadBalanceType) {
                        case DSM_LB_FAILOVER: {

                            //
                            // Cache the first available devInfo that is in A/O
                            //
                            if (!activeDevice) {

                                activeDevice = deviceInfo;

                                TracePrint((TRACE_LEVEL_INFORMATION,
                                            TRACE_FLAG_GENERAL,
                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p choosen as the active device.\n",
                                            Group,
                                            activeDevice));

                                break;
                            }

                            //
                            // Check if this deviceInfo is the preferred path. If yes,
                            // mask the active device's state and make this the new
                            // active device.
                            //
                            if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)deviceInfo->FailGroup->PathId)) {

                                activeDevice->PreviousState = activeDevice->State;
                                activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED ||
                                                       activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState;

                                TracePrint((TRACE_LEVEL_INFORMATION,
                                            TRACE_FLAG_GENERAL,
                                            "DsmpAdjustDeviceStatesALUA (Group %p): Previous active devInfo %p transitioning from %u to %u.\n",
                                            Group,
                                            activeDevice,
                                            activeDevice->PreviousState,
                                            activeDevice->State));

                                activeDevice = deviceInfo;

                                TracePrint((TRACE_LEVEL_INFORMATION,
                                            TRACE_FLAG_GENERAL,
                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (preferred path).\n",
                                            Group,
                                            activeDevice));

                                break;
                            }

                            //
                            // If active device's desired state is not A/O but this
                            // deviceInfo's is, then mask the active device's state
                            // and make this one the new active device.
                            //
                            if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                activeDevice->DesiredState != DSM_DEV_UNDETERMINED) {

                                //
                                // The exception though is if the current active device
                                // is the preferred path
                                //
                                if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) {

                                    deviceInfo->PreviousState = deviceInfo->State;
                                    deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED ||
                                                         deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState;

                                    TracePrint((TRACE_LEVEL_INFORMATION,
                                                TRACE_FLAG_GENERAL,
                                                "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active DI is PrefPath).\n",
                                                Group,
                                                deviceInfo,
                                                deviceInfo->PreviousState,
                                                deviceInfo->State));
                                } else {

                                    //
                                    // If this is the devInfo that is preferred to be A/O, make it such
                                    //
                                    if (PreferredActiveDeviceInfo &&
                                        PreferredActiveDeviceInfo == deviceInfo) {

                                        activeDevice->PreviousState = activeDevice->State;
                                        activeDevice->State = activeDevice->DesiredState;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (found a preferred active DI).\n",
                                                    Group,
                                                    activeDevice,
                                                    activeDevice->PreviousState,
                                                    activeDevice->State));

                                        activeDevice = deviceInfo;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active DI (preferred).\n",
                                                    Group,
                                                    activeDevice));
                                    } else {

                                        //
                                        // Check if this devInfo desires to be in A/O, since the currently
                                        // active one doesn't want to be.
                                        //
                                        if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                            deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) {

                                            //
                                            // This deviceInfo's desire is also not to be in A/O,
                                            // so just leave the current one active.
                                            //
                                            if (devState == DSM_DEV_ACTIVE_OPTIMIZED) {

                                                //
                                                // Exception is if we're processing the device whose state before
                                                // RTPG was sent was already A/O, it is best to leave this device
                                                // in A/O state.
                                                //

                                                activeDevice->PreviousState = activeDevice->State;
                                                activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED ||
                                                                       activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Found a DI that was previously active.\n",
                                                            Group,
                                                            activeDevice,
                                                            activeDevice->PreviousState,
                                                            activeDevice->State));

                                                activeDevice = deviceInfo;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (previously A/O).\n",
                                                            Group,
                                                            activeDevice));
                                            } else {

                                                //
                                                // This device wasn't in A/O state before, so just leave
                                                // the currently selected active device as is.
                                                //
                                                deviceInfo->PreviousState = deviceInfo->State;
                                                deviceInfo->State = deviceInfo->DesiredState;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active device already exists).\n",
                                                            Group,
                                                            deviceInfo,
                                                            deviceInfo->PreviousState,
                                                            deviceInfo->State));
                                            }
                                        } else {

                                            //
                                            // Current devInfo wants (or doesn't) mind being in
                                            // A/O, whereas the current active device doesn't, so
                                            // mask the active device and make this devInfo the
                                            // active device.
                                            //
                                            activeDevice->PreviousState = activeDevice->State;
                                            activeDevice->State = activeDevice->DesiredState;

                                            TracePrint((TRACE_LEVEL_INFORMATION,
                                                        TRACE_FLAG_GENERAL,
                                                        "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Current DI prefers being A/O.\n",
                                                        Group,
                                                        activeDevice,
                                                        activeDevice->PreviousState,
                                                        activeDevice->State));

                                            activeDevice = deviceInfo;

                                            TracePrint((TRACE_LEVEL_INFORMATION,
                                                        TRACE_FLAG_GENERAL,
                                                        "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (desired state).\n",
                                                        Group,
                                                        activeDevice));
                                        }
                                    }
                                }
                            } else {

                                //
                                // The single overriding factor is always the preferred path.
                                // Everything else is secondary, so first check if the currently
                                // active device can even be overridden by another one.
                                //
                                if (Group->PreferredPath != (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) {

                                    //
                                    // It can't be overridden, so we're done.
                                    //
                                    deviceInfo->PreviousState = deviceInfo->State;
                                    deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED ||
                                                         deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState;

                                    TracePrint((TRACE_LEVEL_INFORMATION,
                                                TRACE_FLAG_GENERAL,
                                                "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (current active DI is PrefPath).\n",
                                                Group,
                                                deviceInfo,
                                                deviceInfo->PreviousState,
                                                deviceInfo->State));
                                } else {

                                    //
                                    // Active device's desired state is A/O but it isn't the preferred
                                    // path. Check if this devInfo is preferred as A/O.
                                    //
                                    if (PreferredActiveDeviceInfo &&
                                        PreferredActiveDeviceInfo == deviceInfo) {

                                        activeDevice->PreviousState = activeDevice->State;
                                        activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED ||
                                                               activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. New DI is preferred active.\n",
                                                    Group,
                                                    activeDevice,
                                                    activeDevice->PreviousState,
                                                    activeDevice->State));

                                        activeDevice = deviceInfo;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (this DI is preferred active).\n",
                                                    Group,
                                                    activeDevice));
                                    } else {

                                        //
                                        // Active device's desired state is A/O but it isn't the
                                        // preferred path. Check if this devInfo's desired state
                                        // is also A/O. If yes, we'll need to make certain decisions.
                                        //
                                        if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                            deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) {

                                            //
                                            // Since this device doesn't desire to be in
                                            // A/O and we already have an active device, just
                                            // mask its state.
                                            //
                                            deviceInfo->PreviousState = deviceInfo->State;
                                            deviceInfo->State = deviceInfo->DesiredState;

                                            TracePrint((TRACE_LEVEL_INFORMATION,
                                                        TRACE_FLAG_GENERAL,
                                                        "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (prefers being in non-A/O).\n",
                                                        Group,
                                                        deviceInfo,
                                                        deviceInfo->PreviousState,
                                                        deviceInfo->State));
                                        } else {

                                            //
                                            // Active device is in A/O and this device desires to be in
                                            // A/O too. Make this the new active device only if its state
                                            // before the RTPG was already A/O.
                                            //
                                            if (devState == DSM_DEV_ACTIVE_OPTIMIZED) {

                                                activeDevice->PreviousState = activeDevice->State;
                                                activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED ||
                                                                       activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI was already in A/O previously).\n",
                                                            Group,
                                                            activeDevice,
                                                            activeDevice->PreviousState,
                                                            activeDevice->State));

                                                activeDevice = deviceInfo;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is new active device (since it was in A/O previously too).\n",
                                                            Group,
                                                            activeDevice));
                                            } else {

                                                //
                                                // Just leave the currently active one alone.
                                                //
                                                deviceInfo->PreviousState = deviceInfo->State;
                                                deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED ||
                                                                     deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState;

                                                TracePrint((TRACE_LEVEL_INFORMATION,
                                                            TRACE_FLAG_GENERAL,
                                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (leave current active DI alone).\n",
                                                            Group,
                                                            deviceInfo,
                                                            deviceInfo->PreviousState,
                                                            deviceInfo->State));
                                            }
                                        }
                                    }
                                }
                            }
                            break;
                        }

                        case DSM_LB_ROUND_ROBIN_WITH_SUBSET: {

                            //
                            // At least one path needs to be in A/O state, so
                            // cache the first available devInfo that is in A/O
                            //
                            if (!activeDevice) {

                                activeDevice = deviceInfo;

                                TracePrint((TRACE_LEVEL_INFORMATION,
                                            TRACE_FLAG_GENERAL,
                                            "DsmpAdjustDeviceStatesALUA (Group %p): We have atleast one A/O DevInfo %p.\n",
                                            Group,
                                            activeDevice));

                                break;
                            }

                            //
                            // Check if this device is preferred to be in A/O
                            //
                            if (PreferredActiveDeviceInfo &&
                                PreferredActiveDeviceInfo == deviceInfo) {

                                //
                                // If the currently active device, doesn't desire to be in
                                // A/O state, mask its state.
                                //
                                if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                    activeDevice->DesiredState != DSM_DEV_UNDETERMINED) {

                                    activeDevice->PreviousState = activeDevice->State;
                                    activeDevice->State = activeDevice->DesiredState;

                                    TracePrint((TRACE_LEVEL_INFORMATION,
                                                TRACE_FLAG_GENERAL,
                                                "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desired state non-A/O).\n",
                                                Group,
                                                activeDevice,
                                                activeDevice->PreviousState,
                                                activeDevice->State));
                                }

                                activeDevice = deviceInfo;

                                TracePrint((TRACE_LEVEL_INFORMATION,
                                            TRACE_FLAG_GENERAL,
                                            "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (preferred active DI).\n",
                                            Group,
                                            activeDevice));
                            } else {

                                //
                                // If this device's desired state is specified and not A/O,
                                // mask its path state.
                                //
                                if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                    deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) {

                                    deviceInfo->PreviousState = deviceInfo->State;
                                    deviceInfo->State = deviceInfo->DesiredState;

                                    TracePrint((TRACE_LEVEL_INFORMATION,
                                                TRACE_FLAG_GENERAL,
                                                "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desires to be in non-A/O).\n",
                                                Group,
                                                deviceInfo,
                                                deviceInfo->PreviousState,
                                                deviceInfo->State));
                                } else {

                                    //
                                    // Since this devInfo desires to be in A/O, we are assured
                                    // of at least one path in A/O. So check to see if the
                                    // currently active device doesn't desire to be in A/O.
                                    //
                                    if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED &&
                                        activeDevice->DesiredState != DSM_DEV_UNDETERMINED) {

                                        activeDevice->PreviousState = activeDevice->State;
                                        activeDevice->State = activeDevice->DesiredState;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI desires to be in A/O).\n",
                                                    Group,
                                                    activeDevice,
                                                    activeDevice->PreviousState,
                                                    activeDevice->State));

                                        activeDevice = deviceInfo;

                                        TracePrint((TRACE_LEVEL_INFORMATION,
                                                    TRACE_FLAG_GENERAL,
                                                    "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active DI (desires to be in A/O).\n",
                                                    Group,
                                                    activeDevice));
                                    }
                                }
                            }

                            break;
                        }

                        default: {

                            //
                            // For RR, LQD and WP, paths must be in the same
                            // state as their corresponding TPG. Preferably
                            // all should be A/O.
                            //
                            if (deviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) {
                                DSM_ASSERT(deviceInfo->State == deviceInfo->ALUAState);
                            }

                            break;
                        }
                    }
                }
            }
        }
    }

    //
    // There may have been a change to the device states.
    // DsmpGetPath() will pick these changes for RR, RRWS and LQD.
    // However, it won't for FOO and WP, so update PTBU if needed.
    //
    if (Group->LoadBalanceType == DSM_LB_FAILOVER ||
        Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) {

        deviceInfo = DsmpGetActivePathToBeUsed(Group, FALSE, SpecialHandlingFlag);

        if (deviceInfo) {

            InterlockedExchangePointer(&(Group->PathToBeUsed), deviceInfo->FailGroup);
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpAdjustDeviceStatesALUA (Group %p): Exiting function with status %x\n",
                Group,
                status));

    return status;
}


PDSM_WORKITEM
DsmpAllocateWorkItem(
    _In_ IN PDEVICE_OBJECT DeviceObject,
    _In_ IN PVOID Context
    )
/*++

Routine Description:

    Allocates a work item to handle reservation failover.

Arguments:

    DeviceObject - Target device.
    Context - Workitem context

Return Value:

    Allocated workitem or NULL (if low memory).

--*/
{
    PDSM_WORKITEM dsmWorkItem = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_IOCTL,
                "DsmpAllocateWorkItem (DevObj %p): Entering function.\n",
                DeviceObject));

    dsmWorkItem = DsmpAllocatePool(NonPagedPoolNx,
                                   sizeof(DSM_WORKITEM),
                                   DSM_TAG_WORKITEM);
    if (dsmWorkItem != NULL) {

        dsmWorkItem->WorkItem = IoAllocateWorkItem(DeviceObject);
        if (dsmWorkItem->WorkItem != NULL) {

            dsmWorkItem->Context = Context;
        } else {

            DsmpFreePool(dsmWorkItem);
            dsmWorkItem = NULL;
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_IOCTL,
                "DsmpAllocateWorkItem (DevObj %p): Exiting function. dsmWorkItem %p.\n",
                DeviceObject,
                dsmWorkItem));

    return dsmWorkItem;
}


VOID
DsmpFreeWorkItem(
    _In_ IN PDSM_WORKITEM DsmWorkItem
    )
{
    PVOID temp = DsmWorkItem;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_IOCTL,
                "DsmpFreeWorkItem (WorkItem %p): Entering function.\n",
                DsmWorkItem));

    if (DsmWorkItem != NULL) {

        if (DsmWorkItem->WorkItem != NULL) {
            IoFreeWorkItem(DsmWorkItem->WorkItem);
        }

        DsmpFreePool(DsmWorkItem);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_IOCTL,
                "DsmpFreeWorkItem (WorkItem %p): Exiting function.\n",
                temp));

    return;
}


VOID
DsmpFreeZombieGroupList(
    _In_ IN PDSM_FAILOVER_GROUP FailGroup
    )
{
     PLIST_ENTRY zombieEntry = NULL;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpFreeZombieGroupList (FailGroup %p): Entering function.\n",
                FailGroup));

     while (!IsListEmpty(&FailGroup->ZombieGroupList)) {

         zombieEntry = RemoveHeadList(&FailGroup->ZombieGroupList);

         if (zombieEntry) {

             DsmpFreePool(zombieEntry);
         }
     }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpFreeZombieGroupList (FailGroup %p): Exiting function.\n",
                FailGroup));
}


NTSTATUS
DsmpGetDeviceALUAState(
    _In_ IN PDSM_CONTEXT DsmContext,
    _In_ IN PDSM_DEVICE_INFO DeviceInfo,
    _In_opt_ IN PDSM_DEVICE_STATE DevState
    )
/*++

Routine Description:

    Helper routine to build the RTPG info and send it down to retrieve the
    devInfo's current state.

Arguments:

    DsmContext - DSM context.
    DeviceInfo - DevInfo whose state needs to be changed.
    DevState   - Current state of passed in DeviceInfo.

Return Value:

    STATUS_SUCCESS or appropriate failure code.

--*/
{
    PUCHAR targetPortGroupsInfo = NULL;
    ULONG targetPortGroupsInfoLength = 0;
    PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL;
    KIRQL irql;
    NTSTATUS status;
    ULONG index;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpGetDeviceALUAState (DevInfo %p): Entering function.\n",
                DeviceInfo));

    status = DsmpReportTargetPortGroups(DeviceInfo->TargetObject,
                                        &targetPortGroupsInfo,
                                        &targetPortGroupsInfoLength);


    if (NT_SUCCESS(status) && targetPortGroupsInfo != NULL) {

        irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock));

        status = DsmpParseTargetPortGroupsInformation(DsmContext,
                                                      DeviceInfo->Group,
                                                      targetPortGroupsInfo,
                                                      targetPortGroupsInfoLength);

        for (index = 0; index < DSM_MAX_PATHS; index++) {

            targetPortGroup = DeviceInfo->Group->TargetPortGroupList[index];

            if (targetPortGroup) {

                DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState);
            }
        }

        ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql);

        if (DevState) {

            *DevState = DeviceInfo->State;
        }

    } else {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_GENERAL,
                    "DsmpGetDeviceALUAState (DevInfo %p): ReportTPG failed with %x.\n",
                    DeviceInfo,
                    status));
    }

    if (targetPortGroupsInfo) {

        DsmpFreePool(targetPortGroupsInfo);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_GENERAL,
                "DsmpGetDeviceALUAState (DevInfo %p): Exiting function with status %x\n",
                DeviceInfo,
                status));

    return status;
}


NTSTATUS
DsmpRegCopyTree(
    _In_ IN HANDLE SourceKey,
    _In_ IN HANDLE DestKey
    )
/*++

Routine Description:

    Copies a reg subtree from source key to destination key.
    This routine will first copy over all the key's values, and then
    copy the subkeys, each time recursively handling the subkey's
    values and its subtree.

Arguments:

    SourceKey - Handle to the root of the subtree to copy over.
    DestKey   - Handle to the root of the new tree.

Return Value:

    STATUS_SUCCESS upon successfully coping over the tree.
    Appropriate NT error code in case of failure.

--*/
{
    ULONG numValues = 0;
    ULONG numSubKeys = 0;
    ULONG lengthOfValueName = 0;
    ULONG lengthOfValueData = 0;
    ULONG lengthOfKeyName = 0;
    LPWSTR valueBuf = NULL;
    BYTE *valueDataBuf = NULL;
    ULONG valueDataType;
    ULONG titleIndex;
    HANDLE srcSubKey = NULL;
    HANDLE destSubKey = NULL;
    LPWSTR subKey = NULL;
    NTSTATUS status;
    PKEY_FULL_INFORMATION keyFullInfo = NULL;
    ULONG length = sizeof(KEY_FULL_INFORMATION);
    ULONG index = 0;
    PKEY_VALUE_FULL_INFORMATION keyValueFullInfo = NULL;
    PKEY_BASIC_INFORMATION keyBasicInfo = NULL;
    OBJECT_ATTRIBUTES objectAttributes;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpRegCopyTree (SrcKey %p): Entering function.\n",
                SourceKey));

    if (!SourceKey || !DestKey) {

        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpRegCopyTree;
    }

    //
    // Query the source key for information about number of subkeys, number of values, etc.
    //
    do {
        if (keyFullInfo) {

            DsmpFreePool(keyFullInfo);
        }

        keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED);

        if (!keyFullInfo) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key full info.\n",
                        SourceKey));

            status = STATUS_INSUFFICIENT_RESOURCES;
            goto __Exit_DsmpRegCopyTree;
        }

        status = ZwQueryKey(SourceKey,
                            KeyFullInformation,
                            keyFullInfo,
                            length,
                            &length);

    } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegCopyTree (SrcKey %p): Failed to query key. Status %x.\n",
                    SourceKey,
                    status));

        goto __Exit_DsmpRegCopyTree;
    }

    numSubKeys = keyFullInfo->SubKeys;
    numValues = keyFullInfo->Values;
    lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR);
    lengthOfValueName = keyFullInfo->MaxValueNameLen + sizeof(WCHAR);
    lengthOfValueData = keyFullInfo->MaxValueDataLen + sizeof(WCHAR);

    //
    // Allocate a buffer for the name of the value
    //
    valueBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                                lengthOfValueName,
                                DSM_TAG_REG_KEY_RELATED);
    if (!valueBuf) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value name.\n",
                    SourceKey));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpRegCopyTree;
    }

    //
    // Allocate a buffer for the value data
    //
    valueDataBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                                    lengthOfValueData,
                                    DSM_TAG_REG_KEY_RELATED);

    if (!valueDataBuf) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value's data.\n",
                    SourceKey));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpRegCopyTree;
    }

    //
    // First enumerate all of the values
    //
    status = STATUS_SUCCESS;
    for (index = 0; index < numValues && NT_SUCCESS(status); index++) {

        UNICODE_STRING valueName;

        length = sizeof(KEY_VALUE_FULL_INFORMATION);

        do {

            if (keyValueFullInfo) {

                DsmpFreePool(keyValueFullInfo);
            }

            keyValueFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED);

            if (!keyValueFullInfo) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value full info.\n",
                            SourceKey));

                status = STATUS_INSUFFICIENT_RESOURCES;
                goto __Exit_DsmpRegCopyTree;
            }

            //
            // Get the information of the index'th value
            //
            status = ZwEnumerateValueKey(SourceKey,
                                         index,
                                         KeyValueFullInformation,
                                         keyValueFullInfo,
                                         length,
                                         &length);

        } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegCopyTree (SrcKey %p): Failed to enumerate key's value information. Status %x.\n",
                        SourceKey,
                        status));

            goto __Exit_DsmpRegCopyTree;
        }

        //
        // Capture the data type, data value, and value name.
        //
        titleIndex = keyValueFullInfo->TitleIndex;
        valueDataType = keyValueFullInfo->Type;

        RtlZeroMemory(valueDataBuf, lengthOfValueData);
        RtlCopyMemory(valueDataBuf,
                      (PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset,
                      keyValueFullInfo->DataLength);

        RtlZeroMemory(valueBuf, lengthOfValueName);
        RtlStringCbCopyNW(valueBuf, lengthOfValueName, keyValueFullInfo->Name, keyValueFullInfo->NameLength);
        RtlInitUnicodeString(&valueName, valueBuf);

        //
        // Copy the value over to the new key
        //
        status = ZwSetValueKey(DestKey,
                               &valueName,
                               titleIndex,
                               valueDataType,
                               valueDataBuf,
                               keyValueFullInfo->DataLength);
    }

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegCopyTree (SrcKey %p): Failed to allocate set new key's value information. Status %x.\n",
                    SourceKey,
                    status));

        goto __Exit_DsmpRegCopyTree;
    }

    //
    // Allocate buffer for subkey name
    //
    subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                              lengthOfKeyName,
                              DSM_TAG_REG_KEY_RELATED);

    if(!subKey) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for sub key name.\n",
                    SourceKey));

        status = STATUS_INSUFFICIENT_RESOURCES;
        goto __Exit_DsmpRegCopyTree;
    }

    //
    // Now Enumerate all of the subkeys
    //
    length = sizeof(KEY_BASIC_INFORMATION);
    for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) {

        UNICODE_STRING subKeyName;

        do {
            if (keyBasicInfo) {

                DsmpFreePool(keyBasicInfo);
            }

            keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                                            length,
                                            DSM_TAG_REG_KEY_RELATED);

            if (!keyBasicInfo) {

                TracePrint((TRACE_LEVEL_ERROR,
                            TRACE_FLAG_PNP,
                            "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key basic info.\n",
                            SourceKey));

                status = STATUS_INSUFFICIENT_RESOURCES;
                goto __Exit_DsmpRegCopyTree;
            }

            //
            // Enumerate the index'th subkey
            //
            status = ZwEnumerateKey(SourceKey,
                                    index,
                                    KeyBasicInformation,
                                    keyBasicInfo,
                                    length,
                                    &length);

        } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegCopyTree (SrcKey %p): Failed to enumerate sub key's info. Status %x.\n",
                        SourceKey,
                        status));

            goto __Exit_DsmpRegCopyTree;
        }

        RtlZeroMemory(subKey, lengthOfKeyName);
        RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength);
        RtlInitUnicodeString(&subKeyName, subKey);

        //
        // Open a handle to the the subkey on the old device.
        //
        InitializeObjectAttributes(&objectAttributes,
                                   &subKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   SourceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        if (srcSubKey) {
            ZwClose(srcSubKey);
            srcSubKey = NULL;
        }

        status = ZwOpenKey(&srcSubKey,
                           KEY_ALL_ACCESS,
                           &objectAttributes);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegCopyTree (SrcKey %p): Failed to open reg key %ws. Status %x.\n",
                        SourceKey,
                        subKey,
                        status));

            goto __Exit_DsmpRegCopyTree;
        }

        InitializeObjectAttributes(&objectAttributes,
                                   &subKeyName,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   DestKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        if (destSubKey) {
            ZwClose(destSubKey);
            destSubKey = NULL;
        }

        //
        // Create the subkey on the new device.
        //
        status = ZwCreateKey(&destSubKey,
                             KEY_ALL_ACCESS,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             NULL);

        if (!NT_SUCCESS(status)) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegCopyTree (SrcKey %p): Failed to create reg key %ws. Status %x.\n",
                        SourceKey,
                        subKey,
                        status));

            goto __Exit_DsmpRegCopyTree;
        }

        //
        // That's it. We've got everything we need (ie. handles to the two new
        // subtrees' roots. Call recursively.
        //
        status = DsmpRegCopyTree(srcSubKey, destSubKey);
    }

__Exit_DsmpRegCopyTree:

    if (keyFullInfo) {
        DsmpFreePool(keyFullInfo);
    }

    if (valueBuf) {
        DsmpFreePool(valueBuf);
    }

    if (valueDataBuf) {
        DsmpFreePool(valueDataBuf);
    }

    if (keyValueFullInfo) {
        DsmpFreePool(keyValueFullInfo);
    }

    if (subKey) {
        DsmpFreePool(subKey);
    }

    if (keyBasicInfo) {
        DsmpFreePool(keyBasicInfo);
    }

    if (srcSubKey) {
        ZwClose(srcSubKey);
    }

    if (destSubKey) {
        ZwClose(destSubKey);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpRegCopyTree (SrcKey %p): Exiting function with status %x.\n",
                SourceKey,
                status));

    return status;
}


NTSTATUS
DsmpRegDeleteTree(
    _In_ IN HANDLE KeyRoot
    )
/*++
Routine Description:

    This routine is a recursive worker that enumerates the subkeys
    of a given key, applies itself to each one, then deletes itself.

Arguments:

    KeyRoot - Supplies a handle to the root of subtree to be deleted.

Return Value:

    STATUS_SUCCESS - upon successful deletion of subtree.
    Appropriate NT error code upon failure.

--*/
{
    NTSTATUS status;
    PKEY_FULL_INFORMATION keyFullInfo = NULL;
    ULONG length = sizeof(KEY_FULL_INFORMATION);
    ULONG numSubKeys;
    ULONG lengthOfKeyName;
    LPWSTR subKey = NULL;
    PKEY_BASIC_INFORMATION keyBasicInfo = NULL;
    ULONG index = 0;
    HANDLE srcSubKey = NULL;
    OBJECT_ATTRIBUTES objectAttributes;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpRegDeleteTree (SrcKey %p): Entering function.\n",
                KeyRoot));

    if (!KeyRoot) {

        status = STATUS_INVALID_PARAMETER;
        goto __Exit_DsmpRegDeleteTree;
    }

    //
    // Query the source key for information about number of subkeys and max
    // length needed for subkey name.
    //
    do {
        if (keyFullInfo) {

            DsmpFreePool(keyFullInfo);
        }

        keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED);

        if (!keyFullInfo) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key full info.\n",
                        KeyRoot));

            status = STATUS_INSUFFICIENT_RESOURCES;
            goto __Exit_DsmpRegDeleteTree;
        }

        status = ZwQueryKey(KeyRoot,
                            KeyFullInformation,
                            keyFullInfo,
                            length,
                            &length);

    } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW);

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR,
                    TRACE_FLAG_PNP,
                    "DsmpRegDeleteTree (SrcKey %p): Failed to query key. Status %x.\n",
                    KeyRoot,
                    status));

        goto __Exit_DsmpRegDeleteTree;
    }

    numSubKeys = keyFullInfo->SubKeys;
    lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR);

    if (numSubKeys) {

        //
        // Allocate buffer for subkey name
        //
        subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                                  lengthOfKeyName,
                                  DSM_TAG_REG_KEY_RELATED);

        if(!subKey) {

            TracePrint((TRACE_LEVEL_ERROR,
                        TRACE_FLAG_PNP,
                        "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for sub key.\n",
                        KeyRoot));

            status = STATUS_INSUFFICIENT_RESOURCES;
            goto __Exit_DsmpRegDeleteTree;
        }

        //
        // Now Enumerate all of the subkeys
        //
        index = numSubKeys - 1;
        length = sizeof(KEY_BASIC_INFORMATION);
        do {

            UNICODE_STRING subKeyName;

            do {
                if (keyBasicInfo) {

                    DsmpFreePool(keyBasicInfo);
                }

                keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned,
                                                length,
                                                DSM_TAG_REG_KEY_RELATED);

                if (!keyBasicInfo) {

                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_PNP,
                                "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key basic info.\n",
                                KeyRoot));

                    status = STATUS_INSUFFICIENT_RESOURCES;
                    goto __Exit_DsmpRegDeleteTree;
                }

                //
                // Enumerate the index'th subkey
                //
                status = ZwEnumerateKey(KeyRoot,
                                        index,
                                        KeyBasicInformation,
                                        keyBasicInfo,
                                        length,
                                        &length);

            } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW);

            if (NT_SUCCESS(status)) {

                RtlZeroMemory(subKey, lengthOfKeyName);
                RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength);
                RtlInitUnicodeString(&subKeyName, subKey);

                //
                // Open a handle to the the current root's subkey.
                //
                InitializeObjectAttributes(&objectAttributes,
                                           &subKeyName,
                                           (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                           KeyRoot,
                                           (PSECURITY_DESCRIPTOR) NULL);

                status = ZwOpenKey(&srcSubKey,
                                   KEY_ALL_ACCESS,
                                   &objectAttributes);

                if (!NT_SUCCESS(status)) {

                    TracePrint((TRACE_LEVEL_ERROR,
                                TRACE_FLAG_PNP,
                                "DsmpRegDeleteTree (SrcKey %p): Failed to open key %ws. Status %x.\n",
                                KeyRoot,
                                subKey,
                                status));

                    goto __Exit_DsmpRegDeleteTree;
                }

                //
                // Delete this key's subtree (recursively).
                //
                status = DsmpRegDeleteTree(srcSubKey);

                ZwClose(srcSubKey);
                srcSubKey = NULL;
            }

            index--;

        } while (status != STATUS_NO_MORE_ENTRIES && (LONG)index >= 0);

        if (status == STATUS_NO_MORE_ENTRIES) {

            status = STATUS_SUCCESS;
        }
    }

    ZwDeleteKey(KeyRoot);

__Exit_DsmpRegDeleteTree:

    if (srcSubKey) {
        ZwClose(srcSubKey);
    }

    if (keyFullInfo) {
        DsmpFreePool(keyFullInfo);
    }

    if (subKey) {
        DsmpFreePool(subKey);
    }

    if (keyBasicInfo) {
        DsmpFreePool(keyBasicInfo);
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpRegDeleteTree (SrcKey %p): Exiting function with status %x.\n",
                KeyRoot,
                status));

    return status;
}


#if defined (_WIN64)
VOID
DsmpPassThroughPathTranslate32To64(
    _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32,
    _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64
    )
/*++

Routine Description:

    On WIN64, the SCSI_PASS_THROUGH field of the MPIO_PASS_THROUGH_PATH structure
    sent down by a 32-bit application must be marshaled into a 64-bit version
    of the structure.  This function performs that marshaling.

Arguments:

    MpioPassThroughPath32 - Supplies a pointer to a 32-bit MPIO_PASS_THROUGH_PATH
                            struct.

    MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH
                            structure, into which we'll copy the marshaled
                            32-bit data.

Return Value:

    None.

--*/
{
    //
    // Copy the first set of fields out of the 32-bit structure.  These
    // fields all line up between the 32 & 64 bit versions.
    //
    // Note that we do NOT adjust the length in the SrbControl.  This is to
    // allow the calling routine to compare the length of the actual
    // control area against the offsets embedded within.  If we adjusted the
    // length then requests with the sense area backed against the control
    // area would be rejected because the 64-bit control area is 4 bytes
    // longer.
    //
    RtlCopyMemory(MpioPassThroughPath64,
                  MpioPassThroughPath32,
                  FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset));

    //
    // Copy over the CDB.
    //
    RtlCopyMemory(MpioPassThroughPath64->PassThrough.Cdb,
                  MpioPassThroughPath32->PassThrough.Cdb,
                  16 * sizeof(UCHAR)
                  );

    //
    // Copy over the rest of the fields of the structure.
    //
    MpioPassThroughPath64->Version = MpioPassThroughPath32->Version;
    MpioPassThroughPath64->Length = MpioPassThroughPath32->Length;
    MpioPassThroughPath64->Flags = MpioPassThroughPath32->Flags;
    MpioPassThroughPath64->PortNumber = MpioPassThroughPath32->PortNumber;
    MpioPassThroughPath64->MpioPathId = MpioPassThroughPath32->MpioPathId;

    //
    // Copy the fields that follow the ULONG_PTR.
    //
    MpioPassThroughPath64->PassThrough.DataBufferOffset = (ULONG_PTR)MpioPassThroughPath32->PassThrough.DataBufferOffset;
    MpioPassThroughPath64->PassThrough.SenseInfoOffset = MpioPassThroughPath32->PassThrough.SenseInfoOffset;

    return;
}


VOID
DsmpPassThroughPathTranslate64To32(
    _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64,
    _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32
    )
/*++

Routine Description:

    On WIN64, the SCSI_PASS_THROUGH field of MPIO_PASS_THROUGH_PATH structure
    sent down by a 32-bit application must be marshaled into a 64-bit version
    of the structure.  This function marshals a 64-bit version of the structure
    back into a 32-bit version.

Arguments:

    MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH
                            struct.

    MpioPassThroughPath32 - Supplies the address of a pointer to a 32-bit
                            MPIO_PASS_THROUGH_PATH structure, into which we'll
                            copy the marshaled 64-bit data.

Return Value:

    None.

--*/
{
    //
    // Copy back the fields through the data offsets.
    //
    RtlCopyMemory(MpioPassThroughPath32,
                  MpioPassThroughPath64,
                  FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset));


    //
    // Copy over the CDB.
    //
    RtlCopyMemory(MpioPassThroughPath32->PassThrough.Cdb,
                  MpioPassThroughPath64->PassThrough.Cdb,
                  16 * sizeof(UCHAR)
                  );

    //
    // Copy over the rest of the fields of the structure.
    //
    MpioPassThroughPath32->Version = MpioPassThroughPath64->Version;
    MpioPassThroughPath32->Length = MpioPassThroughPath64->Length;
    MpioPassThroughPath32->Flags = MpioPassThroughPath64->Flags;
    MpioPassThroughPath32->PortNumber = MpioPassThroughPath64->PortNumber;
    MpioPassThroughPath32->MpioPathId = MpioPassThroughPath64->MpioPathId;

    return;
}
#endif


NTSTATUS
DsmpGetMaxPRRetryTime(
    _In_ IN PDSM_CONTEXT Context,
    _Out_ OUT PULONG RetryTime
    )
/*++

Routine Description:

    This routine is used to get the max time period for which a PR request failing
    with a retry-able unit attention should be retried before failing back to MSCS.
    The value is determined by querying the value found at
    "msdsm\Parameters\DsmMaximumStateTransitionTime"

Arguments:

    Context - The DSM Context value.
    RetryTime - The output parameter that will receive the value to be used.

Return Value:

    Status of the RtlQueryRegistryValues call.

--*/
{
    RTL_QUERY_REGISTRY_TABLE queryTable[2];
    WCHAR registryKeyName[56] = {0};
    NTSTATUS status;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetMaxPRRetryTime (DsmCtxt %p): Entering function.\n",
                Context));

    NT_ASSERT(RetryTime);
    *RetryTime = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME;

    RtlZeroMemory(queryTable, sizeof(queryTable));

    //
    // Build the key value name that we want as the base of the query.
    //
    RtlStringCbPrintfW(registryKeyName,
                       sizeof(registryKeyName),
                       DSM_PARAMETER_PATH_W);

    //
    // The query table has two entries. One for the state transition time and
    // the second which is the 'NULL' terminator.
    //
    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK;
    queryTable[0].Name = DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME;
    queryTable[0].EntryContext = RetryTime;
    queryTable[0].DefaultType  = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

    status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                    registryKeyName,
                                    queryTable,
                                    registryKeyName,
                                    NULL);

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpGetMaxPRRetryTime (DsmCtxt %p): Exiting function with status %x.\n",
                Context,
                status));

    return status;
}


NTSTATUS
DsmpQueryCacheInformationFromRegistry(
    _In_ IN PDSM_CONTEXT DsmContext,
    _Out_ OUT PBOOLEAN UseCacheForLeastBlocks,
    _Out_ OUT PULONGLONG CacheSizeForLeastBlocks
    )
/*++

Routine Description:

    This routine is used to get the information about whether sequential IO
    should use the same path when employing Least Blocks policy.
    It also queries the size of cache set by the administrator.
    The value is determined by querying the value found at
    "msdsm\Parameters\DsmUseCacheForLeastBlocks" and
    "msdsm\Parameters\DsmCacheSizeForLeastBlocks"

Arguments:

    Context - The DSM Context value.
    UseCacheForLeastBlocks - Returns the flag that indicates whether or not to
                                use same path for sequential IO when LB policy
                                is Least Blocks.
    CacheSizeForLeastBlocks - Returns the size of the cache (in bytes) set by
                                the Admin to indicate the amount of sequential
                                data that should be use the same path when LB
                                policy is Least Blocks.

Return Value:

    Status of the RtlQueryRegistryValues call.

--*/
{
    RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
    WCHAR registryKeyName[56] = {0};
    HANDLE parametersKey = NULL;
    UNICODE_STRING keyValueName;
    NTSTATUS status;
    struct _cacheSizeForLeastBlocks {
        KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
        ULONGLONG Data;
    } cacheSizeForLeastBlocks;
    ULONG length = 0;
    BOOLEAN useCacheForLeastBlocksDefault = FALSE;

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Entering function.\n",
                DsmContext));

    NT_ASSERT(UseCacheForLeastBlocks);
    NT_ASSERT(CacheSizeForLeastBlocks);

    RtlZeroMemory(queryTable, sizeof(queryTable));

    //
    // Build the key value name that we want as the base of the query.
    //
    RtlStringCbPrintfW(registryKeyName,
                       sizeof(registryKeyName),
                       DSM_PARAMETER_PATH_W);

    //
    // The query table has two entries. One for whether to use cache, and
    // and the second which is the 'NULL' terminator.
    //
    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK;
    queryTable[0].Name = DSM_USE_CACHE_FOR_LEAST_BLOCKS;
    queryTable[0].EntryContext = UseCacheForLeastBlocks;
    queryTable[0].DefaultType  = (REG_BINARY << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_BINARY;
    queryTable[0].DefaultLength = sizeof(BOOLEAN);
    queryTable[0].DefaultData = &useCacheForLeastBlocksDefault;

    status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                    registryKeyName,
                                    queryTable,
                                    registryKeyName,
                                    NULL);

    if (NT_SUCCESS(status)) {

        status = DsmpOpenDsmServicesParametersKey(KEY_QUERY_VALUE, &parametersKey);

        if (NT_SUCCESS(status)) {

            RtlInitUnicodeString(&keyValueName, DSM_CACHE_SIZE_FOR_LEAST_BLOCKS);

            status = ZwQueryValueKey(parametersKey,
                                     &keyValueName,
                                     KeyValuePartialInformation,
                                     &cacheSizeForLeastBlocks,
                                     sizeof(cacheSizeForLeastBlocks),
                                     &length);

            if (NT_SUCCESS(status)) {

                NT_ASSERT(cacheSizeForLeastBlocks.KeyValueInfo.DataLength == sizeof(ULONGLONG));
                *CacheSizeForLeastBlocks = *((ULONGLONG UNALIGNED *)&(cacheSizeForLeastBlocks.KeyValueInfo.Data));
            }
        }

        if (parametersKey) {
            ZwClose(parametersKey);
        }
    }

    TracePrint((TRACE_LEVEL_VERBOSE,
                TRACE_FLAG_PNP,
                "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Exiting function with status %x.\n",
                DsmContext,
                status));

    return status;
}

BOOLEAN
DsmpConvertSharedSpinLockToExclusive(
    _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock
    )
/*++

Routine Description:

    This routine is a wrapper around ExTryConvertSharedSpinLockExclusive() that
    guarantees the given EX_SPIN_LOCK will be acquired in Exclusive mode once
    this function returns.

    It's possible the lock may be released and re-acquired within this function
    so the caller should be very careful about the use of this function.

    N.B. The caller MUST have acquired the given lock in Shared mode before
    calling this function.

Arguments:

    SpinLock - The EX_SPIN_LOCK to convert from Shared to Exclusive mode.

Return Value:

    Status of the ExTryConvertSharedSpinLockExclusive() call.  This function
    will always return with the lock acquired in Exclusive mode.  The FALSE is
    returned, then the lock had to be released and re-acquired.

--*/
{
    BOOLEAN converted = FALSE;

    converted = (BOOLEAN)ExTryConvertSharedSpinLockExclusive(SpinLock);

    //
    // If the conversion attempt failed, then we should release the lock from
    // Shared mode and try to pick it back up in Exclusive mode to guarantee
    // this function will always return with the lock in Exclusive mode.
    //
    if (converted == FALSE) {
        ExReleaseSpinLockSharedFromDpcLevel(SpinLock);
        ExAcquireSpinLockExclusiveAtDpcLevel(SpinLock);
    }

    return converted;
}




<|start_filename|>audio/tests/KSPosTst/KsPosTestTaef.h<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File Name:
//
//  KsPosTestTaef.h
//
// Abstract:
//
//  Include file for KsPosTestTaef
//
// -------------------------------------------------------------------------------
#pragma once

#include <HalfApp.h>

extern IBasicLog*   g_pBasicLog;

// Define custom test resource properties
namespace WEX {
    namespace TestExecution {
        namespace TestResourceProperty
        {
            static const wchar_t c_szMode[] = L"Mode";
            static const wchar_t c_szPin[] = L"Pin";
        }
    }
}

namespace WDMAudio
{
    class KsPosTestPreTest
    {
        BEGIN_TEST_CLASS(KsPosTestPreTest)
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64")
            TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1")
            TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full")
            TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP")
            TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True")
            TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation")
            TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development")
            TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration")
            TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
            END_TEST_CLASS()

            BEGIN_TEST_METHOD(TAEF_VerifyAllEndpointsPluggedIn)
                TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
            END_TEST_METHOD()
    };

    class KsPosTest
    {
        BEGIN_TEST_CLASS(KsPosTest)
            TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full")
            TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP")
            TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration")
            TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True")
            TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1")
            TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DeviceID")
            TEST_CLASS_PROPERTY(L"Kits.Parameter.DeviceID.Description", L"Device id of device under test")
            TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DriverVerifierCustomizeConfiguration")
            TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.Description", L"Specifies that this test may want to automatically update Driver Verifier settings")
            TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.Default", L"True")
            TEST_CLASS_PROPERTY(L"Kits.Parameter.DriverVerifierCustomizeConfiguration.IsUserSettable", L"False")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64")
            TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64")
            TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation")
            TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development")
            TEST_CLASS_PROPERTY(L"Subfeature", L"Wave Driver Tests")
            TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")







        END_TEST_CLASS()

            TEST_CLASS_SETUP(KsPosTestSetup)
            TEST_CLASS_CLEANUP(KsPosTestCleanUp)

            TEST_METHOD_SETUP(TestCaseSetup)
            TEST_METHOD_CLEANUP(TestCaseCleanUp)

            BEGIN_TEST_METHOD(TAEF_DriftAndJitter)
                TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
                TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
                TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
                TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*LOOPBACK*'")
            END_TEST_METHOD()

            BEGIN_TEST_METHOD(TAEF_Latency)
                TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
                TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
                TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
                TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*LOOPBACK*'")
            END_TEST_METHOD()
                      
    public:
        CHalfApp*           m_pHalf;
        CHalfApp*           m_pHostHalf;        // HalfApp corresponding to the host pin of a loopback pin, if any
        PTIMER_MECHANISM    m_pTimer;
        BOOL                m_fLogHistograms;
        LONGLONG            m_qpcFrequency;     // QPC frequency.
        LONGLONG            m_testBaseHns;      // Base time, in HNS, used inside the tests to make QPC times smaller.
        CRtThreadRegistration m_threadPriority;
    };
}

extern WDMAudio::KsPosTest* g_pKsPosTst;


<|start_filename|>avstream/avscamera/sys/PreviewHwSim.cpp<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2014, Microsoft Corporation.

    File:

        PreviewHwSim.cpp

    Abstract:

        This file contains the implementation of the CPreviewHardwareSimulation 
        class.

        This is a specialization of CHardwareSimulation that provides preview-
        specific metadata.

    History:

        created 5/28/2014

**************************************************************************/

#include "Common.h"

/**************************************************************************

    PAGEABLE CODE

**************************************************************************/

#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA


CPreviewHardwareSimulation::CPreviewHardwareSimulation(
    _Inout_ CSensor *Sensor,
    _In_    LONG    PinID
)
    : CHardwareSimulation( Sensor, PinID )
    , m_Illuminated(FALSE)
{
    PAGED_CODE();
}


CPreviewHardwareSimulation::~CPreviewHardwareSimulation()
{
    PAGED_CODE();
}

void
CPreviewHardwareSimulation::
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();

    //  Do normal timer handling.
    CHardwareSimulation::FakeHardware();

    //  Update the zoom.
    m_Sensor->UpdateZoom();
}

//
//  Helper function that collects the current ISP settings into our metadata 
//  structure.
//
METADATA_PREVIEWAGGREGATION
CPreviewHardwareSimulation::
GetMetadata()
{
    PAGED_CODE();

    METADATA_PREVIEWAGGREGATION Metadata;
    ISP_FRAME_SETTINGS *pSettings = GetIspSettings();

    //  Wipe the metadata so all settings will default to "Not Set".
    RtlZeroMemory(&Metadata, sizeof(Metadata));

    //  IsoGains;
    Metadata.IsoAnalogGain =
        CMetadataSRational(GetRandom((LONG)5000, (LONG)20000), (LONG)10000);  // Some number from 0.5 to 2.0 - for testing.
    Metadata.IsoDigitalGain =
        CMetadataSRational(GetRandom((LONG)5000, (LONG)20000), (LONG)10000);  // Some number from 0.5 to 2.0 - for testing.

    //  WhiteBalanceGains;
    Metadata.WhiteBalanceGain_R =       //  MF_CAPTURE_METADATA_WHITEBALANCE_GAINS
        CMetadataSRational(GetRandom((LONG)5000, (LONG)20000), (LONG)10000);  // Some number from 0.5 to 2.0 - for testing.
    Metadata.WhiteBalanceGain_G =       //  MF_CAPTURE_METADATA_WHITEBALANCE_GAINS
        CMetadataSRational(GetRandom((LONG)5000, (LONG)20000), (LONG)10000);  // Some number from 0.5 to 2.0 - for testing.
    Metadata.WhiteBalanceGain_B =       //  MF_CAPTURE_METADATA_WHITEBALANCE_GAINS
        CMetadataSRational(GetRandom((LONG)5000, (LONG)20000), (LONG)10000);  // Some number from 0.5 to 2.0 - for testing.

    return Metadata;
}


VOID
CPreviewHardwareSimulation::
GetCaptureStats(
    _In_ PKSCAMERA_METADATA_CAPTURESTATS pCaptureStats
    )
{
    PAGED_CODE();

    KSCAMERA_METADATA_CAPTURESTATS CaptureStats = {0};
    ISP_FRAME_SETTINGS *pSettings = GetIspSettings();

    CaptureStats.Flags =
        (KSCAMERA_METADATA_CAPTURESTATS_FLAG_EXPOSURETIME |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_EXPOSURECOMPENSATION |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_ISOSPEED |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_FOCUSSTATE |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_LENSPOSITION |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_WHITEBALANCE |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_FLASH |
        KSCAMERA_METADATA_CAPTURESTATS_FLAG_SENSORFRAMERATE);
    
    //  ExposureTime;
    CaptureStats.ExposureTime = GetCurrentExposureTime();

    //  EVCompensation;
    CaptureStats.ExposureCompensationFlags = pSettings->EVCompensation.Mode;
    CaptureStats.ExposureCompensationValue = pSettings->EVCompensation.Value;

    //  ISOSpeed;
    CaptureStats.IsoSpeed = GetCurrentISOSpeed();
    DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", CaptureStats.IsoSpeed, pSettings->ISOMode);

    //  FocusState;
    m_Sensor->GetFocusState((KSCAMERA_EXTENDEDPROP_FOCUSSTATE *)&CaptureStats.FocusState);

    //  LensPosition;
    CaptureStats.LensPosition = pSettings->FocusSetting.VideoProc.Value.ul;

    //  WhiteBalanceMode;
    CaptureStats.WhiteBalance = (ULONG)pSettings->WhiteBalanceMode;

    //  FlashOn;
    CaptureStats.Flash = (ULONG) pSettings->FlashMode;

    //  SensorFrameRate;
    ULARGE_INTEGER  FrameRate = {0};
    if (m_TimePerFrame)
    {
        FrameRate.LowPart = 60 * 60 * 24;
        FrameRate.HighPart = (ULONG)((ONESECOND * FrameRate.LowPart) / m_TimePerFrame);
    }
    CaptureStats.SensorFramerate = FrameRate.QuadPart;

    if (pCaptureStats)
    {
        *pCaptureStats = CaptureStats;
    }

    return;
}

//
//  Emit metadata here for preview pin.
//
void
CPreviewHardwareSimulation::
EmitMetadata(
    _Inout_ PKSSTREAM_HEADER    pStreamHeader
)
/*++

Routine Description:

    Emit metadata for a photo.

Arguments:

    None

Return Value:

    Success / Failure

--*/
{
    PAGED_CODE();

    NT_ASSERT(pStreamHeader);

    //  Add the normal frame info to the metadata
    //  Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized.
    CHardwareSimulation::EmitMetadata( pStreamHeader );

    if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA))
    {
        PKS_FRAME_INFO          pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1);
        PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1);
        ULONG                   BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize;

        if(m_PhotoConfirmationEntry.isRequired())
        {
            if(BytesLeft < sizeof(KSCAMERA_METADATA_PHOTOCONFIRMATION))
            {
                //Fatal driver error, should never come here.
                NT_ASSERT(FALSE);
                return;
            }
            PKSCAMERA_METADATA_PHOTOCONFIRMATION pPhotoConfirmation =
                (PKSCAMERA_METADATA_PHOTOCONFIRMATION) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            pPhotoConfirmation->Header.MetadataId = MetadataId_PhotoConfirmation;
            pPhotoConfirmation->Header.Size = sizeof(*pPhotoConfirmation);
            pPhotoConfirmation->PhotoConfirmationIndex = m_PhotoConfirmationEntry.getIndex();
            pMetadata->UsedSize += sizeof(*pPhotoConfirmation);
            BytesLeft -= sizeof(*pPhotoConfirmation);

            DBG_TRACE("PhotoConfirmation Header.MetadataId = %u", pPhotoConfirmation->Header.MetadataId);
            DBG_TRACE("PhotoConfirmation Header.Size       = %u", pPhotoConfirmation->Header.Size);
            DBG_TRACE("PhotoConfirmation Index             = %u", pPhotoConfirmation->PhotoConfirmationIndex);
        }
        else
        {
            DBG_TRACE("Normal frame; no photo confirmation metadata.");
        }

        if (BytesLeft >= sizeof(CAMERA_METADATA_PREVIEWAGGREGATION))
        {
            PCAMERA_METADATA_PREVIEWAGGREGATION pPreviewAggregation =
                (PCAMERA_METADATA_PREVIEWAGGREGATION)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            pPreviewAggregation->Header.MetadataId = (ULONG)MetadataId_Custom_PreviewAggregation;
            pPreviewAggregation->Header.Size = sizeof(*pPreviewAggregation);
            pPreviewAggregation->Data = GetMetadata();
            pMetadata->UsedSize += sizeof(*pPreviewAggregation);
            BytesLeft -= sizeof(*pPreviewAggregation);
        }

        if( BytesLeft >= sizeof(KSCAMERA_METADATA_CAPTURESTATS) )
        {
            PKSCAMERA_METADATA_CAPTURESTATS pPreviewCaptureStats =
                (PKSCAMERA_METADATA_CAPTURESTATS) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            GetCaptureStats(pPreviewCaptureStats);
            pPreviewCaptureStats->Header.MetadataId = (ULONG)MetadataId_CaptureStats;
            pPreviewCaptureStats->Header.Size = sizeof(*pPreviewCaptureStats);
            pMetadata->UsedSize += sizeof(*pPreviewCaptureStats);
            BytesLeft -= sizeof(*pPreviewCaptureStats);
        }

        if (BytesLeft >= sizeof(CAMERA_METADATA_UVC_HEADER))
        {
            PCAMERA_METADATA_UVC_HEADER pPreviewUvcHeader =
                (PCAMERA_METADATA_UVC_HEADER)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            pPreviewUvcHeader->Header.MetadataId = (ULONG)MetadataId_UsbVideoHeader;
            pPreviewUvcHeader->Header.Size = sizeof(CAMERA_METADATA_UVC_HEADER);

            pPreviewUvcHeader->Data.StartOfFrameTimestamp.PresentationTimeStamp = 1;
            pPreviewUvcHeader->Data.EndOfFrameTimestamp.SourceClockReference = 100;

            pMetadata->UsedSize += sizeof(CAMERA_METADATA_UVC_HEADER);
            BytesLeft -= sizeof(CAMERA_METADATA_UVC_HEADER);
        }

        if (BytesLeft >= sizeof(CAMERA_METADATA_EXTRINSICS))
        {
            PCAMERA_METADATA_EXTRINSICS pPreviewExtrinsics =
                (PCAMERA_METADATA_EXTRINSICS)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            RtlZeroMemory(pPreviewExtrinsics, sizeof(CAMERA_METADATA_EXTRINSICS));

            pPreviewExtrinsics->Header.MetadataId = (ULONG)MetadataId_CameraExtrinsics;
            pPreviewExtrinsics->Header.Size = sizeof(CAMERA_METADATA_EXTRINSICS);

            pMetadata->UsedSize += sizeof(CAMERA_METADATA_EXTRINSICS);
            BytesLeft -= sizeof(CAMERA_METADATA_EXTRINSICS);
        }

        if (BytesLeft >= sizeof(CAMERA_METADATA_INTRINSICS))
        {
            PCAMERA_METADATA_INTRINSICS pPreviewIntrinsics =
                (PCAMERA_METADATA_INTRINSICS)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            RtlZeroMemory(pPreviewIntrinsics, sizeof(CAMERA_METADATA_INTRINSICS));

            pPreviewIntrinsics->Header.MetadataId = (ULONG)MetadataId_CameraIntrinsics;
            pPreviewIntrinsics->Header.Size = sizeof(CAMERA_METADATA_INTRINSICS);

            pMetadata->UsedSize += sizeof(CAMERA_METADATA_INTRINSICS);
            BytesLeft -= sizeof(CAMERA_METADATA_INTRINSICS);
        }

        //  TODO: This metadata should only be exposed on a sensor category preview pin.
        //        It's possible I should derive a new preview sim to handle this; but for now
        //        we just populate the IR illumination state on every preview pin.  Hopefully 
        //        the app is smart enough to realize that this is meaningless if they have not 
        //        selected an IR mediatype.
        if (BytesLeft >= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION))
        {
            PKSCAMERA_METADATA_FRAMEILLUMINATION pPreviewIllumination =
                (PKSCAMERA_METADATA_FRAMEILLUMINATION)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            RtlZeroMemory(pPreviewIllumination, sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION));

            pPreviewIllumination->Header.MetadataId = (ULONG)MetadataId_FrameIllumination;
            pPreviewIllumination->Header.Size = sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);

            //  Toggle the Illumination state for an IR frame.
            CExtendedVidProcSetting State;
            (void) m_Sensor->GetIRTorch(&State);

            if (State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON)
            {
                m_Illuminated = TRUE;
                pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON;
            }
            else if ((State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION))
            {
                m_Illuminated = !m_Illuminated;
                if (m_Illuminated)
                {
                    pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON;
                }
            }
            else
            {
                m_Illuminated = FALSE;
            }

            DBG_TRACE("Frame Illumination: Flags=0x%016llX, State=%s", 
                State.Flags, 
                (pPreviewIllumination->Flags==KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON ?"ON":"OFF"));

            pMetadata->UsedSize += sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);
            BytesLeft -= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);
        }

        //  MF_CAPTURE_METADATA_HISTOGRAM
        //
        //  Generate the entire Histogram metadata blob here.
        //
        if( BytesLeft >= sizeof(CAMERA_METADATA_HISTOGRAM) )
        {
            PCAMERA_METADATA_HISTOGRAM pHistogram =
                (PCAMERA_METADATA_HISTOGRAM) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            ULONG               ChannelMask = m_Synthesizer->GetChannelMask();
            CExtendedProperty   Setting;

            m_Sensor->GetHistogram( &Setting );
            if( Setting.Flags & KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON &&
                    ChannelMask != 0 )
            {
                //  Just checking to see if we messed something up here.
                NT_ASSERT( pMetadata->BufferSize >= CExtendedMetadata::METADATA_MAX + sizeof(CAMERA_METADATA_HISTOGRAM) );

                pHistogram->Header.MetadataId = (ULONG) MetadataId_Custom_Histogram;
                pHistogram->Header.Size = sizeof(*pHistogram);

                //  Acquire the data for every channel.
                pHistogram->Data.ChannelMask = ChannelMask;
                pHistogram->Data.Height = m_Height;
                pHistogram->Data.Width = m_Width;
                pHistogram->Data.FourCC = m_Sensor->GetFourCC( m_PinID );
                m_Synthesizer->Histogram( pHistogram->Data.P0Data, pHistogram->Data.P1Data, pHistogram->Data.P2Data );
                pMetadata->UsedSize += sizeof(*pHistogram);
                BytesLeft -= sizeof(*pHistogram);
            }
        }

        CExtendedVidProcSetting FaceDetect;
        m_Sensor->GetFaceDetection(&FaceDetect);

        if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PREVIEW )
        {
            DBG_TRACE("PREVIEW");
            EmitFaceMetadata(
                pStreamHeader,
                FaceDetect.GetULONG(),
                FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK);
        }
    }
}



<|start_filename|>general/SimpleMediaSource/SimpleMediaSourceDriver/Public.h<|end_filename|>
/*++

Module Name:

    public.h

Abstract:

    This module contains the common declarations shared by driver
    and user applications.

Environment:

    driver and application

--*/

//
// Define an Interface Guid so that apps can find the device and talk to it.
//

DEFINE_GUID (GUID_DEVINTERFACE_SimpleMediaSourceDriver,
    0xb5036295,0xf041,0x4506,0x88,0xdc,0xcb,0x16,0x5c,0x4d,0x67,0x8c);
// {b5036295-f041-4506-88dc-cb165c4d678c}


<|start_filename|>avstream/avshws/capture.cpp<|end_filename|>
/**************************************************************************

    AVStream Simulated Hardware Sample

    Copyright (c) 2001, Microsoft Corporation.

    File:

        capture.cpp

    Abstract:

        This file contains source for the video capture pin on the capture
        filter.  The capture sample performs "fake" DMA directly into
        the capture buffers.  Common buffer DMA will work slightly differently.

        For common buffer DMA, the general technique would be DPC schedules
        processing with KsPinAttemptProcessing.  The processing routine grabs
        the leading edge, copies data out of the common buffer and advances.
        Cloning would not be necessary with this technique.  It would be 
        similiar to the way "AVSSamp" works, but it would be pin-centric.

    History:

        created 3/8/2001

**************************************************************************/

#include "avshws.h"
#include <ksmedia.h>
#include <ntintsafe.h>

/**************************************************************************

    PAGEABLE CODE

**************************************************************************/


#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA

#define DMAX_X 320
#define DMAX_Y 240
#define D_X 320
#define D_Y 240

CCapturePin::
CCapturePin (
    IN PKSPIN Pin
    ) :
    m_Pin (Pin)
    ,m_PresentationTime (0)

/*++

Routine Description:

    Construct a new capture pin.

Arguments:

    Pin -
        The AVStream pin object corresponding to the capture pin

Return Value:

    None

--*/

{

    PAGED_CODE();

    PKSDEVICE Device = KsPinGetDevice (Pin);

    //
    // Set up our device pointer.  This gives us access to "hardware I/O"
    // during the capture routines.
    //
    m_Device = reinterpret_cast <CCaptureDevice *> (Device -> Context);
}

/*************************************************/


NTSTATUS
CCapturePin::
DispatchCreate (
    IN PKSPIN Pin,
    IN PIRP Irp
    )

/*++

Routine Description:

    Create a new capture pin.  This is the creation dispatch for
    the video capture pin.

Arguments:

    Pin -
        The pin being created

    Irp -
        The creation Irp

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;

    CCapturePin *CapPin = new (NonPagedPoolNx, 'niPC') CCapturePin (Pin);

    if (!CapPin) {
        //
        // Return failure if we couldn't create the pin.
        //
        Status = STATUS_INSUFFICIENT_RESOURCES;

    } else {
        //
        // Add the item to the object bag if we we were successful. 
        // Whenever the pin closes, the bag is cleaned up and we will be
        // freed.
        //
        Status = KsAddItemToObjectBag (
            Pin -> Bag,
            reinterpret_cast <PVOID> (CapPin),
            reinterpret_cast <PFNKSFREE> (CCapturePin::Cleanup)
            );

        if (!NT_SUCCESS (Status)) {
            delete CapPin;
        } else {
            Pin -> Context = reinterpret_cast <PVOID> (CapPin);
        }

    }

    //
    // If we succeeded so far, stash the video info header away and change
    // our allocator framing to reflect the fact that only now do we know
    // the framing requirements based on the connection format.
    //
    PKS_VIDEOINFOHEADER VideoInfoHeader = NULL;

    if (NT_SUCCESS (Status)) {

        VideoInfoHeader = CapPin -> CaptureVideoInfoHeader ();
        if (!VideoInfoHeader) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(Status)) {
        //
        // We need to edit the descriptor to ensure we don't mess up any other
        // pins using the descriptor or touch read-only memory.
        //
        Status = KsEdit (
            Pin, 
            &Pin -> Descriptor, 
            AVSHWS_POOLTAG);

        if (NT_SUCCESS (Status)) { 

            //
            // If the edits proceeded without running out of memory, adjust 
            // the framing based on the video info header.
            //
            Status = KsEdit (
                Pin, 
                &Pin -> Descriptor -> AllocatorFraming, 
                AVSHWS_POOLTAG);

            if (NT_SUCCESS (Status)) {

                //
                // We've KsEdit'ed this...  I'm safe to cast away constness as
                // long as the edit succeeded.
                //
                PKSALLOCATOR_FRAMING_EX Framing =
                    const_cast <PKSALLOCATOR_FRAMING_EX> (
                        Pin -> Descriptor -> AllocatorFraming
                        );

                Framing -> FramingItem [0].Frames = 2;

                //
                // The physical and optimal ranges must be biSizeImage.  We only
                // support one frame size, precisely the size of each capture
                // image.
                //
                Framing -> FramingItem [0].PhysicalRange.MinFrameSize =
                    Framing -> FramingItem [0].PhysicalRange.MaxFrameSize =
                    Framing -> FramingItem [0].FramingRange.Range.MinFrameSize =
                    Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize =
                    VideoInfoHeader -> bmiHeader.biSizeImage;

                Framing -> FramingItem [0].PhysicalRange.Stepping = 
                    Framing -> FramingItem [0].FramingRange.Range.Stepping =
                    0;

            }

        }
    }

    if (NT_SUCCESS (Status)) {
        //
        // Adjust the stream header size.  The video packets have extended
        // header info (KS_FRAME_INFO).
        //
        Pin -> StreamHeaderSize = sizeof (KSSTREAM_HEADER) +
            sizeof (KS_FRAME_INFO);

    }
    return Status;
}

/*************************************************/


PKS_VIDEOINFOHEADER 
CCapturePin::
CaptureVideoInfoHeader (
    )

/*++

Routine Description:

    Capture the video info header out of the connection format.  This
    is what we use to base synthesized images off.

Arguments:

    None

Return Value:

    The captured video info header or NULL if there is insufficient
    memory.

--*/

{

    PAGED_CODE();

    PKS_VIDEOINFOHEADER ConnectionHeader =
        &((reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> 
            (m_Pin -> ConnectionFormat)) -> 
            VideoInfoHeader);

    m_VideoInfoHeader = reinterpret_cast <PKS_VIDEOINFOHEADER> (
        ExAllocatePoolZero (
            NonPagedPoolNx,
            KS_SIZE_VIDEOHEADER (ConnectionHeader),
            AVSHWS_POOLTAG
            )
        );

    if (!m_VideoInfoHeader)
        return NULL;

    //
    // Bag the newly allocated header space.  This will get cleaned up
    // automatically when the pin closes.
    //
    NTSTATUS Status =
        KsAddItemToObjectBag (
            m_Pin -> Bag,
            reinterpret_cast <PVOID> (m_VideoInfoHeader),
            NULL
            );

    if (!NT_SUCCESS (Status)) {

        ExFreePool (m_VideoInfoHeader);
        return NULL;

    } else {

        //
        // Copy the connection format video info header into the newly 
        // allocated "captured" video info header.
        //
        RtlCopyMemory (
            m_VideoInfoHeader,
            ConnectionHeader,
            KS_SIZE_VIDEOHEADER (ConnectionHeader)
            );

    }

    return m_VideoInfoHeader;

}


NTSTATUS
CCapturePin::
Process (
    )

/*++

Routine Description:

    The process dispatch for the pin bridges to this location.
    We handle setting up scatter gather mappings, etc...

Arguments:

    None

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;
    PKSSTREAM_POINTER Leading;

    _DbgPrintF(DEBUGLVL_VERBOSE, ("Process"));

    Leading = KsPinGetLeadingEdgeStreamPointer (
        m_Pin,
        KSSTREAM_POINTER_STATE_LOCKED
        );

    while (NT_SUCCESS (Status) && Leading) {

        PKSSTREAM_POINTER ClonePointer;
        PSTREAM_POINTER_CONTEXT SPContext = NULL;

        //
        // If no data is present in the Leading edge stream pointer, just 
        // move on to the next frame
        //
        if ( NULL == Leading -> StreamHeader -> Data ) {
            Status = KsStreamPointerAdvance(Leading);
            continue;
        }
        //
        // For optimization sake in this particular sample, I will only keep
        // one clone stream pointer per frame.  This complicates the logic
        // here but simplifies the completions.
        //
        // I'm also choosing to do this since I need to keep track of the
        // virtual addresses corresponding to each mapping since I'm faking
        // DMA.  It simplifies that too.
        //
        if (!m_PreviousStreamPointer) {
            //
            // First thing we need to do is clone the leading edge.  This allows
            // us to keep reference on the frames while they're in DMA.
            //
            Status = KsStreamPointerClone (
                Leading,
                NULL,
                sizeof (STREAM_POINTER_CONTEXT),
                &ClonePointer
                );

            //
            // I use this for easy chunking of the buffer.  We're not really
            // dealing with physical addresses.  This keeps track of what 
            // virtual address in the buffer the current scatter / gather 
            // mapping corresponds to for the fake hardware.
            //
            if (NT_SUCCESS (Status)) {

                //
                // Set the stream header data used to 0.  We update this 
                // in the DMA completions.  For queues with DMA, we must
                // update this field ourselves.
                //
                ClonePointer -> StreamHeader -> DataUsed = 0;

                SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT> 
                    (ClonePointer -> Context);

                SPContext -> BufferVirtual = 
                    reinterpret_cast <PUCHAR> (
                        ClonePointer -> StreamHeader -> Data
                        );
            }

        } else {

            ClonePointer = m_PreviousStreamPointer;
            SPContext = reinterpret_cast <PSTREAM_POINTER_CONTEXT> 
                (ClonePointer -> Context);
            Status = STATUS_SUCCESS;
        }

        //
        // If the clone failed, likely we're out of resources.  Break out
        // of the loop for now.  We may end up starving DMA.
        //
        if (!NT_SUCCESS (Status)) {
            KsStreamPointerUnlock (Leading, FALSE);
            break;
        }

        //
        // Program the fake hardware.  I would use Clone -> OffsetOut.*, but
        // because of the optimization of one stream pointer per frame, it
        // doesn't make complete sense.
        //
        ULONG MappingsUsed =
            m_Device -> ProgramScatterGatherMappings (
                ClonePointer,
                &(SPContext -> BufferVirtual),
                Leading -> OffsetOut.Mappings,
                Leading -> OffsetOut.Remaining
                );

        //
        // In order to keep one clone per frame and simplify the fake DMA
        // logic, make a check to see if we completely used the mappings in
        // the leading edge.  Set a flag.
        //
        if (MappingsUsed == Leading -> OffsetOut.Remaining) {
            m_PreviousStreamPointer = NULL;
        } else {
            m_PreviousStreamPointer = ClonePointer;
        }

        if (MappingsUsed) {
            //
            // If any mappings were added to scatter / gather queues, 
            // advance the leading edge by that number of mappings.  If 
            // we run off the end of the queue, Status will be 
            // STATUS_DEVICE_NOT_READY.  Otherwise, the leading edge will
            // point to a new frame.  The previous one will not have been
            // dismissed (unless "DMA" completed) since there's a clone
            // pointer referencing the frames.
            //
            Status =
                KsStreamPointerAdvanceOffsets (
                    Leading,
                    0,
                    MappingsUsed,
                    FALSE
                    );
        } else {

            //
            // The hardware was incapable of adding more entries.  The S/G
            // table is full.
            //
            Status = STATUS_PENDING;
            break;

        }

    }

    //
    // If the leading edge failed to lock (this is always possible, remember
    // that locking CAN occassionally fail), don't blow up passing NULL
    // into KsStreamPointerUnlock.  Also, set m_PendIo to kick us later...
    //
    if (!Leading) {

        m_PendIo = TRUE;

        //
        // If the lock failed, there's no point in getting called back 
        // immediately.  The lock could fail due to insufficient memory,
        // etc...  In this case, we don't want to get called back immediately.
        // Return pending.  The m_PendIo flag will cause us to get kicked
        // later.
        //
        Status = STATUS_PENDING;
    }

    //
    // If we didn't run the leading edge off the end of the queue, unlock it.
    //
    if (NT_SUCCESS (Status) && Leading) {
        KsStreamPointerUnlock (Leading, FALSE);
    } else {
        //
        // DEVICE_NOT_READY indicates that the advancement ran off the end
        // of the queue.  We couldn't lock the leading edge.
        //
        if (Status == STATUS_DEVICE_NOT_READY) Status = STATUS_SUCCESS;
    }

    //
    // If we failed with something that requires pending, set the pending I/O
    // flag so we know we need to start it again in a completion DPC.
    //
    if (!NT_SUCCESS (Status) || Status == STATUS_PENDING) {
        m_PendIo = TRUE;
    }

    _DbgPrintF(DEBUGLVL_VERBOSE, ("Leaving Process..."));
    return Status;

}

/*************************************************/


NTSTATUS
CCapturePin::
CleanupReferences (
    )

/*++

Routine Description:

    Clean up any references we're holding on frames after we abruptly
    stop the hardware.

Arguments:

    None

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin);
    PKSSTREAM_POINTER NextClone = NULL;

    //
    // Walk through the clones, deleting them, and setting DataUsed to
    // zero since we didn't use any data!
    //
    while (Clone) {

        NextClone = KsStreamPointerGetNextClone (Clone);

        Clone -> StreamHeader -> DataUsed = 0;
        KsStreamPointerDelete (Clone);

        Clone = NextClone;

    }

    return STATUS_SUCCESS;

}

/*************************************************/


NTSTATUS
CCapturePin::
SetState (
    IN KSSTATE ToState,
    IN KSSTATE FromState
    )

/*++

Routine Description:

    This is called when the caputre pin transitions state.  The routine
    attempts to acquire / release any hardware resources and start up
    or shut down capture based on the states we are transitioning to
    and away from.

Arguments:

    ToState -
        The state we're transitioning to

    FromState -
        The state we're transitioning away from

Return Value:

    Success / Failure

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;

    switch (ToState) {

        case KSSTATE_STOP:

            //
            // First, stop the hardware if we actually did anything to it.
            //
            if (m_HardwareState != HardwareStopped) {
                Status = m_Device -> Stop ();
                NT_ASSERT (NT_SUCCESS (Status));

                m_HardwareState = HardwareStopped;
            }

            //
            // We've stopped the "fake hardware".  It has cleared out
            // it's scatter / gather tables and will no longer be 
            // completing clones.  We had locks on some frames that were,
            // however, in hardware.  This will clean them up.  An
            // alternative location would be in the reset dispatch.
            // Note, however, that the reset dispatch can occur in any
            // state and this should be understood.
            //
            // Some hardware may fill all S/G mappings before stopping...
            // in this case, you may not have to do this.  The 
            // "fake hardware" here simply stops filling mappings and 
            // cleans its scatter / gather tables out on the Stop call.
            //
            Status = CleanupReferences ();

            //
            // Release any hardware resources related to this pin.
            //
            if (m_AcquiredResources) {
                //
                // If we got an interface to the clock, we must release it.
                //
                if (m_Clock) {
                    m_Clock -> Release ();
                    m_Clock = NULL;
                }

                m_Device -> ReleaseHardwareResources (
                    );

                m_AcquiredResources = FALSE;
            }

            break;

        case KSSTATE_ACQUIRE:
            //
            // Acquire any hardware resources related to this pin.  We should
            // only acquire them here -- **NOT** at filter create time. 
            // This means we do not fail creation of a filter because of
            // limited hardware resources.
            //
            if (FromState == KSSTATE_STOP) {
                Status = m_Device -> AcquireHardwareResources (
                    this,
                    m_VideoInfoHeader
                    );

                if (NT_SUCCESS (Status)) {
                    m_AcquiredResources = TRUE;

                    //
                    // Attempt to get an interface to the master clock.
                    // This will fail if one has not been assigned.  Since
                    // one must be assigned while the pin is still in 
                    // KSSTATE_STOP, this is a guranteed method of getting
                    // the clock should one be assigned.
                    //
                    if (!NT_SUCCESS (
                        KsPinGetReferenceClockInterface (
                            m_Pin,
                            &m_Clock
                            )
                        )) {

                        //
                        // If we could not get an interface to the clock,
                        // don't use one.  
                        //
                        m_Clock = NULL;

                    }

                } else {
                    m_AcquiredResources = FALSE;
                }

            } else {
                //
                // Standard transport pins will always receive transitions in
                // +/- 1 manner.  This means we'll always see a PAUSE->ACQUIRE
                // transition before stopping the pin.  
                //
                // The below is done because on DirectX 8.0, when the pin gets
                // a message to stop, the queue is inaccessible.  The reset 
                // which comes on every stop happens after this (at which time
                // the queue is inaccessible also).  So, for compatibility with
                // DirectX 8.0, I am stopping the "fake" hardware at this
                // point and cleaning up all references we have on frames.  See
                // the comments above regarding the CleanupReferences call.
                //
                // If this sample were targeting XP only, the below code would
                // not be here.  Again, I only do this so the sample does not
                // hang when it is stopped running on a configuration such as
                // Win2K + DX8. 
                //
                if (m_HardwareState != HardwareStopped) {
                    Status = m_Device -> Stop ();
                    NT_ASSERT (NT_SUCCESS (Status));

                    m_HardwareState = HardwareStopped;
                }

                Status = CleanupReferences ();
            }

            m_FrameNumber   = 0;
            m_DroppedFrames = 0;
            break;

        case KSSTATE_PAUSE:
            //
            // Stop the hardware simulation if we're coming down from run.
            //
            if (FromState == KSSTATE_RUN) {

                m_PresentationTime = 0;
                Status = m_Device -> Pause (TRUE);

                if (NT_SUCCESS (Status)) {
                    m_HardwareState = HardwarePaused;
                }

            }
            m_FrameNumber   = 0;
            break;

        case KSSTATE_RUN:
            //
            // Start the hardware simulation or unpause it depending on
            // whether we're initially running or we've paused and restarted.
            //
            if (m_HardwareState == HardwarePaused) {
                Status = m_Device -> Pause (FALSE);
            } else {
                Status = m_Device -> Start ();
            }

            if (NT_SUCCESS (Status)) {
                m_HardwareState = HardwareRunning;
            }

            break;

    }

    return Status;

}

/*************************************************/


NTSTATUS
CCapturePin::
IntersectHandler (
    IN PKSFILTER Filter,
    IN PIRP Irp,
    IN PKSP_PIN PinInstance,
    IN PKSDATARANGE CallerDataRange,
    IN PKSDATARANGE DescriptorDataRange,
    IN ULONG BufferSize,
    OUT PVOID Data OPTIONAL,
    OUT PULONG DataSize
    )

/*++

Routine Description:

    This routine handles video pin intersection queries by determining the
    intersection between two data ranges.

Arguments:

    Filter -
        Contains a void pointer to the  filter structure.

    Irp -
        Contains a pointer to the data intersection property request.

    PinInstance -
        Contains a pointer to a structure indicating the pin in question.

    CallerDataRange -
        Contains a pointer to one of the data ranges supplied by the client
        in the data intersection request.  The format type, subtype and
        specifier are compatible with the DescriptorDataRange.

    DescriptorDataRange -
        Contains a pointer to one of the data ranges from the pin descriptor
        for the pin in question.  The format type, subtype and specifier are
        compatible with the CallerDataRange.

    BufferSize -
        Contains the size in bytes of the buffer pointed to by the Data
        argument.  For size queries, this value will be zero.

    Data -
        Optionally contains a pointer to the buffer to contain the data 
        format structure representing the best format in the intersection 
        of the two data ranges.  For size queries, this pointer will be 
        NULL.

    DataSize -
        Contains a pointer to the location at which to deposit the size 
        of the data format.  This information is supplied by the function 
        when the format is actually delivered and in response to size 
        queries.

Return Value:

    STATUS_SUCCESS if there is an intersection and it fits in the supplied
    buffer, STATUS_BUFFER_OVERFLOW for successful size queries, 
    STATUS_NO_MATCH if the intersection is empty, or 
    STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small.

--*/

{
    PAGED_CODE();

    const GUID VideoInfoSpecifier = 
        {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)};
    
    NT_ASSERT(Filter);
    NT_ASSERT(Irp);
    NT_ASSERT(PinInstance);
    NT_ASSERT(CallerDataRange);
    NT_ASSERT(DescriptorDataRange);
    NT_ASSERT(DataSize);
    
    ULONG DataFormatSize;
    
    //
    // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER
    //
    if (IsEqualGUID(CallerDataRange->Specifier, VideoInfoSpecifier) &&
        CallerDataRange -> FormatSize >= sizeof (KS_DATARANGE_VIDEO)) {
            
        PKS_DATARANGE_VIDEO callerDataRange = 
            reinterpret_cast <PKS_DATARANGE_VIDEO> (CallerDataRange);

        PKS_DATARANGE_VIDEO descriptorDataRange = 
            reinterpret_cast <PKS_DATARANGE_VIDEO> (DescriptorDataRange);

        PKS_DATAFORMAT_VIDEOINFOHEADER FormatVideoInfoHeader;

        //
        // Check that the other fields match
        //
        if ((callerDataRange->bFixedSizeSamples != 
                descriptorDataRange->bFixedSizeSamples) ||
            (callerDataRange->bTemporalCompression != 
                descriptorDataRange->bTemporalCompression) ||
            (callerDataRange->StreamDescriptionFlags != 
                descriptorDataRange->StreamDescriptionFlags) ||
            (callerDataRange->MemoryAllocationFlags != 
                descriptorDataRange->MemoryAllocationFlags) ||
            (RtlCompareMemory (&callerDataRange->ConfigCaps,
                    &descriptorDataRange->ConfigCaps,
                    sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) != 
                    sizeof (KS_VIDEO_STREAM_CONFIG_CAPS))) 
        {
            return STATUS_NO_MATCH;
        }

        //
        // KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from
        // the caller's data range.  This **MUST** be validated; the
        // extended bmiHeader size (biSize) must not extend past the end
        // of the range buffer.  Possible arithmetic overflow is also
        // checked for.
        //
        {
            ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
                &callerDataRange->VideoInfoHeader
                );

            ULONG DataRangeSize = 
                FIELD_OFFSET (KS_DATARANGE_VIDEO, VideoInfoHeader) +
                VideoHeaderSize;

            //
            // Check that biSize does not extend past the buffer.  The 
            // first two checks are for arithmetic overflow on the 
            // operations to compute the alleged size.  (On unsigned
            // math, a+b < a iff an arithmetic overflow occurred).
            //
            if (
                VideoHeaderSize < callerDataRange->
                    VideoInfoHeader.bmiHeader.biSize ||
                DataRangeSize < VideoHeaderSize ||
                DataRangeSize > callerDataRange -> DataRange.FormatSize
                ) {

                return STATUS_INVALID_PARAMETER;

            }

        }

        DataFormatSize = 
            sizeof (KSDATAFORMAT) + 
            KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader);

            
        //
        // If the passed buffer size is 0, it indicates that this is a size
        // only query.  Return the size of the intersecting data format and
        // pass back STATUS_BUFFER_OVERFLOW.
        //
        if (BufferSize == 0) {

            *DataSize = DataFormatSize;
            return STATUS_BUFFER_OVERFLOW;

        }
        
        //
        // Verify that the provided structure is large enough to
        // accept the result.
        //
        if (BufferSize < DataFormatSize) 
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        //
        // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader
        //
        *DataSize = DataFormatSize;
            
        FormatVideoInfoHeader = PKS_DATAFORMAT_VIDEOINFOHEADER( Data );

        //
        // Copy over the KSDATAFORMAT.  This is precisely the same as the
        // KSDATARANGE (it's just the GUIDs, etc...  not the format information
        // following any data format.
        // 
        RtlCopyMemory (
            &FormatVideoInfoHeader->DataFormat, 
            DescriptorDataRange, 
            sizeof (KSDATAFORMAT));

        FormatVideoInfoHeader->DataFormat.FormatSize = DataFormatSize;

        //
        // Copy over the callers requested VIDEOINFOHEADER
        //

        RtlCopyMemory (
            &FormatVideoInfoHeader->VideoInfoHeader, 
            &callerDataRange->VideoInfoHeader,
            KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader) 
            );

        //
        // Calculate biSizeImage for this request, and put the result in both
        // the biSizeImage field of the bmiHeader AND in the SampleSize field
        // of the DataFormat.
        //
        // Note that for compressed sizes, this calculation will probably not
        // be just width * height * bitdepth
        //
        FormatVideoInfoHeader->VideoInfoHeader.bmiHeader.biSizeImage =
            FormatVideoInfoHeader->DataFormat.SampleSize = 
            KS_DIBSIZE (FormatVideoInfoHeader->VideoInfoHeader.bmiHeader);

        //
        // REVIEW - Perform other validation such as cropping and scaling checks
        // 
        
        return STATUS_SUCCESS;
        
    } // End of VIDEOINFOHEADER specifier
    
    return STATUS_NO_MATCH;
}

/*************************************************/

BOOL
MultiplyCheckOverflow (
    ULONG a,
    ULONG b,
    ULONG *pab
    )

/*++

Routine Description:

    Perform a 32 bit unsigned multiplication and check for arithmetic overflow.

Arguments:

    a -
        First operand

    b -
        Second operand

    pab -
        Result

Return Value:

    TRUE -
        no overflow

    FALSE -
        overflow occurred

--*/

{
    PAGED_CODE();

    *pab = a * b;
    if ((a == 0) || (((*pab) / a) == b)) {
        return TRUE;
    }
    return FALSE;
}

/*************************************************/


NTSTATUS
CCapturePin::
DispatchSetFormat (
    IN PKSPIN Pin,
    IN PKSDATAFORMAT OldFormat OPTIONAL,
    IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL,
    IN const KSDATARANGE *DataRange,
    IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL
    )

/*++

Routine Description:

    This is the set data format dispatch for the capture pin.  It is called
    in two circumstances.

        1: before Pin's creation dispatch has been made to verify that
           Pin -> ConnectionFormat is an acceptable format for the range
           DataRange.  In this case OldFormat is NULL.

        2: after Pin's creation dispatch has been made and an initial format
           selected in order to change the format for the pin.  In this case,
           OldFormat will not be NULL.

    Validate that the format is acceptable and perform the actions necessary
    to change format if appropriate.

Arguments:

    Pin -
        The pin this format is being set on.  The format itself will be in
        Pin -> ConnectionFormat.

    OldFormat -
        The previous format used on this pin.  If this is NULL, it is an
        indication that Pin's creation dispatch has not yet been made and
        that this is a request to validate the initial format and not to
        change formats.

    OldAttributeList -
        The old attribute list for the prior format

    DataRange -
        A range out of our list of data ranges which was determined to be
        at least a partial match for Pin -> ConnectionFormat.  If the format
        there is unacceptable for the range, STATUS_NO_MATCH should be
        returned.

    AttributeRange -
        The attribute range

Return Value:

    Success / Failure

        STATUS_SUCCESS -
            The format is acceptable / the format has been changed

        STATUS_NO_MATCH -
            The format is not-acceptable / the format has not been changed

--*/

{

    PAGED_CODE();

    NTSTATUS Status = STATUS_NO_MATCH;

    const GUID VideoInfoSpecifier = 
        {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)};

    CCapturePin *CapPin = NULL;

    //
    // Find the pin, if it exists yet.  OldFormat will be an indication of 
    // this.  If we're changing formats, OldFormat will be non-NULL.
    //
    // You cannot use Pin -> Context to make the determination.  AVStream
    // preinitializes this to the filter's context.
    //
    if (OldFormat) {
        CapPin = reinterpret_cast <CCapturePin *> (Pin -> Context);
    }

    if (IsEqualGUID (Pin -> ConnectionFormat -> Specifier,
            VideoInfoSpecifier) &&
        Pin -> ConnectionFormat -> FormatSize >=
            sizeof (KS_DATAFORMAT_VIDEOINFOHEADER)) {

        PKS_DATAFORMAT_VIDEOINFOHEADER ConnectionFormat =
            reinterpret_cast <PKS_DATAFORMAT_VIDEOINFOHEADER> 
                (Pin -> ConnectionFormat);

        //
        // DataRange comes out of OUR data range list.  I know the range
        // is valid as such.
        //
        const KS_DATARANGE_VIDEO *VIRange =
            reinterpret_cast <const KS_DATARANGE_VIDEO *>
                (DataRange);

        //
        // Check that bmiHeader.biSize is valid since we use it later.
        //
        ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
            &ConnectionFormat -> VideoInfoHeader
            );

        ULONG DataFormatSize = FIELD_OFFSET (
            KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader
            ) + VideoHeaderSize;

        if (
            VideoHeaderSize < ConnectionFormat->
                VideoInfoHeader.bmiHeader.biSize ||
            DataFormatSize < VideoHeaderSize ||
            DataFormatSize > ConnectionFormat -> DataFormat.FormatSize
            ) {

            Status = STATUS_INVALID_PARAMETER;

        }

        //
        // Check that the format is a match for the selected range. 
        //
        else if (
            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biWidth !=
                VIRange -> VideoInfoHeader.bmiHeader.biWidth) ||

            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biHeight !=
                VIRange -> VideoInfoHeader.bmiHeader.biHeight) ||

            (ConnectionFormat -> VideoInfoHeader.bmiHeader.biCompression !=
                VIRange -> VideoInfoHeader.bmiHeader.biCompression) 

            ) {

            Status = STATUS_NO_MATCH;

        } else {

            //
            // Compute the minimum size of our buffers to validate against.
            // The image synthesis routines synthesize |biHeight| rows of
            // biWidth pixels in either RGB24 or UYVY.  In order to ensure
            // safe synthesis into the buffer, we need to know how large an
            // image this will produce.
            //
            // I do this explicitly because of the method that the data is
            // synthesized.  A variation of this may or may not be necessary
            // depending on the mechanism the driver in question fills the 
            // capture buffers.  The important thing is to ensure that they
            // aren't overrun during capture.
            //
            ULONG ImageSize;

            if (!MultiplyCheckOverflow (
                (ULONG)ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth,
                (ULONG)abs (ConnectionFormat->
                    VideoInfoHeader.bmiHeader.biHeight),
                &ImageSize
                )) {

                Status = STATUS_INVALID_PARAMETER;
            }

            //
            // We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so
            // this is valid for those formats.
            //
            else if (!MultiplyCheckOverflow (
                ImageSize,
                (ULONG)(ConnectionFormat->
                    VideoInfoHeader.bmiHeader.biBitCount / 8),
                &ImageSize
                )) {

                Status = STATUS_INVALID_PARAMETER;

            }

            //
            // Valid for the formats we use.  Otherwise, this would be
            // checked later.
            //
            else if (ConnectionFormat->VideoInfoHeader.bmiHeader.biSizeImage <
                    ImageSize) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                //
                // We can accept the format. 
                //
                Status = STATUS_SUCCESS;

                //
                // OldFormat is an indication that this is a format change.  
                // Since I do not implement the 
                // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do 
                // not handle dynamic format changes.
                //
                // If something changes while we're in the stop state, we're 
                // fine to handle it since we haven't "configured the hardware"
                // yet.
                //
                if (OldFormat) {
                    //
                    // If we're in the stop state, we can handle just about any
                    // change.  We don't support dynamic format changes. 
                    //
                    if (Pin -> DeviceState == KSSTATE_STOP) {
                        if (!CapPin -> CaptureVideoInfoHeader ()) {
                            Status = STATUS_INSUFFICIENT_RESOURCES;
                        }
                    } else {
                        //
                        // Because we don't accept dynamic format changes, we
                        // should never get here.  Just being over-protective.
                        //
                        Status = STATUS_INVALID_DEVICE_STATE;
                    }
                }

            }

        }

    }
    return Status;
}

/**************************************************************************

    LOCKED CODE

**************************************************************************/

#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA

void
CCapturePin::
CompleteMappings (
    IN ULONG NumMappings
    )

/*++

Routine Description:

    Called to notify the pin that a given number of scatter / gather
    mappings have completed.  Let the buffers go if possible.
    We're called at DPC.

Arguments:

    NumMappings -
        The number of mappings that have completed.

Return Value:

    None

--*/

{

    ULONG MappingsRemaining = NumMappings;

    //
    // Walk through the clones list and delete clones whose time has come.
    // The list is guaranteed to be kept in the order they were cloned.
    //
    PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin);

    while (MappingsRemaining && Clone) {

        PKSSTREAM_POINTER NextClone = KsStreamPointerGetNextClone (Clone);

        // 
        // If we have completed all remaining mappings in this clone, it
        // is an indication that the clone is ready to be deleted and the
        // buffer released.  Set anything required in the stream header which
        // has not yet been set.  If we have a clock, we can timestamp the
        // sample.
        //
        if (Clone -> StreamHeader -> DataUsed >= Clone -> OffsetOut.Remaining) {
            Clone -> StreamHeader -> Duration =
                m_VideoInfoHeader -> AvgTimePerFrame;

            Clone -> StreamHeader -> PresentationTime.Numerator =
                Clone -> StreamHeader -> PresentationTime.Denominator = 1;

            //
            // If a clock has been assigned, timestamp the packets with the
            // time shown on the clock. 
            //
            if (m_Clock) {

                LONGLONG ClockTime = m_Clock -> GetTime ();

                Clone -> StreamHeader -> PresentationTime.Time = ClockTime;

                Clone -> StreamHeader -> OptionsFlags =
                    KSSTREAM_HEADER_OPTIONSF_TIMEVALID |
                    KSSTREAM_HEADER_OPTIONSF_DURATIONVALID;

            } else {
	      //
	      // If there is no clock, don't time stamp the packets.
	      //
	      Clone -> StreamHeader -> PresentationTime.Time = 0;
	      
            }

            //
            // Increment the frame number.  This is the total count of frames which
            // have attempted capture.
            //
            m_FrameNumber++;

            //
            // Double check the Stream Header size.  AVStream makes no guarantee
            // that because StreamHeaderSize is set to a specific size that you
            // will get that size.  If the proper data type handlers are not 
            // installed, the stream header will be of default size.
            //
            if ( Clone -> StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) +
                sizeof (KS_FRAME_INFO)) {

                PKS_FRAME_INFO FrameInfo = reinterpret_cast <PKS_FRAME_INFO> (
                    Clone -> StreamHeader + 1
                    );
    
                FrameInfo -> ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
                FrameInfo -> dwFrameFlags       = KS_VIDEO_FLAG_FRAME;
                FrameInfo -> PictureNumber      = (LONGLONG)m_FrameNumber;

                // I don't really have a way to tell if the device has dropped a frame 
                // or was not able to send a frame on time.
                FrameInfo -> DropCount = (LONGLONG)m_DroppedFrames;
            }


            //
            // If all of the mappings in this clone have been completed,
            // delete the clone.  We've already updated DataUsed above.
            //

            MappingsRemaining--;
            KsStreamPointerDelete (Clone);

        } else {
            //
            // If only part of the mappings in this clone have been completed,
            // update the pointers.  Since we're guaranteed this won't advance
            // to a new frame by the check above, it won't fail.
            //
            (void)KsStreamPointerAdvanceOffsets (
                Clone,
                0,
                Clone -> StreamHeader -> DataUsed,
                FALSE
                );

            MappingsRemaining = 0;

        }

        //
        // Go to the next clone.
        //
        Clone = NextClone;

    }

    //
    // If we've used all the mappings in hardware and pended, we can kick
    // processing to happen again if we've completed mappings.
    //
    if (m_PendIo) {
        m_PendIo = TRUE;
        KsPinAttemptProcessing (m_Pin, TRUE);
    }

}

/**************************************************************************

    DISPATCH AND DESCRIPTOR LAYOUT

**************************************************************************/

//
// FormatRGB24Bpp_Capture:
//
// This is the data range description of the RGB24 capture format we support.
//
const 
KS_DATARANGE_VIDEO 
FormatRGB24Bpp_Capture = {

    //
    // KSDATARANGE
    //
    {   
        sizeof (KS_DATARANGE_VIDEO),                // FormatSize
        0,                                          // Flags
        D_X * D_Y * 3,                              // SampleSize
        0,                                          // Reserved

        STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO),     // aka. MEDIATYPE_Video
        0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 
            0xaf, 0x0b, 0xa7, 0x70,                 // aka. MEDIASUBTYPE_RGB24,
        STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo
    },

    TRUE,               // BOOL,  bFixedSizeSamples (all samples same size?)
    FALSE,              // BOOL,  bTemporalCompression (all I frames?)
    0,                  // Reserved (was StreamDescriptionFlags)
    0,                  // Reserved (was MemoryAllocationFlags   
                        //           (KS_VIDEO_ALLOC_*))
    //
    // _KS_VIDEO_STREAM_CONFIG_CAPS  
    //
    {
        STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID
        KS_AnalogVideo_None,                            // AnalogVideoStandard
        D_X,D_Y,        // InputSize, (the inherent size of the incoming signal
                        //             with every digitized pixel unique)
        D_X,D_Y,        // MinCroppingSize, smallest rcSrc cropping rect allowed
        D_X,D_Y,        // MaxCroppingSize, largest  rcSrc cropping rect allowed
        8,              // CropGranularityX, granularity of cropping size
        1,              // CropGranularityY
        8,              // CropAlignX, alignment of cropping rect 
        1,              // CropAlignY;
        D_X, D_Y,       // MinOutputSize, smallest bitmap stream can produce
        D_X, D_Y,       // MaxOutputSize, largest  bitmap stream can produce
        8,              // OutputGranularityX, granularity of output bitmap size
        1,              // OutputGranularityY;
        0,              // StretchTapsX  (0 no stretch, 1 pix dup, 2 interp...)
        0,              // StretchTapsY
        0,              // ShrinkTapsX 
        0,              // ShrinkTapsY 
        333667,         // MinFrameInterval, 100 nS units
        640000000,      // MaxFrameInterval, 100 nS units
        8 * 3 * 30 * D_X * D_Y,  // MinBitsPerSecond;
        8 * 3 * 30 * D_X * D_Y   // MaxBitsPerSecond;
    }, 
        
    //
    // KS_VIDEOINFOHEADER (default format)
    //
    {
        0,0,0,0,                            // RECT  rcSource; 
        0,0,0,0,                            // RECT  rcTarget; 
        D_X * D_Y * 3 * 8 * 30,             // DWORD dwBitRate;
        0L,                                 // DWORD dwBitErrorRate; 
        333667,                             // REFERENCE_TIME  AvgTimePerFrame;   
        sizeof (KS_BITMAPINFOHEADER),       // DWORD biSize;
        D_X,                                // LONG  biWidth;
        D_Y,                                // LONG  biHeight;
        1,                                  // WORD  biPlanes;
        24,                                 // WORD  biBitCount;
        KS_BI_RGB,                          // DWORD biCompression;
        D_X * D_Y * 3,                      // DWORD biSizeImage;
        0,                                  // LONG  biXPelsPerMeter;
        0,                                  // LONG  biYPelsPerMeter;
        0,                                  // DWORD biClrUsed;
        0                                   // DWORD biClrImportant;
    }
}; 

//
// FormatYUY2_Capture:
//
// This is the data range description of the YUY2 format we support.
//
const 
KS_DATARANGE_VIDEO 
FormatYUY2_Capture = {

    //
    // KSDATARANGE
    //
    {   
        sizeof (KS_DATARANGE_VIDEO),            // FormatSize
        0,                                      // Flags
        DMAX_X * DMAX_Y * 2,                    // SampleSize
        0,                                      // Reserved
        STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video
        0x32595559, 0x0000, 0x0010, 0x80, 0x00, 
        0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,     //aka. MEDIASUBTYPE_YUY2,
        STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo
    },

    TRUE,               // BOOL,  bFixedSizeSamples (all samples same size?)
    FALSE,              // BOOL,  bTemporalCompression (all I frames?)
    0,                  // Reserved (was StreamDescriptionFlags)
    0,                  // Reserved (was MemoryAllocationFlags   
                        //           (KS_VIDEO_ALLOC_*))

    //
    // _KS_VIDEO_STREAM_CONFIG_CAPS  
    //
    {
        STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID
        KS_AnalogVideo_None,                            // AnalogVideoStandard
        DMAX_X, DMAX_Y, // InputSize, (the inherent size of the incoming signal
                        //             with every digitized pixel unique)
        D_X,D_Y,        // MinCroppingSize, smallest rcSrc cropping rect allowed
        DMAX_X, DMAX_Y, // MaxCroppingSize, largest  rcSrc cropping rect allowed
        8,              // CropGranularityX, granularity of cropping size
        1,              // CropGranularityY
        8,              // CropAlignX, alignment of cropping rect 
        1,              // CropAlignY;
        D_X, D_Y,       // MinOutputSize, smallest bitmap stream can produce
        DMAX_X, DMAX_Y, // MaxOutputSize, largest  bitmap stream can produce
        8,              // OutputGranularityX, granularity of output bitmap size
        1,              // OutputGranularityY;
        0,              // StretchTapsX  (0 no stretch, 1 pix dup, 2 interp...)
        0,              // StretchTapsY
        0,              // ShrinkTapsX 
        0,              // ShrinkTapsY 
        333667,         // MinFrameInterval, 100 nS units
        640000000,      // MaxFrameInterval, 100 nS units
        8 * 2 * 30 * D_X * D_Y,  // MinBitsPerSecond;
        8 * 2 * 30 * DMAX_X * DMAX_Y,   // MaxBitsPerSecond;
    }, 
        
    //
    // KS_VIDEOINFOHEADER (default format)
    //
    {
        0, 0, 0, 0,                         // RECT  rcSource; 
        0, 0, 0, 0,                         // RECT  rcTarget; 
        DMAX_X * DMAX_Y * 2 * 8 * 30,       // DWORD dwBitRate;
        0L,                                 // DWORD dwBitErrorRate; 
        333667,                             // REFERENCE_TIME  AvgTimePerFrame;   
        sizeof (KS_BITMAPINFOHEADER),       // DWORD biSize;
        DMAX_X,                             // LONG  biWidth;
        DMAX_Y,                             // LONG  biHeight;
        1,                                  // WORD  biPlanes;
        16,                                 // WORD  biBitCount;
        FOURCC_YUY2,                        // DWORD biCompression;
        DMAX_X * DMAX_Y * 2,                // DWORD biSizeImage;
        0,                                  // LONG  biXPelsPerMeter;
        0,                                  // LONG  biYPelsPerMeter;
        0,                                  // DWORD biClrUsed;
        0                                   // DWORD biClrImportant;
    }
}; 

//
// CapturePinDispatch:
//
// This is the dispatch table for the capture pin.  It provides notifications
// about creation, closure, processing, data formats, etc...
//
const
KSPIN_DISPATCH
CapturePinDispatch = {
    CCapturePin::DispatchCreate,            // Pin Create
    NULL,                                   // Pin Close
    CCapturePin::DispatchProcess,           // Pin Process
    NULL,                                   // Pin Reset
    CCapturePin::DispatchSetFormat,         // Pin Set Data Format
    CCapturePin::DispatchSetState,          // Pin Set Device State
    NULL,                                   // Pin Connect
    NULL,                                   // Pin Disconnect
    NULL,                                   // Clock Dispatch
    NULL                                    // Allocator Dispatch
};

//
// CapturePinAllocatorFraming:
//
// This is the simple framing structure for the capture pin.  Note that this
// will be modified via KsEdit when the actual capture format is determined.
//
DECLARE_SIMPLE_FRAMING_EX (
    CapturePinAllocatorFraming,
    STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED),
    KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
        KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY,
    2,
    0,
    2 * PAGE_SIZE,
    2 * PAGE_SIZE
    );

//
// CapturePinDataRanges:
//
// This is the list of data ranges supported on the capture pin.  We support
// two: one RGB24, and one YUY2.
//
const 
PKSDATARANGE 
CapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = {
    (PKSDATARANGE) &FormatYUY2_Capture,
    (PKSDATARANGE) &FormatRGB24Bpp_Capture
    };


<|start_filename|>audio/sysvad/EndpointsCommon/a2dphpminipairs.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphpminipairs.h

Abstract:

    A2DP Sideband Audio endpoint filter definitions. 

--*/

#ifndef _SYSVAD_A2DPHPMINIPAIRS_H_
#define _SYSVAD_A2DPHPMINIPAIRS_H_

#include "a2dphpspeakertopo.h"
#include "a2dphpspeakertoptable.h"
#include "a2dphpspeakerwavtable.h"

NTSTATUS
CreateMiniportWaveRTSYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

NTSTATUS
CreateMiniportTopologySYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

/*
* A2DP Headphone speaker miniports.
*
**********************************************************************************************
* Topology/Wave bridge connection for A2DP Headphone Speaker                                    *
*                                                                                            *
*              +---------------------------------+                +-------+                  *
*              | Wave                            |                | Topo  |                  *
*              |                                 |                |       |                  *
*              |      +---------------------+    |                |       |                  *
*              |      |                     |    |                |       |                  *
* System   --->|0-----|1  HW Audio Engine  3|---2|---> Loopback   |       |                  *
*              |      |        Node         |    |                |       |                  *
* Offload  --->|1-----|2                   0|---3|===============>|0---->1|---> Line Out     *
*              |      +---------------------+    |                |       |                  *
*              |                                 |                |       |                  *
*              +---------------------------------+                +-------+                  *
*                                                                                            *
**********************************************************************************************/

static
PHYSICALCONNECTIONTABLE A2dpHpSpeakerTopologyPhysicalConnections[] =
{
    {
        KSPIN_TOPO_WAVEOUT_SOURCE,  // TopologyIn
        KSPIN_WAVE_RENDER_SOURCE,   // WaveOut
        CONNECTIONTYPE_WAVE_OUTPUT
    }
};

// A subdevice is created for each HFP device using the reference string in the form of:
// <Base_Reference_String>-<Hashcode>
// where Base_Reference_String is one of the following template names
#define A2DPHP_SPEAKER_TOPO_NAME    L"TopologyA2dpHpSpeaker"
#define A2DPHP_SPEAKER_WAVE_NAME    L"WaveA2dpHpSpeaker"
// And Hashcode is 8-character long hexadecimal value

// A reference string max length is being set to a value to accommodate them both
#define A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH 100

static
ENDPOINT_MINIPAIR A2dpHpSpeakerMiniports =
{
    eA2dpHpSpeakerDevice,
    NULL,                               // Will be generated based on A2DPHP_SPEAKER_TOPO_NAME and made unique for each side band interface
    A2DPHP_SPEAKER_TOPO_NAME,            // template name matches with KSNAME_TopologyBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &A2dpHpSpeakerTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on A2DPHP_SPEAKER_WAVE_NAME and made unique for each side band interface
    A2DPHP_SPEAKER_WAVE_NAME,            // template name matches with KSNAME_WaveBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &A2dpHpSpeakerWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    A2dpHpSpeakerPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(A2dpHpSpeakerPinDeviceFormatsAndModes),
    A2dpHpSpeakerTopologyPhysicalConnections,
    SIZEOF_ARRAY(A2dpHpSpeakerTopologyPhysicalConnections),
    ENDPOINT_OFFLOAD_SUPPORTED
};

//=============================================================================
//
// Render miniport pairs.
//
static
PENDPOINT_MINIPAIR  g_A2dpHpRenderEndpoints[] = 
{
    &A2dpHpSpeakerMiniports
};

#define g_cA2dpHpRenderEndpoints  (SIZEOF_ARRAY(g_A2dpHpRenderEndpoints))

// If the count of Render endpoints changes, update formula below
C_ASSERT(g_cA2dpHpRenderEndpoints == 1);

//=============================================================================
//
// Total miniports = # endpoints (topology + wave)
//
// 
// Allow 5 A2DP Headphone Endpoints.
//
#define MAX_A2DPHS_DEVICES      (5)
#define g_MaxA2dpHpMiniports    ((g_cA2dpHpRenderEndpoints) * 2 * MAX_A2DPHS_DEVICES)

// g_MaxA2dpHpMiniports is used when calculating the MaxObjects inside AddDevice.
C_ASSERT(g_MaxA2dpHpMiniports == 10);

#endif // _SYSVAD_A2DPHPMINIPAIRS_H_



<|start_filename|>audio/simpleaudiosample/Source/Filters/speakertopo.h<|end_filename|>

/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    speakertopo.h

Abstract:

    Declaration of topology miniport for the speaker (internal).
--*/

#ifndef _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_
#define _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_

NTSTATUS PropertyHandler_SpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest);

NTSTATUS PropertyHandler_SpeakerTopology(_In_ PPCPROPERTY_REQUEST PropertyRequest);

#endif // _SIMPLEAUDIOSAMPLE_SPEAKERTOPO_H_


<|start_filename|>usb/usbview/langidlist.h<|end_filename|>
/*++

Copyright (c) 2003-2008 Microsoft Corporation

Module Name:

    LANGIDLIST.H

Abstract:

    This file LANGIDLIST.H contains content from USB.org, and was reviewed
    by LCA in June 2011. Per discussion with USB consortium counsel their
    material is "free to any use".

    This header file contains a list of all currently known USB Language IDs
    and the language name associated with each Language ID.



Source:
    http://www.usb.org

Environment:

    Kernel & user mode

Revision History:

    03-28-03 : created

--*/

#ifndef   __LANGIDLIST_H__
#define   __LANGIDLIST_H__

//
// Language ID structure
//
typedef struct {
    USHORT  usLangID;
    PCHAR   szLanguage;
} USBLANGID, *PUSBLANGID;

//
// This list built from information obtained on Nov-30-2000 from
// http://www.usb.org
//
// This information has not been independently verified and no claims
// are made here as to its accuracy.
//

USBLANGID USBLangIDs[] =
{
    {1078   , /*    0x0436  */ "Afrikaans"},
    {1052   , /*    0x041c  */ "Albanian"},
    {1025   , /*    0x0401  */ "Arabic (Saudi Arabia)"},
    {2049   , /*    0x0801  */ "Arabic (Iraq)"},
    {3073   , /*    0x0c01  */ "Arabic (Egypt)"},
    {4097   , /*    0x1001  */ "Arabic (Libya)"},
    {5121   , /*    0x1401  */ "Arabic (Algeria)"},
    {6145   , /*    0x1801  */ "Arabic (Morocco)"},
    {7169   , /*    0x1c01  */ "Arabic (Tunisia)"},
    {8193   , /*    0x2001  */ "Arabic (Oman)"},
    {9217   , /*    0x2401  */ "Arabic (Yemen)"},
    {10241  , /*    0x2801  */ "Arabic (Syria)"},
    {11265  , /*    0x2c01  */ "Arabic (Jordan)"},
    {12289  , /*    0x3001  */ "Arabic (Lebanon)"},
    {13313  , /*    0x3401  */ "Arabic (Kuwait)"},
    {14337  , /*    0x3801  */ "Arabic (U.A.E.)"},
    {15361  , /*    0x3c01  */ "Arabic (Bahrain)"},
    {16385  , /*    0x4001  */ "Arabic (Qatar)  "},
    {1067   , /*    0x042b  */ "Armenian"},
    {1101   , /*    0x044d  */ "Assamese"},
    {1068   , /*    0x042c  */ "Azeri (Latin)"},
    {2092   , /*    0x082c  */ "Azeri (Cyrillic)"},
    {1069   , /*    0x042d  */ "Basque"},
    {1059   , /*    0x0423  */ "Belarussian"},
    {1093   , /*    0x0445  */ "Bengali"},
    {1026   , /*    0x0402  */ "Bulgarian"},
    {1109   , /*    0x0455  */ "Burmese"},
    {1027   , /*    0x0403  */ "Catalan"},
    {1028   , /*    0x0404  */ "Chinese (Taiwan)"},
    {2052   , /*    0x0804  */ "Chinese (PRC)"},
    {3076   , /*    0x0c04  */ "Chinese (Hong Kong SAR, PRC)"},
    {4100   , /*    0x1004  */ "Chinese (Singapore)"},
    {5124   , /*    0x1404  */ "Chinese (MACAO SAR)"},
    {1050   , /*    0x041a  */ "Croatian"},
    {1029   , /*    0x0405  */ "Czech"},
    {1030   , /*    0x0406  */ "Danish"},
    {1043   , /*    0x0413  */ "Dutch (Netherlands)"},
    {2067   , /*    0x0813  */ "Dutch (Belgium)"},
    {1033   , /*    0x0409  */ "English (United States)"},
    {2057   , /*    0x0809  */ "English (United Kingdom)"},
    {3081   , /*    0x0c09  */ "English (Australian)"},
    {4105   , /*    0x1009  */ "English (Canadian)"},
    {5129   , /*    0x1409  */ "English (New Zealand)"},
    {6153   , /*    0x1809  */ "English (Ireland)"},
    {7177   , /*    0x1c09  */ "English (South Africa)"},
    {8201   , /*    0x2009  */ "English (Jamaica)"},
    {9225   , /*    0x2409  */ "English (Caribbean)"},
    {10249  , /*    0x2809  */ "English (Belize)"},
    {11273  , /*    0x2c09  */ "English (Trinidad)"},
    {12297  , /*    0x3009  */ "English (Zimbabwe)"},
    {13321  , /*    0x3409  */ "English (Philippines)"},
    {1061   , /*    0x0425  */ "Estonian"},
    {1080   , /*    0x0438  */ "Faeroese"},
    {1065   , /*    0x0429  */ "Farsi   "},
    {1035   , /*    0x040b  */ "Finnish"},
    {1036   , /*    0x040c  */ "French (Standard)"},
    {2060   , /*    0x080c  */ "French (Belgian)"},
    {3084   , /*    0x0c0c  */ "French (Canadian)"},
    {4108   , /*    0x100c  */ "French (Switzerland)"},
    {5132   , /*    0x140c  */ "French (Luxembourg)"},
    {6156   , /*    0x180c  */ "French (Monaco)"},
    {1079   , /*    0x0437  */ "Georgian"},
    {1031   , /*    0x0407  */ "German (Standard)"},
    {2055   , /*    0x0807  */ "German (Switzerland)"},
    {3079   , /*    0x0c07  */ "German (Austria)"},
    {4103   , /*    0x1007  */ "German (Luxembourg)"},
    {5127   , /*    0x1407  */ "German (Liechtenstein)"},
    {1032   , /*    0x0408  */ "Greek"},
    {1095   , /*    0x0447  */ "Gujarati"},
    {1037   , /*    0x040d  */ "Hebrew"},
    {1081   , /*    0x0439  */ "Hindi"},
    {1038   , /*    0x040e  */ "Hungarian"},
    {1039   , /*    0x040f  */ "Icelandic"},
    {1057   , /*    0x0421  */ "Indonesian"},
    {1040   , /*    0x0410  */ "Italian (Standard)"},
    {2064   , /*    0x0810  */ "Italian (Switzerland)"},
    {1041   , /*    0x0411  */ "Japanese"},
    {1099   , /*    0x044b  */ "Kannada"},
    {2144   , /*    0x0860  */ "Kashmiri (India)"},
    {1087   , /*    0x043f  */ "Kazakh"},
    {1111   , /*    0x0457  */ "Konkani"},
    {1042   , /*    0x0412  */ "Korean"},
    {2066   , /*    0x0812  */ "Korean (Johab)"},
    {1062   , /*    0x0426  */ "Latvian"},
    {1063   , /*    0x0427  */ "Lithuanian"},
    {2087   , /*    0x0827  */ "Lithuanian (Classic)"},
    {1086   , /*    0x043e  */ "Malay (Malaysian)"},
    {2110   , /*    0x083e  */ "Malay (Brunei Darussalam)"},
    {1100   , /*    0x044c  */ "Malayalam"},
    {1112   , /*    0x0458  */ "Manipuri"},
    {1102   , /*    0x044e  */ "Marathi"},
    {2145   , /*    0x0861  */ "Nepali (India)"},
    {1071   , /*    0x042f  */ "North Macedonia"},
    {1044   , /*    0x0414  */ "Norwegian (Bokmal)"},
    {2068   , /*    0x0814  */ "Norwegian (Nynorsk)"},
    {1096   , /*    0x0448  */ "Odia"},
    {1045   , /*    0x0415  */ "Polish"},
    {1046   , /*    0x0416  */ "Portuguese (Brazil)"},
    {2070   , /*    0x0816  */ "Portuguese (Portugal)"},
    {1094   , /*    0x0446  */ "Punjabi"},
    {1048   , /*    0x0418  */ "Romanian"},
    {1049   , /*    0x0419  */ "Russian"},
    {1103   , /*    0x044f  */ "Sanskrit"},
    {3098   , /*    0x0c1a  */ "Serbian (Cyrillic)"},
    {2074   , /*    0x081a  */ "Serbian (Latin)"},
    {1113   , /*    0x0459  */ "Sindhi"},
    {1051   , /*    0x041b  */ "Slovak"},
    {1060   , /*    0x0424  */ "Slovenian"},
    {1034   , /*    0x040a  */ "Spanish (Traditional Sort)"},
    {2058   , /*    0x080a  */ "Spanish (Mexican)"},
    {3082   , /*    0x0c0a  */ "Spanish (Modern Sort)"},
    {4106   , /*    0x100a  */ "Spanish (Guatemala)"},
    {5130   , /*    0x140a  */ "Spanish (Costa Rica)"},
    {6154   , /*    0x180a  */ "Spanish (Panama)"},
    {7178   , /*    0x1c0a  */ "Spanish (Dominican Republic)"},
    {8202   , /*    0x200a  */ "Spanish (Venezuela)"},
    {9226   , /*    0x240a  */ "Spanish (Colombia)"},
    {10250  , /*    0x280a  */ "Spanish (Peru)"},
    {11274  , /*    0x2c0a  */ "Spanish (Argentina)"},
    {12298  , /*    0x300a  */ "Spanish (Ecuador)"},
    {13322  , /*    0x340a  */ "Spanish (Chile)"},
    {14346  , /*    0x380a  */ "Spanish (Uruguay)"},
    {15370  , /*    0x3c0a  */ "Spanish (Paraguay)"},
    {16394  , /*    0x400a  */ "Spanish (Bolivia)"},
    {17418  , /*    0x440a  */ "Spanish (El Salvador)"},
    {18442  , /*    0x480a  */ "Spanish (Honduras)"},
    {19466  , /*    0x4c0a  */ "Spanish (Nicaragua)"},
    {20490  , /*    0x500a  */ "Spanish (Puerto Rico)"},
    {1072   , /*    0x0430  */ "Sutu"},
    {1089   , /*    0x0441  */ "Swahili (Kenya)"},
    {1053   , /*    0x041d  */ "Swedish"},
    {2077   , /*    0x081d  */ "Swedish (Finland)"},
    {1097   , /*    0x0449  */ "Tamil"},
    {1092   , /*    0x0444  */ "Tatar (Tatarstan)"},
    {1098   , /*    0x044a  */ "Telugu"},
    {1054   , /*    0x041e  */ "Thai"},
    {1055   , /*    0x041f  */ "Turkish"},
    {1058   , /*    0x0422  */ "Ukrainian"},
    {1056   , /*    0x0420  */ "Urdu (Pakistan)"},
    {2080   , /*    0x0820  */ "Urdu (India)"},
    {1091   , /*    0x0443  */ "Uzbek (Latin)"},
    {2115   , /*    0x0843  */ "Uzbek (Cyrillic)"},
    {1066   , /*    0x042a  */ "Vietnamese"},
    {1279   , /*    0x04ff  */ "HID (Usage Data Descriptor)"},
    {61695  , /*    0xf0ff  */ "HID (Vendor Defined 1)"},
    {62719  , /*    0xf4ff  */ "HID (Vendor Defined 2)"},
    {63743  , /*    0xf8ff  */ "HID (Vendor Defined 3)"},
    {64767  , /*    0xfcff  */ "HID (Vendor Defined 4)"},
    { 0x00, "End"}
};

#endif /* __LANGIDLIST_H__ */



<|start_filename|>audio/tests/PinResourceHelpers/PropertyHelper.cpp<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
//  PropertyHelper.cpp
//
// Abstract:
//
//  Implementation for common helpers of endpoint property
//
// -------------------------------------------------------------------------------
#include "PreComp.h"
#include <AVEndpointKeys.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <devpkey.h>
#include <PropertyHelper.h>
#include <imagehlp.h>

using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetConnectorId
//
// Retrieve the connector id from the property store and checks if the connector exists
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetConnectorId
(
    IMMDevice*              pDevice,
    EndpointConnectorType   eConnectorType,
    bool*                   hasConnector,
    UINT*                   pConnectorId
)

















































///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetEndpointFriendlyName
//
// Retrieve the endpoint friendly name from the property store
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetEndpointFriendlyName
(
    IMMDevice*              pDevice,
    LPWSTR*                 ppwszEndpointName
)
{
    HRESULT                                     hr = S_OK;
    wil::com_ptr_nothrow<IPropertyStore>        spPropertyStore;
    wil::unique_prop_variant                    var;
    wil::unique_cotaskmem_string                pwszName;

    *ppwszEndpointName = NULL;

    // Retrieve the endpoint friendly name from the propertey store
    if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) {
        return hr;
    }
    if (!VERIFY_SUCCEEDED(hr = spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var))) {
        return hr;
    }
    if (!VERIFY_IS_TRUE(var.vt == VT_LPWSTR)) {
        hr = E_FAIL;
        return hr;
    }

    pwszName = wil::make_cotaskmem_string_nothrow(var.pwszVal);
    if (!VERIFY_IS_TRUE(pwszName.get() && wcslen(pwszName.get()) != 0)) {
        hr = E_FAIL;
        return hr;
    }

    *ppwszEndpointName = pwszName.release();

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetAudioFilterAsDevice
//
// Get the audio filter from MMDevice. This method is called to activate IKsControl, IKsFormatSupport or IKsGetProposedFormat.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetAudioFilterAsDevice
(
    IMMDevice* pDevice,
    IMMDevice** ppAudioFilterAsDevice
)



































///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetCachedProcessingModes
//
// Read the processing mode characteristics from the property store and get all processing modes. This method applies to host and keyword detector pins only.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetCachedProcessingModes
(
    IMMDevice*              pDevice,
    EndpointConnectorType   eConnectorType,
    ULONG                   *pCount,
    AUDIO_SIGNALPROCESSINGMODE **ppModes
)
















































































































///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetProcessingModes
//
// Query for connector's processing modes property
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetProcessingModes
(
    IMMDevice*              pDevice,
    UINT                    pinId,
    ULONG                   *pCount,
    AUDIO_SIGNALPROCESSINGMODE **ppModes
)
{
    HRESULT                                     hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>             spAudioFilter;
    wil::com_ptr_nothrow<IKsControl>            spKsControl;
    KSP_PIN                                     pinProp;
    ULONG                                       cbNeeded = 0;
    ULONG                                       cbProperty;
    PKSMULTIPLE_ITEM                            pProperty = NULL;
    ULONG                                       cbReturned = 0;
    AUDIO_SIGNALPROCESSINGMODE*                 pModes = NULL;
    CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE>     spModes;

    *pCount = 0;
    *ppModes = NULL;

    // Get the audio filter
    if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
        return hr;
    }

    // Activate IKsControl
    if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) {
        return hr;
    }

    // Read the audio signal processing mode property
    pinProp.Property.Set = KSPROPSETID_AudioSignalProcessing;
    pinProp.Property.Id = KSPROPERTY_AUDIOSIGNALPROCESSING_MODES;
    pinProp.Property.Flags = KSPROPERTY_TYPE_GET;
    pinProp.PinId = pinId & PARTID_MASK;
    pinProp.Reserved = 0;

    // Determined the needed size
    if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), NULL, 0, &cbNeeded))) {
        return hr;
    }
    if (!VERIFY_IS_TRUE(cbNeeded > 0) || !VERIFY_IS_TRUE((cbNeeded - sizeof(KSMULTIPLE_ITEM)) % sizeof(GUID) == 0)) {
        hr = E_NOTFOUND;
        return hr;
    }

    cbProperty = cbNeeded;
    pProperty = (PKSMULTIPLE_ITEM)CoTaskMemAlloc(cbNeeded);

    if (!VERIFY_IS_NOT_NULL(pProperty)) {
        hr = E_OUTOFMEMORY;
        cbProperty = 0;
        return hr;
    }

    // Query the processing mode property
    if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), pProperty, cbNeeded, &cbReturned))) {
        goto Exit;
    }

    if (!VERIFY_IS_NOT_NULL(pProperty)) {
        hr = E_NOTFOUND;
        goto Exit;
    }

    if (!VERIFY_IS_TRUE(cbProperty >= (sizeof(KSMULTIPLE_ITEM) + pProperty->Count * sizeof(GUID)))) {
        hr = E_NOTFOUND;
        goto Exit;
    }

    pModes = reinterpret_cast<GUID*>(pProperty + 1);

    if (!VERIFY_IS_TRUE(spModes.Allocate(pProperty->Count)))
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    memcpy(spModes, pModes, sizeof(AUDIO_SIGNALPROCESSINGMODE)*(pProperty->Count));

    *pCount = pProperty->Count;
    *ppModes = spModes.Detach();

Exit:
    if (pProperty != NULL) {
        CoTaskMemFree(pProperty);
    }
    cbProperty = 0;

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetCachedSupportedFormatRecords
//
// Read the processing mode characteristics from the property store and get all supported formats for processing mode. This method applies to host and keyword detector pins only.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetCachedSupportedFormatRecords
(
    IMMDevice*              pDevice,
    EndpointConnectorType   eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE mode,
    ULONG *pCount,
    FORMAT_RECORD **ppFormatRecords
)























































































































































///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetSupportedFormatRecords
//
// For pins do not have cached supported formats information, such as offload pin, we provide our predefined format list and check whether each is supported by the pin.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetSupportedFormatRecords
(
    IMMDevice*                  pDevice,
    UINT                        pinId,
    EndpointConnectorType       eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    STACKWISE_DATAFLOW          dataFlow,
    ULONG                       *pCount,
    FORMAT_RECORD               **ppFormatRecords
)
{
    HRESULT                             hr = S_OK;
    FORMAT_RECORD                       *pFormatRecords;
    UINT                                cFormatRecords = 0;
    CComHeapPtr<FORMAT_RECORD>          spFormatRecords;
    BOOL                                isSupported = false;
    WAVEFORMATEX                        *pWfx;
    UINT32                              u32DefaultPeriodicityInFrames = 0;
    UINT32                              u32FundamentalPeriodicityInFrames = 0;
    UINT32                              u32MinPeriodicityInFrames = 0;
    UINT32                              u32MaxPeriodicityInFrames = 0;
    UINT32                              u32MaxPeriodicityInFramesExtended = 0;

    // Init return
    *pCount = 0;
    *ppFormatRecords = NULL;

    pFormatRecords = new FORMAT_RECORD[COUNT_FORMATS];
    if (!VERIFY_IS_NOT_NULL(pFormatRecords)) {
        hr = E_OUTOFMEMORY;
        return hr;
    }
    memset(pFormatRecords, 0, sizeof(FORMAT_RECORD)*COUNT_FORMATS);

    for (ULONG i = 0; i < COUNT_FORMATS; i++) {
        pWfx = (WAVEFORMATEX*)&ListofFormats[i];

        // Check if format is supported
        if (!VERIFY_SUCCEEDED(hr = IsFormatSupported(pDevice, pinId, pWfx, &isSupported))) {
            return hr;
        }

        // If the format is supported, we discover the periodicity characteristics for the format and store them along with the format in our FORMAT_RECORD struct.
        if (isSupported) {
            if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pWfx, dataFlow, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) {
                return hr;
            }

            pFormatRecords[cFormatRecords].defaultPeriodInFrames = u32DefaultPeriodicityInFrames;
            pFormatRecords[cFormatRecords].fundamentalPeriodInFrames = u32FundamentalPeriodicityInFrames;
            pFormatRecords[cFormatRecords].minPeriodInFrames = u32MinPeriodicityInFrames;
            pFormatRecords[cFormatRecords].maxPeriodInFrames = u32MaxPeriodicityInFrames;
            pFormatRecords[cFormatRecords].maxPeriodInFramesExtended = u32MaxPeriodicityInFramesExtended;
            memcpy(&pFormatRecords[cFormatRecords].wfxEx, &ListofFormats[i], sizeof(WAVEFORMATEX) + ListofFormats[i].Format.cbSize);
            cFormatRecords++;
        }
    }

    if (!VERIFY_IS_TRUE(spFormatRecords.Allocate(cFormatRecords))) {
        hr = E_OUTOFMEMORY;
        return hr;
    }

    memcpy(spFormatRecords, pFormatRecords, sizeof(FORMAT_RECORD)*cFormatRecords);
    delete[] pFormatRecords;

    *pCount = cFormatRecords;
    *ppFormatRecords = spFormatRecords.Detach();

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsFormatSupported
//
// Wrap up the IKsFormatSupport::IsFormatSupported method.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsFormatSupported
(
    IMMDevice*              pDevice,
    UINT                    pinId,
    WAVEFORMATEX            *pWfx,
    BOOL*                   pbSupported
)
{
    HRESULT                                 hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>         spAudioFilter;
    wil::com_ptr_nothrow<IDeviceTopology>   spAudioFilterTopology;
    wil::com_ptr_nothrow<IPart>             spPart;
    wil::com_ptr_nothrow<IKsFormatSupport>  spFormatSupport;
    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE      pKsRequestedFormat = NULL;
    PKSDATAFORMAT                           pKsFormat = NULL;
    KSDATAFORMAT_WAVEFORMATEXTENSIBLE       KsWfex = {};
    DWORD                                   cbKsFormat = 0;

    // Init return
    *pbSupported = FALSE;

    // Get the audio filter
    if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
        return hr;
    }

    // Activate an IDeviceTopology on the filter
    if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) {
        return hr;
    }

    // Get the connector
    if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) {
        return hr;
    }

    // Activate IKsFormatSupport interface
    if (!VERIFY_SUCCEEDED(hr = spPart->Activate(CLSCTX_ALL, __uuidof(IKsFormatSupport), (void**)&spFormatSupport))) {
        return hr;
    }

    // Convert to KSDATAFORMAT_WAVEFORMATEX
    if (!VERIFY_SUCCEEDED(hr = CreateKSFormatFromWFXFormat(pWfx, (KSDATAFORMAT_WAVEFORMATEX**)&pKsRequestedFormat))) {
        return hr;
    }

    if (pKsRequestedFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE))
    {
        memcpy(&KsWfex, pKsRequestedFormat, pKsRequestedFormat->DataFormat.FormatSize);
        pKsFormat = reinterpret_cast<KSDATAFORMAT *>(&KsWfex);
        cbKsFormat = sizeof(KsWfex);
    }
    else
    {
        pKsFormat = reinterpret_cast<KSDATAFORMAT *>(pKsRequestedFormat);
        cbKsFormat = pKsRequestedFormat->DataFormat.FormatSize;
    }

    // Expect that pbFormat is really a KSDATAFORMAT
    if (!VERIFY_IS_TRUE(cbKsFormat >= sizeof(KSDATAFORMAT))) {
        hr = E_INVALIDARG;
        return hr;
    }

    // validate return pointers (this is an externally callable method)
    if (!VERIFY_IS_TRUE(pKsFormat)) {
        hr = E_POINTER;
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = spFormatSupport->IsFormatSupported(pKsFormat, cbKsFormat, pbSupported))) {
        return hr;
    }

    CoTaskMemFree(pKsRequestedFormat);

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DiscoverPeriodicityCharacteristicsForFormat
//
// Discover the periodicity characteristics for given format.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT DiscoverPeriodicityCharacteristicsForFormat
(
    IMMDevice*                  pDevice,
    EndpointConnectorType       eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    WAVEFORMATEX                *pWfx,
    STACKWISE_DATAFLOW          dataFlow,
    UINT32                      *pDefaultPeriodicityInFrames,
    UINT32                      *pFundamentalPeriodicityInFrames,
    UINT32                      *pMinPeriodicityInFrames,
    UINT32                      *pMaxPeriodicityInFrames,
    UINT32                      *pMaxPeriodicityInFramesExtended
)
{
    HRESULT                                     hr = S_OK;
    HNSTIME                                     hnsDefaultPeriod = kSystemDefaultPeriod;
    UINT32                                      ActualPeriodicityInFrames;

    // Verify format is not null
    if (!VERIFY_IS_NOT_NULL(pWfx)) {
        hr = E_INVALIDARG;
        return hr;
    }

    *pDefaultPeriodicityInFrames =
        *pFundamentalPeriodicityInFrames =
        *pMinPeriodicityInFrames =
        *pMaxPeriodicityInFrames = HNSTIME_TO_FRAMES_DOUBLE(hnsDefaultPeriod, pWfx->nSamplesPerSec);

    // Check to see whether the endpoint supports the default periodicity and return the actual periodicity
    if (!VERIFY_SUCCEEDED(hr = CheckConnectorSupportForPeriodicity(pDevice, eConnectorType, mode, pWfx, dataFlow, hnsDefaultPeriod, &ActualPeriodicityInFrames))) {
        return hr;
    }

    // Use what we get as the default, max, min and fundamental periodicity
    *pDefaultPeriodicityInFrames =
        *pFundamentalPeriodicityInFrames =
        *pMinPeriodicityInFrames =
        *pMaxPeriodicityInFrames = ActualPeriodicityInFrames;

    // Max periodicity is capped to the default periodicity for all clients except extended periodicity clients
    *pMaxPeriodicityInFramesExtended = *pMaxPeriodicityInFrames;
    if (*pMaxPeriodicityInFrames > *pDefaultPeriodicityInFrames)
    {
        *pMaxPeriodicityInFrames = *pDefaultPeriodicityInFrames;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CheckConnectorSupportForPeriodicity
//
// Check to see if the connector supports the specified periodicity and return the actual periodicity.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT CheckConnectorSupportForPeriodicity
(
    IMMDevice*                  pDevice,
    EndpointConnectorType       eConnectorType,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    WAVEFORMATEX                *pWfx,
    STACKWISE_DATAFLOW          dataFlow,
    HNSTIME                     RequestedPeriodicity,
    UINT32                      *pActualPeriodicityInFrames
)









































































































































///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetCachedDefaultFormat
//
// Read the default device format from the property store. Use audio engine device format for all other pin types. For Keyword Detector pin, it has a different default device format.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetCachedDefaultFormat
(
    IMMDevice*              pDevice,
    EndpointConnectorType   eConnectorType,
    WAVEFORMATEX**          ppDefaultFormat
)






























///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetProposedFormatForProcessingMode
//
// Wrap up the IKsGetProposedFormat::GetProposedFormatForMode method.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetProposedFormatForProcessingMode
(
    IMMDevice*                  pDevice,
    UINT                        pinId,
    AUDIO_SIGNALPROCESSINGMODE  mode,
    WAVEFORMATEX                **ppProposedFormat
)
{
    HRESULT                                     hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>             spAudioFilter;
    wil::com_ptr_nothrow<IDeviceTopology>       spAudioFilterTopology;
    wil::com_ptr_nothrow<IPart>                 spPart;



    PKSDATAFORMAT                               pKsProposedFormat = NULL;
    WAVEFORMATEXTENSIBLE*                       pWfxEx = NULL;

    // Init return
    *ppProposedFormat = NULL;

    if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
        return hr;
    }

    // Activate an IDeviceTopology on the filter
    if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) {
        return hr;
    }

    // Get the connector
    if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) {
        return hr;
    }

    // Retrieve the proposed format for a specific processing mode and store it in pKsProposedFormat...












    // Allocate memory for ppOffloadDefaultFormat and copy pKsDataDeviceFormat to ppOffloadDefaultFormat
    pWfxEx = (WAVEFORMATEXTENSIBLE *)CoTaskMemAlloc(sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize);
    if (!VERIFY_IS_NOT_NULL(pWfxEx)) {
        CoTaskMemFree(pKsProposedFormat);
        hr = E_OUTOFMEMORY;
        return hr;
    }
    memcpy(pWfxEx, &(((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx), sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize);
    *ppProposedFormat = (WAVEFORMATEX*)pWfxEx;

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetCurrentAvailiablePinInstanceCount
//
// Query for the connector's pin instance count property.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetAvailiablePinInstanceCount
(
    IMMDevice*          pDevice,
    UINT                pinId,
    UINT32*             pAvailablePinInstanceCount
)
{
    HRESULT                                     hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>             spAudioFilter;
    wil::com_ptr_nothrow<IKsControl>            spKsControl;
    KSP_PIN                                     pinProp;
    KSPIN_CINSTANCES                            pinInstances;
    ULONG                                       cbReturned = 0;

    *pAvailablePinInstanceCount = 0;

    if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) {
        return hr;
    }

    // Read the pin instance count
    pinProp.Property.Set = KSPROPSETID_Pin;
    pinProp.Property.Id = KSPROPERTY_PIN_CINSTANCES;
    pinProp.Property.Flags = KSPROPERTY_TYPE_GET;
    pinProp.PinId = pinId & PARTID_MASK;
    pinProp.Reserved = 0;

    if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), &pinInstances, sizeof(KSPIN_CINSTANCES), &cbReturned))) {
        return hr;
    }

    *pAvailablePinInstanceCount = pinInstances.PossibleCount - pinInstances.CurrentCount;

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetDriverPathViaService
//
// Get the full path to the driver .sys file by the service name
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetDriverPathViaService
(
    LPWSTR             ServiceName,
    LPWSTR             DriverFullPath,
    UINT               cchFullPath
)
{
    HRESULT                             hr = S_OK;
    wil::unique_schandle                scManager;
    wil::unique_schandle                scService;

    scManager = wil::unique_schandle(OpenSCManagerW(nullptr, nullptr, GENERIC_READ));
    if (!VERIFY_IS_NOT_NULL(scManager.get()))
    {
        hr = E_OUTOFMEMORY;
        return hr;
    }

    scService = wil::unique_schandle(OpenServiceW(scManager.get(), ServiceName, GENERIC_READ));
    if (!VERIFY_IS_NOT_NULL(scService.get()))
    {
        hr = E_OUTOFMEMORY;
        return hr;
    }

    DWORD BytesRequired;
    if (!VERIFY_IS_TRUE(!QueryServiceConfigW(scService.get(), nullptr, 0, &BytesRequired) && ERROR_INSUFFICIENT_BUFFER == GetLastError()))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    wil::unique_cotaskmem_ptr<BYTE>     pBuff;
    pBuff.reset((BYTE*)CoTaskMemAlloc(BytesRequired));
    if (!VERIFY_IS_NOT_NULL(pBuff))
    {
        hr = E_OUTOFMEMORY;
        return hr;
    }

    LPQUERY_SERVICE_CONFIGW ServiceConfig = (LPQUERY_SERVICE_CONFIGW)pBuff.get();
    DWORD BytesReturned;

    if (!VERIFY_IS_TRUE(QueryServiceConfigW(scService.get(), ServiceConfig, BytesRequired, &BytesReturned)))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    if (!VERIFY_IS_NOT_NULL(ServiceConfig->lpBinaryPathName))
    {
        hr = E_UNEXPECTED;
        return hr;
    }

    if (!VERIFY_IS_TRUE('\0' != ServiceConfig->lpBinaryPathName[0]))
    {
        hr = E_UNEXPECTED;
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = GetFullPathFromImagePath(ServiceConfig->lpBinaryPathName, DriverFullPath, cchFullPath)))
    {
        return hr;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetFullPathFromImagePath
//
// Get the full driver path from the image path
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetFullPathFromImagePath
(
    LPWSTR              ImagePath,
    LPWSTR              DriverFullPath,
    UINT                cchFullPath
)
{
    HRESULT hr = S_OK;
    //
    // First, check if the ImagePath uses either of the well-known kernel
    // DosDevices prefixes.  If so, skip over those first since
    // GetFileAttributes would succeed, leading us to think it is a valid DOS
    // path, and GetFullPathName handles these incorrectly anyways.  Don't
    // bother handling the user DosDevices formats since it would be invalid to
    // specify that format for the ImagePath of a kernel module.
    //
    LPWSTR pImagePath = (LPWSTR)ImagePath;

    const wchar_t* DosDevicesPath =  L"\\DosDevices\\";
    const wchar_t* QuestionPath = L"\\??\\";
    if (_wcsnicmp(pImagePath,
        DosDevicesPath,
        static_cast<int>(wcslen(DosDevicesPath))) == 0)
    {
        pImagePath += wcslen(DosDevicesPath);
    }
    else if (_wcsnicmp(pImagePath,
        QuestionPath,
        static_cast<int>(wcslen(QuestionPath))) == 0)
    {
        pImagePath += wcslen(QuestionPath);
    }

    //
    // Check if the ImagePath happens to be a valid full path.
    //
    if (GetFileAttributesW(pImagePath) != 0xFFFFFFFF)
    {
        if (!VERIFY_IS_TRUE(GetFullPathNameW(pImagePath, cchFullPath, DriverFullPath, nullptr)))
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            return hr;
        }
        return hr;
    }

    //
    // If the ImagePath starts with "\SystemRoot" or "%SystemRoot%" then
    // remove those values.
    //
    LPWSTR pRelativePath = (LPWSTR)pImagePath;

    const wchar_t* SystemRootPath = L"\\SystemRoot\\";
    const wchar_t* SystemRootVariablePath = L"%SystemRoot%\\";

    if (_wcsnicmp(pRelativePath,
        SystemRootPath,
        static_cast<int>(wcslen(SystemRootPath))) == 0)
    {
        pRelativePath += wcslen(SystemRootPath);
    }
    else if (_wcsnicmp(pRelativePath,
        SystemRootVariablePath,
        static_cast<int>(wcslen(SystemRootVariablePath))) == 0)
    {
        pRelativePath += wcslen(SystemRootVariablePath);
    }

    //
    // At this point pRelativePath should point to the image path relative to
    // the windows directory.
    //
    WCHAR WindowsPath[MAX_PATH];
    if (!VERIFY_IS_TRUE(GetSystemWindowsDirectoryW(WindowsPath, MAX_PATH)))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = StringCchPrintfW(DriverFullPath, cchFullPath, L"%s\\%s", WindowsPath, pRelativePath)))
    {
        return hr;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CheckImports
//
// Check if the driver imports a specific module
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT CheckImports
(
    LPWSTR              DriverPath,
    LPSTR               ModuleNameToCheck,
    LPSTR               MethodNameToCheck,
    bool*               pIsImported
)
{
    HRESULT hr = S_OK;

    *pIsImported = FALSE;

    CHAR szDriverPath[MAX_PATH];
    size_t bytesConverted = 0;
    if (!VERIFY_IS_TRUE(0 == wcstombs_s(&bytesConverted, szDriverPath, MAX_PATH, DriverPath, _TRUNCATE)))
    {
        hr = E_FAIL;
        return hr;
    }

    LOADED_IMAGE image;
    //Load the image
    if (!VERIFY_IS_TRUE(MapAndLoad(szDriverPath, NULL, &image, TRUE, TRUE)))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    auto imageCleanup = wil::scope_exit([&]()
    {
        (VOID)UnMapAndLoad(&image);
    });

    //Get the Import Directory
    ULONG importDescriptorSize;
    PIMAGE_SECTION_HEADER sectionHeader;
    PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(image.MappedAddress,
        FALSE,
        IMAGE_DIRECTORY_ENTRY_IMPORT,
        &importDescriptorSize,
        &sectionHeader);
    if (!VERIFY_IS_NOT_NULL(importDescriptor))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    //Iterate through each directory entry
    while (!(importDescriptor->Characteristics == 0 &&
        importDescriptor->TimeDateStamp == 0 &&
        importDescriptor->ForwarderChain == 0 &&
        importDescriptor->Name == 0 &&
        importDescriptor->FirstThunk == 0))
    {
        CHAR* str = (PCHAR)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
            image.MappedAddress,
            importDescriptor->Name,
            NULL);
        if (!str || !*str)
        {
            continue;
        }

        if (_stricmp(str, ModuleNameToCheck) == 0)
        {
            // If a specific method name is provided, check for the specific method
            if (MethodNameToCheck)
            {
                PIMAGE_THUNK_DATA thunk;
                //Iterate through the INT(Import Name Table)
                if (importDescriptor->OriginalFirstThunk == 0)
                {
                    thunk = nullptr;
                }
                else
                {
                    thunk = (PIMAGE_THUNK_DATA)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
                        image.MappedAddress,
                        importDescriptor->OriginalFirstThunk,
                        NULL);
                }

                while (thunk && thunk->u1.Ordinal != 0)
                {
                    if (!IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
                    {
                        PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(ImageNtHeader(image.MappedAddress),
                            image.MappedAddress,
                            (ULONG)thunk->u1.ForwarderString,
                            NULL);
                        if (importName && _stricmp(importName->Name, MethodNameToCheck) == 0)
                        {
                            *pIsImported = TRUE;
                        }
                    }

                    thunk++;
                }
            }
            else
            {
                *pIsImported = TRUE;
            }
            
            break;
        }

        importDescriptor++;
    }

    return hr;

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsPortCls
//
// Check if audio device is PortCls
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsPortCls
(
    IMMDevice*          pDevice,
    bool*               pIsPortCls
)
{
    HRESULT                                 hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>         spDevnodeDevice;
    wil::com_ptr_nothrow<IPropertyStore>    spPnpProperties;
    wil::unique_prop_variant                varDeviceService;

    *pIsPortCls = false;

    // Find the associated PnP device 
    if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) {
        return hr;
    }

    // Open pnp device property store
    if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) {
        return hr;
    }

    // Read the DEVPKEY_Device_Service
    if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) {
        return hr;
    }

    // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO.
    // For practical purposes, this would most likely mean a non-PortCls pin, which is what we were trying to determine, so it is OK.
    if (varDeviceService.vt == VT_EMPTY) {
        return S_OK;
    }

    if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) {
        hr = E_FAIL;
        return hr;
    }

    WCHAR FullPath[MAX_PATH];
    if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) {
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "portcls.sys", "PcNewPort", pIsPortCls))) {
        return hr;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsAVStream
//
// Check if audio device is AVStream
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsAVStream
(
    IMMDevice*          pDevice,
    bool*               pIsAVStream
)
{
    HRESULT                                 hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>         spDevnodeDevice;
    wil::com_ptr_nothrow<IPropertyStore>    spPnpProperties;
    wil::unique_prop_variant                varDeviceService;

    *pIsAVStream = false;

    // Find the associated PnP device 
    if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) {
        return hr;
    }

    // Open pnp device property store
    if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) {
        return hr;
    }

    // Read the DEVPKEY_Device_Service
    if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) {
        return hr;
    }

    // If DEVPKEY_Device_Service is empty, it likely means a raw PDO, which means the device is handled by the parent FDO.
    // For practical purposes, this would most likely mean a non-AVStream pin, which is what we were trying to determine, so it is OK.
    if (varDeviceService.vt == VT_EMPTY) {
        return S_OK;
    }

    if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) {
        hr = E_FAIL;
        return hr;
    }

    WCHAR FullPath[MAX_PATH];
    if (!VERIFY_SUCCEEDED(hr = GetDriverPathViaService(varDeviceService.pwszVal, FullPath, MAX_PATH))) {
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = CheckImports(FullPath, "ks.sys", nullptr, pIsAVStream))) {
        return hr;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsBluetooth
//
// Check if audio device is Bluetooth
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsBluetooth
(
    IMMDevice* pDevice,
    bool* pIsBluetooth
)
{
    HRESULT                                 hr = S_OK;
    wil::com_ptr_nothrow<IPropertyStore>    spPropertyStore;
    wil::unique_prop_variant                varIsBluetooth;

    *pIsBluetooth = false;

    // Open pnp device property store
    if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) {
        return hr;
    }

    // Read the PKEY_Endpoint_IsBluetooth
    hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsBluetooth, &varIsBluetooth);
    if (hr != S_OK) {
        return hr;
    }

    if (varIsBluetooth.vt == VT_BOOL)
    {
        *pIsBluetooth = varIsBluetooth.boolVal == VARIANT_TRUE ? true : false;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsSideband
//
// Check if audio device is side band
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsSideband
(
    IMMDevice* pDevice,
    bool* pIsSideband
)
{
    HRESULT                                 hr = S_OK;
    wil::com_ptr_nothrow<IPropertyStore>    spPropertyStore;
    wil::unique_prop_variant                varIsSideband;

    *pIsSideband = false;

    // Open pnp device property store
    if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) {
        return hr;
    }

    // Read the PKEY_Endpoint_IsBluetooth
    hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsSideband, &varIsSideband);
    if (hr != S_OK) {
        return hr;
    }

    if (varIsSideband.vt == VT_BOOL)
    {
        *pIsSideband = varIsSideband.boolVal == VARIANT_TRUE ? true : false;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IsMVA
//
// Check if audio device is side band
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT IsMVA
(
    EndpointConnectorType eConnectorType,
    IMMDevice* pDevice,
    bool* pIsMVA
)
{
    HRESULT hr = S_OK;
    wil::com_ptr_nothrow<IMMDevice>             adapterDevice;
    wil::com_ptr_nothrow<IKsControl>            ksControl;
    ULONG                                       ulReturned = 0;
    ULONG                                       propertySupportFlags = 0;
    KSSOUNDDETECTORPROPERTY                     vam2Property = {0};

    if (!VERIFY_IS_TRUE(pDevice != nullptr)) {
        hr = E_FAIL;
        return hr;
    }

    if (!VERIFY_IS_TRUE(pIsMVA != nullptr)) {
        hr = E_FAIL;
        return hr;
    }

    // If it isn't a keyword connector, it's not MVA, we're done.
    if (eConnectorType != eKeywordDetectorConnector)
    {
        *pIsMVA = FALSE;
        return S_OK;
    }

    if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &adapterDevice)))
    {
        return hr;
    }

    if (!VERIFY_SUCCEEDED(hr = adapterDevice->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**) &ksControl))) {
        return hr;
    }

    // At this point we know that it is a keyword detector connector and we have the required interface to call into the driver
    // to see if it's MVA. If it's not MVA, it's SVA, so mark it as SVA here.
    *pIsMVA = false;

    vam2Property.Property.Set = KSPROPSETID_SoundDetector2;
    vam2Property.Property.Id = KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS;
    vam2Property.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT;
    vam2Property.EventId = GUID_NULL;

    hr = ksControl->KsProperty(
                (PKSPROPERTY)&vam2Property,
                sizeof(vam2Property),
                &propertySupportFlags,
                sizeof(propertySupportFlags),
                &ulReturned);
    if (SUCCEEDED(hr) && 
        propertySupportFlags == (KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT))
    {
        // Success, we've identified this as a connector which supports MVA, mark it.
        *pIsMVA = true;
    }

    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// GetPnpDevnodeFromMMDeivce
//
// Get pnp devnode from mm device
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT GetPnpDevnodeFromMMDevice
(
    IMMDevice* pDevice,
    IMMDevice** pDevnodeDevice
)
{
    HRESULT                                     hr = S_OK;
    wil::com_ptr_nothrow<IMMDeviceEnumerator>   spEnumerator;
    wil::com_ptr_nothrow<IPropertyStore>        spProps;
    wil::com_ptr_nothrow<IMMDevice>             spDevnodeAsDevice;
    wil::com_ptr_nothrow<IPropertyStore>        spDevnodeProps;
    wil::unique_prop_variant                    varDevnode;

    // Create an enumerator
    if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) {
        return hr;
    }

    // Read the PKEY_Endpoint_Devnode
    if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spProps))) {
        return hr;
    }
    if (!VERIFY_SUCCEEDED(hr = spProps->GetValue(PKEY_Endpoint_Devnode, &varDevnode))) {
        return hr;
    }

    // Get the devnode as an IMMDevice
    if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(varDevnode.pwszVal, &spDevnodeAsDevice))) {
        return hr;
    }
    *pDevnodeDevice = spDevnodeAsDevice.detach();

    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// VerifyAllEndpointsPluggedIn
//
// Verify there is no unplugged endpoint
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void VerifyAllEndpointsPluggedIn()
{
    wil::com_ptr_nothrow<IMMDeviceEnumerator>    spEnumerator;
    wil::com_ptr_nothrow<IMMDeviceCollection>    spEndpoints;
    UINT                                         cDevices = 0;

    SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
    DisableVerifyExceptions disable;

    VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));

    // Create IMMDevice Enumerator
    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator));

    // Enumerate all unplugged endpoints
    VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_UNPLUGGED, &spEndpoints));
    VERIFY_SUCCEEDED(spEndpoints->GetCount(&cDevices));

    if (!VERIFY_IS_TRUE(cDevices == 0))
    {
        Log::Comment(L"Following unplugged audio device(s) found, please plug in unplugged device(s).");

        for (UINT i = 0; i < cDevices; i++) {
            wil::com_ptr_nothrow<IMMDevice> spEndpoint;
            wil::unique_cotaskmem_string id;
            wil::unique_cotaskmem_string friendlyName;

            VERIFY_SUCCEEDED(spEndpoints->Item(i, &spEndpoint));
            VERIFY_SUCCEEDED(spEndpoint->GetId(&id));
            VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName));

            Log::Comment(String().Format(L"Device: %s (%s) is unplugged.", friendlyName.get(), id.get()));
        }
    }
}


<|start_filename|>general/SimpleMediaSource/MediaSource/pch.h<|end_filename|>
// pch.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once
// Windows Header Files:
#include <unknwn.h>
#include <windows.h>
#include <propvarutil.h>
#include <ole2.h>
#include <initguid.h>
#include <ks.h>
#include <ksproxy.h>
#include <ksmedia.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <nserror.h>
#include <winmeta.h>
#include <d3d9types.h>

#define RESULT_DIAGNOSTICS_LEVEL 4 // include function name

#include <wil\cppwinrt.h> // must be before the first C++ WinRT header, ref:https://github.com/Microsoft/wil/wiki/Error-handling-helpers
#include <wil\result.h>
#include <wil\com.h>

#include "SimpleFrameGenerator.h"
#include "SimpleMediaStream.h"
#include "SimpleMediaSource.h"
#include "SimpleMediaSourceActivate.h"

inline void DebugPrint(LPCWSTR szFormat, ...)
{
    WCHAR szBuffer[MAX_PATH] = { 0 };

    va_list pArgs;
    va_start(pArgs, szFormat);
    StringCbVPrintf(szBuffer, sizeof(szBuffer), szFormat, pArgs);
    va_end(pArgs);
    OutputDebugStringW(szBuffer);
}

#define DEBUG_MSG(msg,...) \
{\
    DebugPrint(L"[%s@%d] ", TEXT(__FUNCTION__), __LINE__);\
    DebugPrint(msg, __VA_ARGS__);\
    DebugPrint(L"\n");\
}\

namespace wilEx
{
    //template <typename T>
    //using make_unique_cotaskmem_array = unique_any_array_ptr<typename details::element_traits<T>::type>;

    template<typename T>
    wil::unique_cotaskmem_array_ptr<T> make_unique_cotaskmem_array(size_t numOfElements)
    {
        wil::unique_cotaskmem_array_ptr<T> arr;
        size_t cb = sizeof(wil::details::element_traits<T>::type) * numOfElements;
        void* ptr = ::CoTaskMemAlloc(cb);
        if (ptr != nullptr)
        {
            ZeroMemory(ptr, cb);
            arr.reset(reinterpret_cast<typename wil::details::element_traits<T>::type*>(ptr), numOfElements);
        }
        return arr;
    }
};

namespace winrt
{
    template<> bool is_guid_of<IMFMediaSourceEx>(guid const& id) noexcept;

    template<> bool is_guid_of<IMFMediaStream2>(guid const& id) noexcept;

    template<> bool is_guid_of<IMFActivate>(guid const& id) noexcept;
};




<|start_filename|>gnss/gnssUmdf/FixHandler.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    fixhandler.cpp

Abstract:

    This file contains the Fix handler and contains logic to provide position.

Environment:

    Windows User-Mode Driver Framework

--*/

#include "precomp.h"
#include "Trace.h"
#include "Defaults.h"
#include "FixSession.h"
#include "FixHandler.h"

#include "FixHandler.tmh" 


NTSTATUS
CFixHandler::Initialize(
    _In_ WDFQUEUE Queue
)
{
    NTSTATUS status = STATUS_SUCCESS;

    status = _SingleShotSession.Initialize(Queue);

    return status;
}

NTSTATUS
CFixHandler::StartFix(
    _In_ WDFREQUEST Request,
    _In_ BOOL LocationServiceEnabled
)
{
    NTSTATUS status = STATUS_SUCCESS;
    PGNSS_FIXSESSION_PARAM fixSessionParameters = nullptr;

    // For privacy reasons, the location stack notifies the driver when user toggles the location device controls.
    // When set to off, the driver must not entertain any new location sessions.
    if (!LocationServiceEnabled)
    {
        status = STATUS_UNSUCCESSFUL;
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Location master switch is off, we do not start a fix session");
        goto Exit;
    }

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(GNSS_FIXSESSION_PARAM), (void**)&fixSessionParameters, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "WdfRequestRetrieveInputBuffer failed with %!STATUS!", status);
        goto Exit;
    }

    //
    // FIX ME:
    // This sample currently supports only single shot session fix.
    // If you support another session type, e.g., distance tracking, update the logic below accordingly.
    //
    if (fixSessionParameters->SessionType != GNSS_FixSession_SingleShot)
    {
        status = STATUS_UNSUCCESSFUL;
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Session type is not GNSS_FixSession_SingleShot");
        goto Exit;
    }

    _CurrentSinglsShotSessionId = fixSessionParameters->FixSessionID;

    status = _SingleShotSession.StartAcquiringFix(fixSessionParameters);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "StartFix failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

NTSTATUS
CFixHandler::StopFix(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_SUCCESS;
    PGNSS_STOPFIXSESSION_PARAM stopFixSessionParam = nullptr;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(GNSS_STOPFIXSESSION_PARAM), (void**)&stopFixSessionParam, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "WdfRequestRetrieveInputBuffer failed with %!STATUS!", status);
        goto Exit;
    }

    // Send the session parameters to the right session based on the ID
    if (stopFixSessionParam->FixSessionID != _CurrentSinglsShotSessionId)
    {
        status = STATUS_UNSUCCESSFUL;
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Session type is not GNSS_FixSession_SingleShot");
        goto Exit;
    }

    _CurrentSinglsShotSessionId = INVALID_SESSION_ID;

    status = _SingleShotSession.StopAcquiringFix(stopFixSessionParam);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "StopFix failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

NTSTATUS
CFixHandler::StopExistingSinglsShotFix()
{
    NTSTATUS status = STATUS_SUCCESS;
    PGNSS_STOPFIXSESSION_PARAM stopFixSessionParam = nullptr;
    
    if (_CurrentSinglsShotSessionId == INVALID_SESSION_ID)
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "There is no single shot session to stop");
        goto Exit;
    }

    _CurrentSinglsShotSessionId = INVALID_SESSION_ID;
    
    INIT_POD_GNSS_STRUCT(stopFixSessionParam);
    status = _SingleShotSession.StopAcquiringFix(stopFixSessionParam);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "StopFix failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

NTSTATUS
CFixHandler::ModifyFix(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_SUCCESS;
    PGNSS_FIXSESSION_PARAM fixSessionParameters = nullptr;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(GNSS_FIXSESSION_PARAM), (void**)&fixSessionParameters, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "WdfRequestRetrieveInputBuffer failed with %!STATUS!", status);
        goto Exit;
    }

    //
    // FIX ME:
    // We currently support only single shot session fix.
    // If you support another session type, e.g., distance tracking, update the logic below accordingly.
    //
    if (fixSessionParameters->SessionType != GNSS_FixSession_SingleShot)
    {
        status = STATUS_UNSUCCESSFUL;
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Session type is not GNSS_FixSession_SingleShot");
        goto Exit;
    }
    
    //
    // FIX ME:
    // Since only single session is supported, use _SingleShotSession instance.
    // If multiple sessions, get the right session pointer searched by the session ID.
    //
    _SingleShotSession.ModifyAcquiringFix(fixSessionParameters);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "ModifyFix failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

NTSTATUS
CFixHandler::GetFixRequest(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG sessionId = INVALID_SESSION_ID;
    ULONG *pSessionId = nullptr;

    // Get the session ID this get fix is for
    status = WdfRequestRetrieveInputBuffer(Request, sizeof(sessionId), (void**)&pSessionId, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Could not retrieve input sessionID for request");
        goto Exit;
    }

    //
    // FIX ME:
    // Single session is only option now, but if multiple sessions, this sample should do this check at the FixHandler Level.
    //
    sessionId = *pSessionId;

    status = _SingleShotSession.GetFix(Request);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "SinglShotSession.GetFix failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}


<|start_filename|>storage/miniports/storahci/src/pnppower.c<|end_filename|>
﻿/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    pnppower.c

Abstract:

    This file contains function of pnp and power process of the AHCI miniport.


Notes:

Revision History:

--*/

#pragma warning(push)
#pragma warning(disable:26015) //26015: "Potential overflow using expression 'outParams->DriverStatus.bDriverError'. Buffer access is apparently unbounded by the buffer size.
                               //Output buffer cannot be checked for size.  ATAport provides this validation check as the input buffer size and output buffer size are 2 of the 4 parameters passed in on the SMART IRP.  Storport doesn’t do this and the miniport doesn’t get the IRP so it cannot do this for itself.  This is just the condition of a legacy IOCTL.
                                //26015: "Potential overflow using expression 'nRB->NRBStatus' Buffer access is apparently unbounded by the buffer size.
                                //The same is true for the NVCache IOCTL.  Instead of the output buffer, this time it is the NVCache_Request_Block.
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union

#include "generic.h"
#include "acpiioct.h"

//_DSM for Link Power is uniquely identified by the following UUID:
//  E4DB149B-FCFE-425B-A6D8-92357D78FC7F
static const GUID LINK_POWER_ACPI_DSM_GUID = {
    0xE4DB149B,
    0xFCFE,
    0x425B,
    { 0xA6,0xD8,0x92,0x35,0x7D,0x78,0xFC,0x7F }
  };

VOID
LogPageDiscoveryCompletion (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
    );


BOOLEAN
AhciPortInitialize(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
/*++
    This function is used to start an AHCI port
It assumes:

Called By:
    HwFindAdapter

Affected Variables/Registers:
    ChannelExtension->StateFlags
    ChannelExtension->CommandList
    ChannelExtension->ReceivedFIS

    PX.CLB,PX.CLBU
    PX.FB,PX.FBU
    CMD.ST
It performs:
    (overview)
    1. Start with some defensive structure checking and variable initialization
    2. Initialize the ChannelConfiguration structure (final steps)
    3. Enable the AHCI interface as per AHCI 1.1 section 10.1.2 (final steps)
    4. Allocate memory for the CommandList, the Receive FIS buffer, and SRB Extension.
    5. Start the channel processing commands.
    (details)
    1.1 Initialize variables
    1.2 Verify the Channel Configuration
    2.1 Initialize the ChannelConfiguration structure
    2.2 Initialize the Channel's base address and the controller's Interrupt Status register address
    3.1 Enable the AHCI interface
        AHCI 1.1 Section 10.1.2 - 5.
        "For each implemented port, system software shall allocate memory for and program:
            •    PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
            •    PxFB and PxFBU (if CAP.S64A is set to ‘1’)
            It is good practice for system software to ‘zero-out’ the memory allocated and referenced by PxCLB and PxFB.  After setting PxFB and PxFBU to the physical address of the FIS receive area, system software shall set PxCMD.FRE to ‘1’."
    3.2 Enable Interrupts on the Channel
        AHCI 1.1 Section 10.1.2 - 7.
        "Determine which events should cause an interrupt, and set each implemented port’s PxIE register with the appropriate enables."
        Note: Due to the multi-tiered nature of the AHCI HBA’s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to ‘0’ before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted.

    4.1 Allocate memory for the CommandList, the Receive FIS buffer and SRB Extension
        Now is the time to allocate memory that will be used for controller and per request structures.
        In AHCI, the controller structures are both the command header list and the received FIS buffer.
        The per request structure will be received through the SRB and will be used to make a Command Table
        The mechanism for requesting all of this memory is AtaPortGetUnCachedExtension.
        NOTE! AtaPortGetUnCachedExtension can only be called while processing a HwControl IdeStart.
        Also NOTE! In order to perform crashdump/hibernate the UncachedExtensionSize cannot be larger than 30K.

        The call to AtaPortGetUnCachedExtension is complicated by alignment restrictions that an AHCI controller has so here are the rules:
        - Command List Base Addresses must be 1K aligned, and the Command list is (sizeof (AHCI_COMMAND_HEADER) * cap.NCS), which is some multiple of 32 bytes in length.
        - The FIS Base Address must be 256 aligned, and the FIS Receive buffer is sizeof (AHCI_RECEIVED_FIS), 256 bytes in length.
        - The Command Table must be 128 aligned, and is sizeof(AHCI_COMMAND_TABLE), 1280 bytes in length thanks to some padding in the AHCI_COMMAND_TABLE structure.

        The alignment of the addresses (virtual and physical) returned by the function follow these rules
        - the address returned by AtaPortGetUnCachedExtension will have both its virtual and physical addresses page aligned
        - the memory received through the SRB will either be physically and virtually 4K aligned or SRBExtensionSize aligned. The first allocation will be on a 4K boundary the address of the second will be SRBExtensionSize larger than the first, the third will be SRBExtensionSize larger than the second, etc.

        Since the Command Header must be 1K aligned and the uncached extension starts 4K aligned, this works.
        However, the Command Header is variable and must be padded so the Received FIS is on a 256 boundary.
        Therefore the number of Command Headers must be 256/32 = 8. Round cap.NCS to the next multiple of 8
    4.2 Setup the CommandList
        Although the pointer returned from AtaPortGetUnCachedExtension is useful to this driver, it does the controller no good and can't be used in CLB. The VIRTUAL address must be translated into the PHYSICAL address before being written to the CLB register as the controller doesn't have the CPU's virtual address translation tables.  AtaPortGetPhysicalAddress Returns the physical address for the given Va. The va has to be an offset into any one of the following buffers.
            - SRB's data buffer
            - SRB's SrbExtension
            - Miniport's uncached extension
    4.3 Setup the Receive FIS buffer
        Handle the Receive FIS buffer the same as 4.2 Command List
    4.4 Setup the Local SRB Extension
    5.1 Enable Command Processing
    5.2 Initialize the ChannelConfiguration structure
        ChannelConfiguration->ChannelNumber and    ChannelConfiguration->ChannelResources are kept default values.
        If it is found that CI and/or SACT can be changed from a 1 to 0, Number of overlapped requests becomes 1.
        Number of overlapped requests is a 1 based number (1=1, 2=2, etc.), CAP.NCS is a 0 based number.
    5.3 START COMMAND PROCESSING
Return Values:
    The miniport driver returns TRUE if it successfully execute the whole function.
    Any errors causes the function to return FALSE and prevents the channel from loading. This ultimately causes a yellow '!' to show up on the channel in device manager.

NOTE: as this routine is invoked from FindAdapter where the adapter might not be fully initialized, do not retrieve registry information.
--*/
    PAHCI_ADAPTER_EXTENSION adapterExtension = NULL;
    PAHCI_MEMORY_REGISTERS  abar = NULL;

   //these are throw away variables
    ULONG   mappedLength = 0;

  //1.1 Initialize variables
    adapterExtension = ChannelExtension->AdapterExtension;
    if (LogExecuteFullDetail(adapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000024);//AhciPortInitialize
    }

    ChannelExtension->CurrentCommandSlot = 1; //slot 0 is reserved for internal commands,
    ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE;

    abar = (PAHCI_MEMORY_REGISTERS)adapterExtension->ABAR_Address;
    ChannelExtension->Px = &abar->PortList[ChannelExtension->PortNumber];

  // NonCachedExtension is for CommandList, Receive FIS, SRB Extension for Local SRB and Sense SRB., READ_LOG/IDENTIFY buffer
  // (sizeof(AHCI_COMMAND_HEADER) * paddedNCS) + sizeof(AHCI_RECEIVED_FIS) + 2*sizeof(AHCI_SRB_EXTENSION) + sizeof(AHCI_READ_LOG_EXT_DATA);

  //4.2 Setup the CommandList
    ChannelExtension->CommandListPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->CommandList, &mappedLength);
    if (ChannelExtension->CommandListPhysicalAddress.QuadPart == 0) {
        RecordExecutionHistory(ChannelExtension, 0xff02);//Command List Failed
        return FALSE;
    }
  //3.1.1 PxCLB and PxCLBU (AHCI 1.1 Section 10.1.2 - 5)
    if ( (ChannelExtension->CommandListPhysicalAddress.LowPart % 1024) == 0 ) {
        // validate the alignment is fine
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CLB.AsUlong, ChannelExtension->CommandListPhysicalAddress.LowPart);
    }else{
        RecordExecutionHistory(ChannelExtension, 0xff03);//Command List alignment failed
        return FALSE;
    }
    if (adapterExtension->CAP.S64A) { //If the controller supports 64 bits, write the high part too
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CLBU, ChannelExtension->CommandListPhysicalAddress.HighPart);
    }

  //4.3 Setup the Receive FIS buffer
    ChannelExtension->ReceivedFisPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->ReceivedFIS, &mappedLength);
    if (ChannelExtension->ReceivedFisPhysicalAddress.QuadPart == 0) {
        RecordExecutionHistory(ChannelExtension, 0xff04);//Receive FIS failed
        return FALSE;
    }

  //3.1.2 PxFB and PxFBU (AHCI 1.1 Section 10.1.2 - 5)
    if ((ChannelExtension->ReceivedFisPhysicalAddress.LowPart % 256) == 0) {
        // validate the alignment is fine
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->FB.AsUlong, ChannelExtension->ReceivedFisPhysicalAddress.LowPart);
    } else {
        RecordExecutionHistory(ChannelExtension, 0xff05);//Receive FIS alignment failed
        return FALSE;
    }
    if (adapterExtension->CAP.S64A) { //If the controller supports 64 bits, write the high part too
        StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->FBU, ChannelExtension->ReceivedFisPhysicalAddress.HighPart);
    }

  //4.4 Setup the Local SRB Extension
    ChannelExtension->Local.SrbExtensionPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->Local.SrbExtension, &mappedLength);
    ChannelExtension->Sense.SrbExtensionPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->Sense.SrbExtension, &mappedLength);

  //4.6 Setup Device Identify Data and Inquiry Data buffers
    ChannelExtension->DeviceExtension[0].IdentifyDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].IdentifyDeviceData, &mappedLength);
    ChannelExtension->DeviceExtension[0].InquiryDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].InquiryData, &mappedLength);

  //4.8 Setup STOR_ADDRESS for the device. StorAHCI uses Bus/Target/Lun addressing model, thus uses STOR_ADDRESS_TYPE_BTL8.
  //        Port - not need to be set by miniport, Storport has this knowledge. miniport can get the value by calling StorPortGetSystemPortNumber().
  //        Path - StorAHCI reports (highest implemented port number + 1) as bus number, thus "port number" will be "Path" value.
  //        Target - StorAHCI only supports single device on each port, the "Target" value will be 0.
  //        Lun - StorAHCI only supports single device on each port, the "Lun" value will be 0.
    ChannelExtension->DeviceExtension[0].DeviceAddress.Type = STOR_ADDRESS_TYPE_BTL8;
    ChannelExtension->DeviceExtension[0].DeviceAddress.Port = 0;
    ChannelExtension->DeviceExtension[0].DeviceAddress.AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
    ChannelExtension->DeviceExtension[0].DeviceAddress.Path = (UCHAR)ChannelExtension->PortNumber;
    ChannelExtension->DeviceExtension[0].DeviceAddress.Target = 0;
    ChannelExtension->DeviceExtension[0].DeviceAddress.Lun = 0;

    //
    // Initialize device power state to D0.
    //
    ChannelExtension->DevicePowerState = StorPowerDeviceD0;

  //3.2 Clear Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7)
  //We will enable interrupt after channel started
    PortClearPendingInterrupt(ChannelExtension);

  //5.1 Enable Command Processing
    ChannelExtension->StateFlags.Initialized = TRUE;

    if (adapterExtension->CAP.NCS > 0) { //this leaves one emergency slot free if possible, as CAP.NCS is 0-based.
        ChannelExtension->MaxPortQueueDepth = (UCHAR)adapterExtension->CAP.NCS;
    } else {
        ChannelExtension->MaxPortQueueDepth = 1;
    }

    if ( IsSingleIoDevice(adapterExtension) || IsDumpMode(adapterExtension) ) {
        ChannelExtension->MaxPortQueueDepth = 1;
    }

    ChannelExtension->LastActiveSlot = 0;
    ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth = ChannelExtension->MaxPortQueueDepth;

    if (!IsDumpMode(adapterExtension)) {
        if (AdapterResetInInit(adapterExtension)) {
            P_NotRunning(ChannelExtension, ChannelExtension->Px);
            AhciCOMRESET(ChannelExtension, ChannelExtension->Px);
        }
    }

    RecordExecutionHistory(ChannelExtension, 0x10000024);//Exit AhciPortInitialize
    return TRUE;
}


BOOLEAN
AhciAdapterPowerUp(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
/*++
    Indicates that the adapter is being powered up.
    Anything that doesn't persist across a power cycle needs to be done here.
It assumes:
    PCI Ensures the HBA is in D0 (Offset PMCAP + 4h: PMCS[0,1])
Called by:
    AhciAdapterControl

It performs:
    Enables the AHCI Interface and global Interrupts
Affected Variables/Registers:
    GHC.AE, GHC.IE
Return Values:
    TRUE always.
--*/
{
    ULONG i;
    AHCI_Global_HBA_CONTROL ghc;
    PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address;

    AdapterExtension->StateFlags.PowerDown = 0;

    // adapter is on its way power up. there will be no power down request coming in before this function finishes.
    // thus there is no need to call AdapterAcquireActiveReference;

    ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong);
    if (ghc.AE == 0) {
        ghc.AsUlong = 0;
        ghc.AE = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);
    }
    if (ghc.IE == 0) {
        ghc.IE = 1;
        StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);
    }

    // Power up all ports that don't have a device present.
    // There is protection method in AhciPortPowerUp() to only allow it run once.
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if ((AdapterExtension->PortExtension[i] != NULL) &&
            (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == TRUE) &&
            (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) {
            AhciPortPowerUp(AdapterExtension->PortExtension[i]);
        }
    }

    return TRUE;
}

BOOLEAN
AhciAdapterPowerDown(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
/*++
    Indicates that the adapter is being powered down.
It assumes:
    PCI powers down the HBA after this function returns: D3 Offset PMCAP + 4h: PMCS[0,1]
Called by:
    AhciAdapterControl

It performs:
    1. Clear GHC.IE
    AHCI 1.1 Section 8.3.3
    "Software must disable interrupts (GHC.IE must be cleared to ‘0’) prior to requesting a transition of the HBA to the D3 state.  This precaution by software avoids an interrupt storm if an interrupt occurs during the transition to the D3 state."
Affected Variables/Registers:
    GHC.IE
Return Values:
    TRUE always.
--*/
{
    ULONG i;
    AHCI_Global_HBA_CONTROL ghc;
    PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address;

    // Power down all ports that don't have a device present.
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if ((AdapterExtension->PortExtension[i] != NULL) &&
            (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) &&
            (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) {
            AhciPortPowerDown(AdapterExtension->PortExtension[i]);
        }
    }

    ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong);
    ghc.IE = 0;
    StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong);

    AdapterExtension->StateFlags.PowerDown = 1;

    return TRUE;
}

VOID
AhciPortStop(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
/*++

    The miniport driver should stop using the resources allocated for this port.
It assumes:
    AhciAdapterStop is called after all the ports are stopped.

    StartIo spin lock must be held before this function is invoked.
    
    Note: 
    Currently this function has only one caller - AdapterStop, which has the 
    following callers. Every respective code path has StartIo spin lock acquired.

    1. AhciAdapterStop. 

       It has one caller - AhciHwStartIo. Storport has already acquired StartIo spin lock
       before calling AhciHwStartIo.

    2. AhciHwAdapterControl.
             
       AhciHwAdapterControl has already acquired StartIo spin lock before calling AdapterStop.

Called by:

It performs:
    (overview)
    1. Stop the channel
    2. Undefine all references to anything within the Uncached Extension
Affected Variables/Registers:
    CMD.ST, CMD.CR, CMD.FRE, CMD.FR

Return Values:
    TRUE if the function executed completely.
    FALSE if the channel could not be stopped.
--*/

    if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x00000025);//AhciPortStop
    }

    //1. Stop the channel
    if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) {
        // don't need RESET, the port will be tried to start when processing start device request
        RecordExecutionHistory(ChannelExtension, 0xff08);   //AhciPortStop Failed
    }

    //2. Disable Interrupt and disconnect with Port resources

    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong, 0);    //disabling interrupts
    PortClearPendingInterrupt(ChannelExtension);

    ChannelExtension->Px = 0;

    ChannelExtension->StateFlags.Initialized = FALSE;
    ChannelExtension->StateFlags.NoMoreIO = FALSE;

    RecordExecutionHistory(ChannelExtension, 0x10000025);//Exit AhciPortStop

    return;
}

VOID
AhciPortPowerUp(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
/*++
    Indicates that the channel is being powered up.

Called by:
    AhciHwStartIo

It performs:
    (overview)
    1. Start the Port.
    2. If APM is supported, make sure the Link is Active as defined in AHCI1.0 8.3.1.2.
    3. If Port Multiplier is supported, powered it up.
    (details)
    1.1 Reload the CLB,CBU,FLB,FBU stored in the channel extension
    1.3 Reinitialize the StateFlags
    1.4 Start the channel
Affected Variables/Registers:
    PxCMD.ST, PxCMD.ICC
    PxCLB,PxCLBU,PxFB,PxFBU
    PxIE
Return Values:
    none
--*/

    AHCI_LPM_POWER_SETTINGS userLpmSettings;
    BOOLEAN portPowerDown;
    AHCI_INTERRUPT_STATUS pxis = { 0 };

    RecordExecutionHistory(ChannelExtension, 0x00000026);//Enter AhciPortPowerUp

    ++(ChannelExtension->TotalCountPowerUp);

    pxis.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong);

    // 1.0 Reinitialize the StateFlags. e.g. ChannelExtension->StateFlags.PowerDown = FALSE;
    portPowerDown = InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 12);    //StateFlags.PownDown field is at bit 12

    if (portPowerDown == FALSE) {

        RecordExecutionHistory(ChannelExtension, 0x00010026);//AhciPortPowerUp: port already powered up.
        return;
    }

    // 1.1 Reload the CLB,CBU,FLB,FBU
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong, ChannelExtension->CommandListPhysicalAddress.LowPart);
    if (ChannelExtension->AdapterExtension->CAP.S64A) { //If the controller supports 64 bits, write high part
        StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU, ChannelExtension->CommandListPhysicalAddress.HighPart);
    }
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong, ChannelExtension->ReceivedFisPhysicalAddress.LowPart);
    if (ChannelExtension->AdapterExtension->CAP.S64A) { //If the controller supports 64 bits, write high part
        StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU, ChannelExtension->ReceivedFisPhysicalAddress.HighPart);
    }

    //
    // If D3 Cold is enabled and we are being powered up from D3, we need to be
    // a bit heavy-handed with powering up the port due to loss of context.
    //
    if (ChannelExtension->DevicePowerState == StorPowerDeviceD3 && IsPortD3ColdEnabled(ChannelExtension)) {

        STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 };
        BOOLEAN powerUpInitializationInProgress = 0;

        // 1.4.1 Re-issue init commands (this will also restore preserved settings).
        AhciPortIssueInitCommands(ChannelExtension);

        // Set PowerUpInitializationInProgress flag, which will be cleared when preserved settings command done.
        powerUpInitializationInProgress = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22

        if (powerUpInitializationInProgress == 1) {

            // PowerUpInitializationInProgress flag was not cleared properly, which is not expected to happen, but not a fatal issue.
            RecordExecutionHistory(ChannelExtension, 0x10020026); // AhciPortPowerUp: PowerUpInitializationInProgress flag was not cleared properly.
        }

        // 1.4.2 Restore LPM settings
        userLpmSettings.AsUlong = ChannelExtension->LastUserLpmPowerSetting;
        AhciLpmSettingsModes(ChannelExtension, userLpmSettings);    //ignore the returned value, IO will be restarted anyway.

        // 1.5 Start the channel by issuing a reset to restore PHY communication.
        AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
        AhciPortReset(ChannelExtension, FALSE);
        AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);

    } else {

        //
        // If there is change in Current Connect Status(PCS:1), then QDR is needed.
        //
        if ((pxis.PCS == 1) &&
            (ChannelExtension->StartState.ChannelNextStartState == StartFailed)) {
            ChannelExtension->StateFlags.NeedQDR = TRUE;
        }

        // 1.4.1 Restore Preserved Settings
        if (NeedToSetTransferMode(ChannelExtension)) {
            RestorePreservedSettings(ChannelExtension, FALSE);
        }

        // 1.5 Start the channel
        P_Running_StartAttempt(ChannelExtension, FALSE);
    }

    RecordExecutionHistory(ChannelExtension, 0x10000026);//Exit AhciPortPowerUp

    return;
}

VOID
AhciPortPowerDown(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
/*++
Indicates that the channel is being powered down.

It assumes:
    the device has been powered down through ATA commands
    All outstanding IO will be complete before the first power request is sent to the miniport
Called by:
    AhciHwStartIo

It performs:
    Then each port must be stopped. PxCMD.ST
    If APM is supported, the Link need to be put into Slumber as defined in AHCI 1.1 Section 8.3.1.2
    If Port Multiplier is support, it would need to be powered down next.
Affected Variables/Registers:
    none
Return Values:
    TRUE if the function executed completely.
    FALSE if the channel could not be stopped for Power Down.
    Neither return value is used by ATAport.
--*/
    ChannelExtension->StateFlags.PowerDown = TRUE;

    ++(ChannelExtension->TotalCountPowerDown);

    //
    // Cancel the StartPortTimer since we're going into a lower power state.
    //
    StorPortRequestTimer(ChannelExtension->AdapterExtension,
                         ChannelExtension->StartPortTimer,
                         P_Running_Callback,
                         ChannelExtension,
                         0, 0);

    if (ChannelExtension->StateFlags.PoFxEnabled == 1) {
        if (IsPortD3ColdEnabled(ChannelExtension)) {
            // the link will be inactive, ignore the hot plug noise.
            ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE;
        }

    }

    RecordExecutionHistory(ChannelExtension, 0x10000027);//Exit AhciPortPowerDown
}


VOID
ReportLunsComplete(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
    )
{
    //Port start completed. Prepare device list.
    ULONG       lunCount;
    ULONG       lunLength;
    PLUN_LIST   lunList;
    ULONG       i;

    PAHCI_SRB_EXTENSION srbExtension;
    ULONG               srbDataBufferLength = SrbGetDataTransferLength(Srb);

    srbExtension = GetSrbExtension(Srb);

    // clean up callback fields so that the SRB can be completed.
    srbExtension->AtaFunction = 0;
    srbExtension->CompletionRoutine = NULL;

    // report error back so that Storport may retry the command.
    // tolerate failure from IDE_COMMAND_READ_LOG_EXT during device enumeration as it's not part of device enumeration commands.
    if ( (srbExtension->TaskFile.Current.bCommandReg != IDE_COMMAND_READ_LOG_EXT) &&
         (Srb->SrbStatus != SRB_STATUS_PENDING) &&
         (Srb->SrbStatus != SRB_STATUS_SUCCESS) &&
         (Srb->SrbStatus != SRB_STATUS_NO_DEVICE) ) {
        return;
    }

    Srb->SrbStatus = SRB_STATUS_SUCCESS;
    SrbSetScsiStatus(Srb, SCSISTAT_GOOD);

    lunList = (PLUN_LIST)SrbGetDataBuffer(Srb);

    if ( ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist ) {
        lunCount = 0;
    } else {
        //lunCount = ChannelExtension->DeviceExtension->DeviceParameters.MaximumLun + 1;
        lunCount = 1;
    }

    lunLength = lunCount * 8;

    if ( srbDataBufferLength < (sizeof(LUN_LIST) + lunLength) ) {
        Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
        if (srbDataBufferLength >= sizeof(ULONG)) {
            //fill in required buffer size
            lunList->LunListLength[0] = (UCHAR)(lunLength >> (8*3));
            lunList->LunListLength[1] = (UCHAR)(lunLength >> (8*2));
            lunList->LunListLength[2] = (UCHAR)(lunLength >> (8*1));
            lunList->LunListLength[3] = (UCHAR)(lunLength >> (8*0));
        }
    } else {
        lunList->LunListLength[0] = (UCHAR)(lunLength >> (8*3));
        lunList->LunListLength[1] = (UCHAR)(lunLength >> (8*2));
        lunList->LunListLength[2] = (UCHAR)(lunLength >> (8*1));
        lunList->LunListLength[3] = (UCHAR)(lunLength >> (8*0));

        for (i = 0; i < lunCount; i++) {
            lunList->Lun[i][0] = 0;
            lunList->Lun[i][1] = (UCHAR)i;
            lunList->Lun[i][2] = 0;
            lunList->Lun[i][3] = 0;
            lunList->Lun[i][4] = 0;
            lunList->Lun[i][5] = 0;
            lunList->Lun[i][6] = 0;
            lunList->Lun[i][7] = 0;
        }
    }

    return;
}

__inline
VOID
GetLogInfoRegValueName(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Out_writes_(ValueNameLength) PCHAR ValueName,
    _In_ ULONG ValueNameLength
    )
/*++

Routine Description:

    This function append Port Number to "LogPageInfo" as name of registry value.
    This is a work around as StorAHCI doesn't have access to RtlStringCbPrintfA().

Arguments:

    ChannelExtension - Pointer to the device extension for channel.

    ValueName - Registry name to be written.

    ValueNameLength - Registry name length.

Return Value:

    None.

--*/
{
    NT_ASSERT(ChannelExtension->PortNumber <= 255);
    
    if (ValueNameLength >= 14) {

        ULONG portNumber = ChannelExtension->PortNumber;
        ULONG remainder = 0; 
        
        StorPortCopyMemory(ValueName, "LogPageInfo", 12);
        ValueName[13] = '\0';
        
        remainder = portNumber % 16;  // use HEX value, base is 16.
        ValueName[12] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A'));
        
        portNumber /= 16;
        remainder = portNumber % 16;
        ValueName[11] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A'));
    }
    
    return;
}

_Function_class_(HW_WORKITEM)
VOID
AhciRegistryWriteWorker(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension,
    _In_ PVOID Context,
    _In_ PVOID WorkItem
    )
/*++

Routine Description:

    This function runs in the context of a work item.  It performs writting registry with data
    from Log Pages that needs to be cached.

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

    Context - The AHCI_CHANNEL_EXTENSION we specified when we queued this work item.

    WorkItem - The work item object.

Return Value:

    None.

--*/
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)Context;
    ULONG storStatus;
    CHAR valueName[16] = { 0 };
    AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 };

    NT_ASSERT(Context != NULL);
    NT_ASSERT(WorkItem != NULL);

    GetLogInfoRegValueName(channelExtension, valueName, sizeof(valueName));
 
    StorPortCopyMemory((PVOID)&logPageInfo.QueryLogPages, &channelExtension->DeviceExtension[0].QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY));
    StorPortCopyMemory((PVOID)&logPageInfo.SupportedGPLPages, &channelExtension->DeviceExtension[0].SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES));
    StorPortCopyMemory((PVOID)&logPageInfo.SupportedCommands, &channelExtension->DeviceExtension[0].SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED));
    StorPortCopyMemory((PVOID)&logPageInfo.FirmwareUpdate, &channelExtension->DeviceExtension[0].FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES));

    storStatus = StorPortRegistryWriteAdapterKey(AdapterExtension,
                                                 (PUCHAR)"StorAHCI",
                                                 (PUCHAR)valueName,
                                                 MINIPORT_REG_BINARY,
                                                 &logPageInfo,
                                                 sizeof(AHCI_DEVICE_LOG_PAGE_INFO));

    if (storStatus == STOR_STATUS_SUCCESS) {
        channelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = FALSE;
    } else {
        NT_ASSERT(FALSE);
    }

    //
    // Call the callback routine if there is one.
    //

    if (WorkItem != NULL) {
        StorPortFreeWorker(AdapterExtension, WorkItem);
    }

    return;
}

VOID
PreserveLogPageInformation(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
/*++

    This routine allocates a work item and schedule it for saving information retrieved from Log Pages.

Parameters:

    ChannelExtension - port and device that log pages are retrieved from.

Return Values:

    None

--*/
{
    ULONG storStatus;
    PVOID workItem = NULL;

    storStatus = StorPortInitializeWorker(ChannelExtension->AdapterExtension, &workItem);

    if (storStatus == STOR_STATUS_SUCCESS) {
        storStatus = StorPortQueueWorkItem(ChannelExtension->AdapterExtension, AhciRegistryWriteWorker, workItem, ChannelExtension);
    }

    NT_ASSERT(storStatus == STOR_STATUS_SUCCESS);

    if ((storStatus != STOR_STATUS_SUCCESS) && (workItem != NULL)) {
        // Free the work item as it cannot be scheduled to run.
        StorPortFreeWorker(ChannelExtension->AdapterExtension, workItem);
    }

    return;
}

VOID
InitQueryLogPages(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
/*++

    Initialize Log Pages to Read.

    Note that this functions should only be called after Identify Device Data is retrieved.

Parameters:

    ChannelExtension - port that log-pages-to-query should be inited.

Return Values:

    None

--*/
{
    PUSHORT index = &ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount;

    //
    // Log Page only applies to ATA device; General Purpose Logging feature should be supported; 
    // 48bit command should be supported as READ LOG EXT is a 48bit command.
    //
    if (!IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) {
        return;
    }

    AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY));

    // Read Log Directory
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DIRECTORY_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Device Statistics log - supported page
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Device Statistics log - general page
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Identify Device Data log - supported page
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Identify Device Data log - Supported Capabilities page
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Identify Device Data log - SATA page
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read Saved Device Internal log
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_SAVED_DEVICE_INTERNAL_STATUS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read NCQ non-Data log
    if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) &&
        (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqQueueMgmt == 1)) {
        ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    } else {
        ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = FALSE;
    }
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_NCQ_NON_DATA_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    // Read NCQ Send Receive log
    if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) &&
        (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqReceiveSend == 1)) {
        ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE;
    } else {
        ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = FALSE;
    }
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1;
    ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0;
    *index = *index + 1;

    NT_ASSERT(*index <= ATA_GPL_PAGES_QUERY_COUNT);

    return;
}

VOID
IssueReadLogExtCommand(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ UCHAR  LogAddress,
    _In_ USHORT PageNumber,
    _In_ USHORT BlockCount,
    _In_ USHORT FeatureField,
    _In_opt_ PSTOR_PHYSICAL_ADDRESS PhysicalAddress,
    _In_ PVOID DataBuffer,
    _In_opt_ PSRB_COMPLETION_ROUTINE CompletionRoutine
    )
/*++
    Issue READ LOG EXT command to device

Parameters:
    ChannelExtension    - port that the command should be sent to
    Srb                 - Srb that carries READ LOG EXT command in SrbExtension
    LogAddress          - Log address
    PageNumber          - Page# of the log
    BlockCount          - How many blocks (in 512 bytes)
    FeatureField        - log specific value
    PhysicalAddress     - Buffer physical address
    DataBuffer          - Buffer
    CompletionRoutine   - Routine that needs to be executed after READ LOG EXT command completed

Return Values:
    None

--*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);

    UNREFERENCED_PARAMETER(ChannelExtension);

    //1 Fills in the local SRB
    srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND;

    srbExtension->Flags |= ATA_FLAGS_DATA_IN ;
    srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND ;

    srbExtension->CompletionRoutine = CompletionRoutine;

    //setup TaskFile
    srbExtension->TaskFile.Current.bFeaturesReg = (UCHAR)(FeatureField & 0xFF);     //FeatureField, low part
    srbExtension->TaskFile.Current.bSectorCountReg = (UCHAR)(BlockCount & 0xFF);    //Number of blocks to read, low part
    srbExtension->TaskFile.Current.bSectorNumberReg = LogAddress;                   //Log address
    srbExtension->TaskFile.Current.bCylLowReg = (UCHAR)(PageNumber & 0xFF);         //Page#, low part
    srbExtension->TaskFile.Current.bCylHighReg = 0;
    srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0 | IDE_LBA_MODE;
    srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_READ_LOG_EXT;
    srbExtension->TaskFile.Current.bReserved = 0;

    srbExtension->TaskFile.Previous.bFeaturesReg = (UCHAR)((FeatureField >> 8) & 0xFF);     //FeatureField, high part
    srbExtension->TaskFile.Previous.bSectorCountReg = (UCHAR)((BlockCount >> 8) & 0xFF);    //Number of blocks to read, high part
    srbExtension->TaskFile.Previous.bSectorNumberReg = 0;
    srbExtension->TaskFile.Previous.bCylLowReg = (UCHAR)((PageNumber >> 8) & 0xFF);         //Page#, high part
    srbExtension->TaskFile.Previous.bCylHighReg = 0;
    srbExtension->TaskFile.Previous.bDriveHeadReg = 0;
    srbExtension->TaskFile.Previous.bCommandReg = 0;
    srbExtension->TaskFile.Previous.bReserved = 0;

    Srb->SrbStatus = SRB_STATUS_PENDING;
    srbExtension->DataBuffer = DataBuffer;
    if ( PhysicalAddress ) {
        srbExtension->DataBufferPhysicalAddress.QuadPart = PhysicalAddress->QuadPart;
    }

    //setup SGL
    if ( PhysicalAddress ) {
        srbExtension->LocalSgl.NumberOfElements = 1;
        srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = PhysicalAddress->LowPart;
        srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = PhysicalAddress->HighPart;
        srbExtension->LocalSgl.List[0].Length = ATA_BLOCK_SIZE * (ULONG)BlockCount;
        srbExtension->Sgl = &srbExtension->LocalSgl;
        srbExtension->DataTransferLength = ATA_BLOCK_SIZE * (ULONG)BlockCount;
    }

    return;
}

__inline
USHORT
GetNextQueryLogPageIndex (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
)
/*++
    Get an Index value that log page should be queried.

Parameters:
    ChannelExtension

Return Values:
    USHORT

--*/
{
    USHORT i;
    USHORT index = ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex;

    if (index >= ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount) {
        return ATA_GPL_PAGES_INVALID_INDEX;
    }

    for (i = index; i < ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; i++) {
        if (ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].Query) {
            ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex = i;
            return i;
        }
    }

    return ATA_GPL_PAGES_INVALID_INDEX;
}

__inline
VOID
ReadQueryLogPage (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ USHORT                  Index
)
{
    IssueReadLogExtCommand( ChannelExtension,
                           Srb,
                           ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].LogAddress,
                           ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].PageNumber,
                           ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].BlockCount,
                           ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].FeatureField,
                           &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress,
                           (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData,
                           LogPageDiscoveryCompletion
    );
}


__inline
VOID
UpdateQueryLogPageSupportive (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR                   LogAddress,
    _In_ USHORT                  PageNumber,
    _In_ BOOLEAN                 Supported
)
/*++
    Mark log page supportive information.

Parameters:
    ChannelExtension    - port that log-page-to-query should be updated.
    LogAddress          - Log address
    PageNumber          - Page# of the log
    Supported           - log page is supported or not by device

Return Values:
    None

--*/
{
    USHORT i;

    for (i = 0; i < ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; i++) {
        if ((ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].LogAddress == LogAddress) &&
            (ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].PageNumber == PageNumber)) {
            
            ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].Query = Supported;
            return;
        }
    }

    return;
}

VOID
UpdateDownloadMicrocodeSupport(
    _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PIDENTIFY_DEVICE_DATA_LOG_PAGE_SUPPORTED_CAPABILITIES Capabilities
)
/*++

Routine Description:

    Takes the given Supported Capabilities log page and updates the firmware
    update (download microcode) support information that is cached in the given
    Channel Extension.

Arguments:

    Channel Extension
    Supported Capabilities

--*/
{
    NT_ASSERT(Capabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION);
    NT_ASSERT(Capabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE);

    //
    // Set it to unsupported by default.
    //
    ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = 0;

    //
    // If the Download Microcode capabilites are valid then fill in our cached copy.
    //
    if (Capabilities->DownloadMicrocodeCapabilities.Valid == 1) {

        if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DownloadMicrocodeDmaSupported == 1) ||
            (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.DownloadMicrocode == 1)) {

            ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = (Capabilities->DownloadMicrocodeCapabilities.DmOffsetsDeferredSupported == 1);

            if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) {

                if ((Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize > 0) &&
                    (Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize < 0xFFFF)) {
                    ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE);
                } else {
                    ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1;
                }

                if ((Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize > 0) &&
                    (Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize < 0xFFFF)) {
                    ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE);
                } else {
                    ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE;
                }
            }
        }
    }
}

VOID
GetDownloadMicrocodeSupportCompletion(
    _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
)
/*++

Routine Description:

    Called when the Supported Capabilities log page has been obtained from the
    device.  The log page is parsed for the Download Microcode support and the
    cached copy of this data is updated in the ChannelExtension.

Arguments:

    Channel Extension
    SRB

--*/
{
    PAHCI_SRB_EXTENSION         srbExtension;

    srbExtension = GetSrbExtension(Srb);

    //
    // If the request succeeded then update the cached copy of the Download
    // Microcode support information.
    //
    if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
        UpdateDownloadMicrocodeSupport(ChannelExtension, srbExtension->DataBuffer);
    }

    AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress);

    //
    // The SRB will be completed after this completion routine returns so
    // there's no need to do it here.
    //
}

ULONG
GetDownloadMicrocodeSupport(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK Srb
)
/*++
Routine Description:

    Queries the Download Microcode support from the Identify Log Page and
    updates our cached copy of it.

Arguments:
    ChannelExtension
    SRB

Return Value:

    Status code.

--*/
{
    ULONG                   status = STOR_STATUS_SUCCESS;
    PVOID                   buffer = NULL;
    STOR_PHYSICAL_ADDRESS   bufferPhysicalAddress = { 0 };

    //
    // Fail the request if it's not supported by device.
    //
    if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) == FALSE) {
        Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
        return STOR_STATUS_INVALID_PARAMETER;
    }

    //
    // Allocate DMA buffer to the log page.
    //
    status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress);

    if ((status != STOR_STATUS_SUCCESS) || (buffer == NULL)) {
        if (buffer != NULL) {
            AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, buffer, bufferPhysicalAddress);
        }
        Srb->SrbStatus = SRB_STATUS_ERROR;
        return STOR_STATUS_INSUFFICIENT_RESOURCES;
    }

    AhciZeroMemory((PCHAR)buffer, IDE_GP_LOG_SECTOR_SIZE);

    //
    // Issue the command.  The completion routine will update the cached
    // firmware update information with the info from the log page.
    // The completion routine will also free the DMA buffer.
    //
    IssueReadLogExtCommand(ChannelExtension,
                           Srb,
                           IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS,
                           IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE,
                           1,
                           0,      // feature field
                           &bufferPhysicalAddress,
                           buffer,
                           GetDownloadMicrocodeSupportCompletion);
    return status;
}

VOID
LogPageDiscoveryCompletion (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
    )
/*
    This process is to discover all needed information from general log pages.
    The process is initiated by reading log directory when Identify Device command completes.

*/
{
    USHORT nextPageIndex = ATA_GPL_PAGES_INVALID_INDEX;

    UCHAR  completedLogAddress = 0;
    USHORT completedPageNumber = 0;

    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);

    // get the Log Address and Log Page just received
    completedLogAddress = srbExtension->TaskFile.Current.bSectorNumberReg;
    completedPageNumber = ((USHORT)srbExtension->TaskFile.Previous.bCylLowReg << 8) | srbExtension->TaskFile.Current.bCylLowReg;

    if ( (completedLogAddress == IDE_GP_LOG_DIRECTORY_ADDRESS) && (completedPageNumber == 0) ) {
        // the issued command was for getting Log Directory

        // ACS8-3, 4.12.1 Devices that report support for the NCQ feature set shall also report support for the GPL feature set (see 4.9),
        // the General Purpose Log Directory log and the NCQ Command Error log.
        NT_ASSERT( (Srb->SrbStatus == SRB_STATUS_SUCCESS) ||
                   (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 0) );


        if ( (Srb->SrbStatus == SRB_STATUS_SUCCESS) &&
             (ChannelExtension->DeviceExtension->ReadLogExtPageData[0] == IDE_GP_LOG_VERSION) ) {
            // per ACS spec: The value of the General Purpose Logging Version word shall be 0001h
            // preserve the log address supportive information
            ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS] > 0) ? 1 : 0;
            if (ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE);
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE);
            }

            ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS] > 0) ? 1 : 0;
            if (ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE);
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, FALSE);
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE);
            }

            ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqCommandError = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS] > 0) ? 1 : 0;

            ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqNonData = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_NON_DATA_ADDRESS] > 0) ? 1 : 0;
            if (ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqNonData == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_NON_DATA_ADDRESS, 0, FALSE);
            }

            ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqSendReceive = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS] > 0) ? 1 : 0;
            if (ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqSendReceive == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS, 0, FALSE);
            }

            ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.HybridInfo = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_HYBRID_INFO_ADDRESS] > 0) ? 1 : 0;

        } else {
            // Log Directory can be optional. Preset supportive info, they will be updated if the actual command fails later.
            // In case of the disk doesn't support NCQ and doesn't support Log Directory, still try to discover some log pages.

            // don't query all other log pages.
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_NON_DATA_ADDRESS, 0, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS, 0, FALSE);
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS) && (completedPageNumber == IDE_GP_LOG_SUPPORTED_PAGES) ) {
        // the issued command was for getting supported log pages of device statistics log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PDEVICE_STATISTICS_LOG_PAGE_HEADER pageHeader = (PDEVICE_STATISTICS_LOG_PAGE_HEADER)ChannelExtension->DeviceExtension->ReadLogExtPageData;
            PUCHAR pageSupported = (PUCHAR)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            // first byte after header is how many entries in following list.
            UCHAR pageCount = *(pageSupported + sizeof(DEVICE_STATISTICS_LOG_PAGE_HEADER));

            // The value of revision number word shall be 0001h. The first supported page shall be 00h.
            if ( (pageHeader->RevisionNumber == IDE_GP_LOG_VERSION) &&
                 (pageHeader->PageNumber == IDE_GP_LOG_SUPPORTED_PAGES) &&
                 (pageCount > 1) ) {

                int i;
                for (i = 1; i <= pageCount; i++) {
                    // if the page number is shown in supported list, mark it's supported.
                    if (*(pageSupported + sizeof(DEVICE_STATISTICS_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) {
                        ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics = 1;
                    }

                    if (*(pageSupported + sizeof(DEVICE_STATISTICS_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_DEVICE_STATISTICS_TEMPERATURE_PAGE) {
                        ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.TemperatureStatistics = 1;
                        break;
                    }
                }
            }

            if (ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE);
            }
        } else {
            ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported = 0;
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE);
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS) && (completedPageNumber == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) ) {
        // the issued command was for getting General Statistics page of Device Statistics Log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PGP_LOG_GENERAL_STATISTICS generalStatistics = (PGP_LOG_GENERAL_STATISTICS)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            // The value of revision number word shall be 0002h. (It's changed to 0001h in ACS4)
            if ( ((generalStatistics->Header.RevisionNumber == 0x0002) || (generalStatistics->Header.RevisionNumber == IDE_GP_LOG_VERSION)) &&
                 (generalStatistics->Header.PageNumber == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) ) {
                if (generalStatistics->DateAndTime.Supported == 1) {
                    ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime = 1;
                }
            }
        } else {
            ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics = 0;
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS) && (completedPageNumber == IDE_GP_LOG_SUPPORTED_PAGES) ) {
        // the issued command was for getting supported log pages of identify device data log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PIDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER pageHeader = (PIDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER)ChannelExtension->DeviceExtension->ReadLogExtPageData;
            PUCHAR pageSupported = (PUCHAR)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            // first byte after header is how many entries in following list.
            UCHAR pageCount = *(pageSupported + sizeof(IDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER));

            // The value of revision number word shall be 0001h. The first supported page shall be 00h.
            if ( (pageHeader->RevisionNumber == IDE_GP_LOG_VERSION) &&
                 (pageHeader->PageNumber == IDE_GP_LOG_SUPPORTED_PAGES) &&
                 (pageCount > 1) ) {

                int i;
                for (i = 1; i <= pageCount; i++) {
                    // if the page number is shown in supported list, mark it's supported.
                    if (*(pageSupported + sizeof(IDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE) {
                        ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SupportedCapabilities = 1;
                    } else if (*(pageSupported + sizeof(IDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE) {
                        ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA = 1;
                    }
                }
            }

            if (ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA == 0) {
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, FALSE);
                UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE);
            }
        } else {
            ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported = 0;
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, FALSE);
            UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE);
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS) && (completedPageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE) ) {
        // the issued command was for getting supported capabilities log page of identify device data log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PIDENTIFY_DEVICE_DATA_LOG_PAGE_SUPPORTED_CAPABILITIES supportedCapabilities = (PIDENTIFY_DEVICE_DATA_LOG_PAGE_SUPPORTED_CAPABILITIES)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            // The value of revision number word shall be 0001h.
            if ((supportedCapabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION) &&
                (supportedCapabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE)) {

                UpdateDownloadMicrocodeSupport(ChannelExtension, supportedCapabilities);
                
            }

        } else {
            ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SupportedCapabilities = 0;
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS) && (completedPageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE) ) {
        // the issued command was for getting SATA log page of identify device data log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {

        } else {
            ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA = 0;
        }

    } else if ( (completedLogAddress == IDE_GP_LOG_SAVED_DEVICE_INTERNAL_STATUS) && (completedPageNumber == 0) ) {
        // the issued command was for getting saved device internal data log

        if (Srb->SrbStatus != SRB_STATUS_SUCCESS) {
            ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.SavedDeviceInternalStatusData = 0;
        }
    } else if ( (completedLogAddress == IDE_GP_LOG_NCQ_NON_DATA_ADDRESS) && (completedPageNumber == 0) ) {
        // the issued command was for getting ncq non-data log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PGP_LOG_NCQ_NON_DATA ncqNonData = (PGP_LOG_NCQ_NON_DATA)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            ChannelExtension->DeviceExtension->SupportedCommands.HybridDemoteBySize = ncqNonData->SubCmd2.HybridDemoteBySize;
            ChannelExtension->DeviceExtension->SupportedCommands.HybridChangeByLbaRange = ncqNonData->SubCmd3.HybridChangeByLbaRange;
            ChannelExtension->DeviceExtension->SupportedCommands.HybridControl = ncqNonData->SubCmd4.HybridControl;

        } else {
            NT_ASSERT(FALSE);
        }
    } else if ( (completedLogAddress == IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS) && (completedPageNumber == 0) ) {
        // the issued command was for getting ncq send receive log

        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            PGP_LOG_NCQ_SEND_RECEIVE ncqSendReceive = (PGP_LOG_NCQ_SEND_RECEIVE)ChannelExtension->DeviceExtension->ReadLogExtPageData;

            ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict = ncqSendReceive->SubCmd.HybridEvict;

        } else {
            NT_ASSERT(FALSE);
        }
    } else {
        // all log addresses and log pages in log page discovery process should be covered in above conditions.
        NT_ASSERT(FALSE);
    }

    //
    // Move index to the next one and check if there is any log pages pending to read.
    //
    ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex++;
    nextPageIndex = GetNextQueryLogPageIndex(ChannelExtension);

    if (nextPageIndex != ATA_GPL_PAGES_INVALID_INDEX) {
        ReadQueryLogPage(ChannelExtension, Srb, nextPageIndex);
    } else {

        ReportLunsComplete(ChannelExtension, Srb);

        // Use a work itme to preserve information from Log Pages into registry.
        PreserveLogPageInformation(ChannelExtension);
    }

    return;
}


VOID
UpdateDeviceType(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
/*++

Routine Description:

    Update device type information after device enumeration.

    Assumption - PxSIG is ready to be accessed.

Arguments:

    ChannelExtension

Return Value:

    None.

--*/
{
    PATA_DEVICE_PARAMETERS deviceParameters = &ChannelExtension->DeviceExtension->DeviceParameters;

    // There is chance that during device enumeration, port is not started yet and signature register is 0xffffffff,
    // which will result in the AtaDeviceType is set to DeviceUnknown.
    // Update it here if above situation is true.
    if (IsUnknownDevice(deviceParameters)) {

        ULONG sig = 0;
        sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong);

        if (sig == ATA_DEVICE_SIGNATURE_ATA) {
            ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAta;
        } else if (sig == ATA_DEVICE_SIGNATURE_ATAPI) {
            ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAtapi;
        } else {
            NT_ASSERT(FALSE);
        }
    }
}


VOID
AhciPortIdentifyDevice(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
  )
{
    PAHCI_SRB_EXTENSION srbExtension;
    PCDB                cdb = SrbGetCdb(Srb);

    srbExtension = GetSrbExtension(Srb);

    if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) {
        return;
    }

    if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) {
        // command failed consider as no device
        ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceNotExist;
    }

    if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {

        // Update device ata type if it isn't initialized correctly.
        UpdateDeviceType(ChannelExtension);

        //Re-initialize device specific information to avoid the values being reused after device switched.
        ChannelExtension->StateFlags.NCQ_Activated = 0;
        ChannelExtension->StateFlags.NCQ_Succeeded = 0;
        ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 0;

        ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs = 0;

        AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES));
        AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED));
        AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES));

        // identify completes, digest identify data / inquiry data
        UpdateDeviceParameters(ChannelExtension);

        //
        // Cache if PUIS is enabled or not.  We'll look at this later when
        // powering up the port to determine if we need to send the spin up
        // command first.
        //
        ChannelExtension->StateFlags.PuisEnabled = ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetActive.PowerUpInStandby;

        // Initialize port properties
        ChannelExtension->PortProperties = 0;

        if (ChannelExtension->StateFlags.IdentifyDeviceSuccess == 0) {
            ChannelExtension->StateFlags.IdentifyDeviceSuccess = 1;
        }

        if (IsExternalPort(ChannelExtension)) {
            SETMASK(ChannelExtension->PortProperties, PORT_PROPERTIES_EXTERNAL_PORT);
        }
    }

    // Identify Device can only be triggered from REPORT LUNS command or
    // INQUIRY command (for disk in dump environment)
    if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS)) {

        BOOLEAN cachedLogPageInfoUsable = FALSE;

        if ((Srb->SrbStatus == SRB_STATUS_SUCCESS) &&
            IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) {

            ULONG storStatus;

            //
            // Check to determine whether use cached device settings.
            //
            storStatus = StorPortIsDeviceOperationAllowed(ChannelExtension->AdapterExtension,
                                                          NULL,
                                                          &STORPORT_DEVICEOPERATION_CACHED_SETTINGS_INIT_GUID,
                                                          (PULONG)(&cachedLogPageInfoUsable));

            cachedLogPageInfoUsable = (cachedLogPageInfoUsable && (ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo == FALSE));
            
            if (cachedLogPageInfoUsable) {
                // In case this is not a newly plugged in device and it's allowed to use cached Log Page Information.
                CHAR valueName[16] = { 0 };
                AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 };
                PVOID dataBuffer = &logPageInfo;
                ULONG dataLength = sizeof(AHCI_DEVICE_LOG_PAGE_INFO);

                GetLogInfoRegValueName(ChannelExtension, valueName, sizeof(valueName));

                storStatus = StorPortRegistryReadAdapterKey(ChannelExtension->AdapterExtension,
                                                            (PUCHAR)"StorAHCI",
                                                            (PUCHAR)valueName,
                                                            MINIPORT_REG_BINARY,
                                                            &dataBuffer,
                                                            &dataLength);

                if ((storStatus == STOR_STATUS_SUCCESS) && (dataLength == sizeof(AHCI_DEVICE_LOG_PAGE_INFO))) {

                    StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].QueryLogPages, &logPageInfo.QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY));
                    StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedGPLPages, &logPageInfo.SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES));
                    StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedCommands, &logPageInfo.SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED));
                    StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].FirmwareUpdate, &logPageInfo.FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES));

                    ReportLunsComplete(ChannelExtension, Srb);
                } else {
                    cachedLogPageInfoUsable = FALSE;
                }
            }

            //
            // Start log page discovery process if decide not use cached log page data.
            //
            if (!cachedLogPageInfoUsable) {

                USHORT index;

                InitQueryLogPages(ChannelExtension);

                index = GetNextQueryLogPageIndex(ChannelExtension);

                NT_ASSERT(index == 0);

                if (index != ATA_GPL_PAGES_INVALID_INDEX) {
                    // First page should be log directory. Read it to get pages supported by device.
                    ReadQueryLogPage(ChannelExtension, Srb, index);
                } else {
                    ReportLunsComplete(ChannelExtension, Srb);
                }
            }
        } else {
            ReportLunsComplete(ChannelExtension, Srb);
        }
    } else if (IsDumpMode(ChannelExtension->AdapterExtension) && 
               (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)) {

        if (IsDumpResumeMode(ChannelExtension->AdapterExtension) &&
            (Srb->SrbStatus == SRB_STATUS_SUCCESS) &&
            IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) &&
            IsDeviceHybridInfoSupported(ChannelExtension)) {
            //
            // Read Hybrid Information log during resume, so that disk can stop self-pinning.
            // In normal stack, suerfetch sends down HYBRID_FUNCTION_GET_INFO triggers the log to be read.
            //
            IssueReadLogExtCommand( ChannelExtension,
                                    Srb,
                                    IDE_GP_LOG_HYBRID_INFO_ADDRESS,
                                    0,
                                    1,
                                    0,      // feature field
                                    &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress,
                                    (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData,
                                    (PSRB_COMPLETION_ROUTINE)InquiryComplete
                                    );
        } else {
            InquiryComplete(ChannelExtension, Srb);
        }
    } else if ((ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) &&
               (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)) {

        //
        // We are refreshing Identify information because of a firmware update.
        // The firmware update support information is contained in the Supported
        // Capabilities log page so we need to make sure we query that page as well.
        // 
        ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 0;

        //
        // Finish processing the Inquiry command before re-using the SRB to get
        // the Supported Capabilities log page.
        //        
        InquiryComplete(ChannelExtension, Srb);
              
        if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) {
            GetDownloadMicrocodeSupport(ChannelExtension, Srb);
        }

    }

    return;
}

VOID
AhciPortNVCacheCompletion(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
  )
{
    PSRB_IO_CONTROL         srbControl;
    PNVCACHE_REQUEST_BLOCK  nRB;
    PATA_TASK_FILE          TaskFile;
    PAHCI_SRB_EXTENSION     srbExtension = GetSrbExtension(Srb);

    UNREFERENCED_PARAMETER(ChannelExtension);

    srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb);
    nRB = ((PNVCACHE_REQUEST_BLOCK) ( (PSRB_IO_CONTROL) srbControl + 1) );  //26015: "Potential overflow using expression 'nRB->NRBStatus' Buffer access is apparently unbounded by the buffer size.

    // Return status success indicating that the request was handled by the device.
    nRB->NRBStatus = NRB_SUCCESS;

    TaskFile = (PATA_TASK_FILE)srbExtension->ResultBuffer;

    if ( TaskFile != NULL ) {

        nRB->NVCacheStatus = TaskFile->Current.bCommandReg;
        if (TaskFile->Current.bCommandReg & 1) {  // command failed
            nRB->NVCacheSubStatus = TaskFile->Current.bFeaturesReg;
        }

        nRB->Count = (TaskFile->Current.bSectorCountReg << 8) +
                     (TaskFile->Previous.bSectorCountReg);

        nRB->LBA = (ULONGLONG) TaskFile->Previous.bCylHighReg;
        nRB->LBA <<= 8;
        nRB->LBA += (ULONGLONG) TaskFile->Previous.bCylLowReg;
        nRB->LBA <<= 8;
        nRB->LBA += (ULONGLONG) TaskFile->Previous.bSectorNumberReg;
        nRB->LBA <<= 8;
        nRB->LBA += (ULONGLONG) TaskFile->Current.bCylHighReg;
        nRB->LBA <<= 8;
        nRB->LBA += (ULONGLONG) TaskFile->Current.bCylLowReg;
        nRB->LBA <<= 8;
        nRB->LBA += (ULONGLONG) TaskFile->Current.bSectorNumberReg;

        //
        // Free the buffer allocated as mode sense info buffer , holding task file
        //
        AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile, srbExtension->ResultBufferPhysicalAddress);

    } else {
        // in case TaskFile is not returned in SenseInfoBuffer, use cached ATA Status and Error register values.
        if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            // command succeeded
            nRB->NVCacheStatus = 0;
            nRB->NVCacheSubStatus = 0;
        } else {
            // command failed
            nRB->NVCacheStatus = srbExtension->AtaStatus;
            nRB->NVCacheSubStatus = srbExtension->AtaError;
        }
    }

    return;
}

VOID
AhciPortSmartCompletion(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
  )
{
    PSENDCMDOUTPARAMS           outParams;
    PAHCI_SRB_EXTENSION         srbExtension;
    PUCHAR                      buffer;//to make the pointer arithmatic easier

    UNREFERENCED_PARAMETER(ChannelExtension);

    buffer       = (PUCHAR)SrbGetDataBuffer(Srb) + sizeof(SRB_IO_CONTROL);
    outParams    = (PSENDCMDOUTPARAMS) buffer;                          //26015: "Potential overflow using expression 'outParams->DriverStatus.bDriverError'  Buffer access is apparently unbounded by the buffer size.
    srbExtension = GetSrbExtension(Srb);

    Srb->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID;  // remove this flag as there is no data copy back to original Sense Buffer

    if (Srb->SrbStatus == SRB_STATUS_SUCCESS) {
        outParams->DriverStatus.bDriverError = 0;
        outParams->DriverStatus.bIDEError = 0;

        // RETURN_SMART_STATUS does not perform data transfer but copies the registers.
        if (srbExtension->TaskFile.Current.bFeaturesReg == RETURN_SMART_STATUS) {
            outParams->cBufferSize = sizeof(ATAREGISTERS);
        } else {
            outParams->cBufferSize = srbExtension->DataTransferLength;
        }

    } else  {
        // command failed
        outParams->DriverStatus.bDriverError = SMART_IDE_ERROR;
        outParams->DriverStatus.bIDEError = srbExtension->AtaStatus;
        outParams->cBufferSize = 0;
    }

    return;
}

__inline
VOID
BuildLocalCommand(
    _In_ PAHCI_CHANNEL_EXTENSION        ChannelExtension,
    _In_ PATA_TASK_FILE                 TaskFile,
    _In_opt_ PSRB_COMPLETION_ROUTINE    CompletionRountine
    )
/*++

It assumes:
    nothing
Called by:
    IssuePreservedSettingCommands

It performs:
    1 Fills in the local SRB with the ATA command

Affected Variables/Registers:
    none

--*/
{
    PSCSI_REQUEST_BLOCK srb;
    PAHCI_SRB_EXTENSION srbExtension;

    //
    // Local Srb still uses SCSI_REQUEST_BLOCK type.
    // do not touch field "srb->NextSrb". It should be only touched in queue related operations.
    //
    srb = &ChannelExtension->Local.Srb;
    srb->SrbStatus = SRB_STATUS_PENDING;
    srb->SrbExtension = (PVOID)ChannelExtension->Local.SrbExtension;
    srb->TimeOutValue = 1;      //as it's sent by miniport, no one monitors the timeout value.

    // Fills in the local SRB with the SetFeatures command
    srbExtension = ChannelExtension->Local.SrbExtension;
    AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION));

    srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND;
    srbExtension->CompletionRoutine = CompletionRountine;

    //setup TaskFile
    StorPortCopyMemory(&srbExtension->TaskFile, TaskFile, sizeof(ATA_TASK_FILE));

    if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x1000001d);//Exit BuildLocalCommand
    }

    return;
}

VOID
IssuePreservedSettingCommands(
    _In_ PAHCI_CHANNEL_EXTENSION    ChannelExtension,
    _In_opt_ PSTORAGE_REQUEST_BLOCK Srb
  )
/*++
    Uses the local SRB to send down the next Preserved Setting
It assumes:
    Local SRB is only used for restoring preserved settings
Called by:
    RestorePreservedSettings,
    IssueInitCommands,
    Itself indirectly through local SRB callback

It performs:
    1 Verify local SRB is not in use
    2 Find the next Preserved Setting
    3 Send it

Affected Variables/Registers:
    none
--*/
{
    UCHAR i;
    ULONG allocated;
    ATA_TASK_FILE taskFile = {0};

    UNREFERENCED_PARAMETER(Srb);

    RecordExecutionHistory(ChannelExtension, 0x00000043); // IssuePreservedSettingCommands

    //1 Verify local SRB is not in use
    allocated = GetOccupiedSlots(ChannelExtension);

    if ((allocated & (1 << 0)) > 0) {

        // Already restoring preserved Settings
        RecordExecutionHistory(ChannelExtension, 0x10010043); // IssuePreservedSettingCommands Slot 0 in use
        return;
    }

    //2 find the next command to send
    for (i = 0; i < MAX_SETTINGS_PRESERVED; i++) {
        if ( (ChannelExtension->PersistentSettings.SlotsToSend & (1 << i)) > 0 ) {
            ChannelExtension->PersistentSettings.SlotsToSend &= ~(1 << i);
            break;
        }
    }

    // Perhaps there is none.  Done.
    if ( i >= MAX_SETTINGS_PRESERVED) {
        // Release active reference for process of restore preserved settings
        if (ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced == 1) {
            PortReleaseActiveReference(ChannelExtension, NULL);
            ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 0;
        }

        RecordExecutionHistory(ChannelExtension, 0x10020043); // IssuePreservedSettingCommands done and clear flag

        InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3
        InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22

        return;
    }

    //3 Otherwise use the LocalSRB to send the command. When it is done, call this routine again
    taskFile.Current.bFeaturesReg = ChannelExtension->PersistentSettings.CommandParams[i].Features;
    taskFile.Current.bSectorCountReg = ChannelExtension->PersistentSettings.CommandParams[i].SectorCount;
    taskFile.Current.bDriveHeadReg = 0xA0;
    taskFile.Current.bCommandReg = IDE_COMMAND_SET_FEATURE;

    BuildLocalCommand(ChannelExtension, &taskFile, IssuePreservedSettingCommands);

    return;
}

VOID
IssueInitCommands(
    _In_ PAHCI_CHANNEL_EXTENSION    ChannelExtension,
    _In_opt_ PSTORAGE_REQUEST_BLOCK Srb
  )
/*++
    Uses the local SRB to send down the next Init Command or Preserved Setting Command
It assumes:
    Local SRB is only used for restoring preserved settings
Called by:
    AhciIssueInitCommands,
    Itself indirectly through local SRB callback

It performs:
    1 Verify local SRB is not in use
    2 Find the next Init Command or Preserved Setting Command
    3 Send it

Affected Variables/Registers:
    none
--*/
{
    ULONG           allocated;
    PATA_TASK_FILE  taskFile;

    UNREFERENCED_PARAMETER(Srb);

    RecordExecutionHistory(ChannelExtension, 0x00000044); // IssueInitCommands

    // Verify local SRB is not in use
    allocated = GetOccupiedSlots(ChannelExtension);

    if ((allocated & (1 << 0)) > 0) {

        // Already restoring preserved Settings
        RecordExecutionHistory(ChannelExtension, 0x10010044); // IssueInitCommands slot 0 in use
        return;
    }

    // if all Init commands have been sent, send Preserved Setting Commands
    if (ChannelExtension->DeviceInitCommands.CommandToSend >= ChannelExtension->DeviceInitCommands.ValidCommandCount) {

        RecordExecutionHistory(ChannelExtension, 0x10020044); // IssueInitCommands init commands done, send Preserved Setting Commands

        ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots;
        IssuePreservedSettingCommands(ChannelExtension, NULL);
        return;
    }

    // find the next command to send
    taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + ChannelExtension->DeviceInitCommands.CommandToSend;
    taskFile->Current.bDriveHeadReg = 0xA0;

    //
    // If this is a PUIS spin-up command and a spin-up is *not* needed then
    // skip to the next command.  This command is not needed if:
    //  * PUIS is not enabled; or
    //  * The device doesn't support PUIS, is a hybrid, or is an SSD; or
    //  * The device is currently powered up (not in D3)
    //
    if (taskFile->Current.bFeaturesReg == IDE_FEATURE_PUIS_SPIN_UP &&
        (ChannelExtension->StateFlags.PuisEnabled == FALSE ||
         NeedsPuisSpinUpOnPowerUp(ChannelExtension) == FALSE ||
         ChannelExtension->DevicePowerState != StorPowerDeviceD3)) {
        ChannelExtension->DeviceInitCommands.CommandToSend++;
        taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + ChannelExtension->DeviceInitCommands.CommandToSend;
        taskFile->Current.bDriveHeadReg = 0xA0;
    }

    BuildLocalCommand(ChannelExtension, taskFile, IssueInitCommands);
    ChannelExtension->DeviceInitCommands.CommandToSend++;

    return;
}


VOID
SetDateAndTimeCompletion(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb
    )
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);

    UNREFERENCED_PARAMETER(ChannelExtension);

    AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION));

    srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND;
    srbExtension->CompletionRoutine = NULL;
    SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE);

    return;
}

VOID
BuildSetDateAndTimeTaskFile(
    _In_ PATA_TASK_FILE  TaskFile
)
{
    LARGE_INTEGER       temp;
    ULONGLONG           now;

    AhciZeroMemory((PCHAR)TaskFile, sizeof(ATA_TASK_FILE));

    //setup TaskFile
    StorPortQuerySystemTime(&temp);

    now = (ULONGLONG) temp.QuadPart;
    now /= 10000;  //4 orders of magnitude

    // 2) subtract 369 years in seconds.
    //    Number of milliseconds in a Julian year = 31,557,600,000 (1millisecond * 1000second * 60minute * 60hour * 24day * 365.25year)
    //    369 * 31,557,600,000  = 11,644,754,400,000 (0xA97 4173 1300)
    now -= 0xA9741731300;

    // Example 2010-09-29 10 am = 0x1cb5ffd`22c1bf5e
    // 0x1cb5ffd`22c1bf5e/10000 = 0xbc2`8f496393 (12930255512467 or 12930255512467.8494) milliseconds
    // 0xbc2`8f496393 - 0xa97'41731300 = 0x012b`4dd65093
    // NOTE: this number won't roll over for another ~8700 years.

    TaskFile->Current.bSectorNumberReg =     (UCHAR) (0xFF & now);
    now >>= 8;
    TaskFile->Current.bCylLowReg =           (UCHAR) (0xFF & now);
    now >>= 8;
    TaskFile->Current.bCylHighReg =          (UCHAR) (0xFF & now);
    now >>= 8;
    TaskFile->Previous.bSectorNumberReg =    (UCHAR) (0xFF & now);
    now >>= 8;
    TaskFile->Previous.bCylLowReg =          (UCHAR) (0xFF & now);
    now >>= 8;
    TaskFile->Previous.bCylHighReg =         (UCHAR) (0xFF & now);

    TaskFile->Current.bDriveHeadReg = 0xA0;
    TaskFile->Current.bCommandReg = IDE_COMMAND_SET_DATE_AND_TIME;

    return;
}

VOID
IssueSetDateAndTimeCommand(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Inout_ PSCSI_REQUEST_BLOCK Srb,
    _In_ BOOLEAN SendStandBy
  )
/*++
It assumes:
    Srb is not the local SRB.
Called by:
    AhciHwStartIo with SRB_FUNCTION_SHUTDOWN

It performs:
    1 Builds a Set Date & Time taskfile and associates it with the provided Srb.
Affected Variables/Registers:
    none

--*/
{

    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb);

    NT_ASSERT(Srb != &ChannelExtension->Local.Srb);

    UNREFERENCED_PARAMETER(ChannelExtension);

    //setup TaskFile
    BuildSetDateAndTimeTaskFile(&srbExtension->TaskFile);

    srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND;
    srbExtension->CompletionRoutine = (SendStandBy ? SetDateAndTimeCompletion : NULL);
}

BOOLEAN
AhciDeviceInitialize (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
    RecordExecutionHistory(ChannelExtension, 0x00000007);   //AhciDeviceInitialize

  //1 update preserved commands per device needs.
    if (IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) {

        if (NeedToSetTransferMode(ChannelExtension)) {
            //1.1 Set DMA mode to this device
            UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_SET_TRANSFER_MODE, 0, 0x44);
        }

        //1.2 Persist Write Cache
        UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_ENABLE_WRITE_CACHE, 0, 0);

    } else if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) {
      //2.1 Persist SATA transfer mode for some SATAI/PATAPI bridge chips
        UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_SET_TRANSFER_MODE, 0, 0x42);

        if ( IsDeviceSupportsAN(ChannelExtension->DeviceExtension->IdentifyPacketData) &&
             !IsDeviceEnabledAN(ChannelExtension->DeviceExtension->IdentifyPacketData) ) {
        //2.2 Enable Asynchronous Notification if supported
            UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_ENABLE_SATA_FEATURE, 0, IDE_SATA_FEATURE_ASYNCHRONOUS_NOTIFICATION);
        }
    }

    //
    // Enable Power Up in Standby (PUIS) on hybrids if it's supported and not
    // already enabled.
    //
    if (IsDeviceHybridInfoSupported(ChannelExtension) &&
        ChannelExtension->DeviceExtension[0].IdentifyDeviceData->CommandSetSupport.PowerUpInStandby &&
        ChannelExtension->DeviceExtension[0].IdentifyDeviceData->CommandSetActive.PowerUpInStandby == FALSE) {
        UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_DISABLE_PUIS, IDE_FEATURE_ENABLE_PUIS, 0, 0);
        ChannelExtension->StateFlags.PuisEnabled = TRUE;
    }

    //3.1 evaluate ACPI _SDD method informing information about device connected.
    AhciPortEvaluateSDDMethod(ChannelExtension);

    //3.2 retrieve _GTF commands and add needed commands in list.
    AhciPortGetInitCommands(ChannelExtension);

  //5.1 Configure device with init commands and persistent configuration commands
    AhciPortIssueInitCommands(ChannelExtension);

    ActivateQueue(ChannelExtension, FALSE);

    RecordExecutionHistory(ChannelExtension, 0x10000007);//Exit AhciDeviceInitialize
    return TRUE;
}

VOID
AhciDeviceStart (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
/*
    Running at PASSIVE_LEVEL

    This function is called when IRP_MN_START_DEVICE is being processed.
    device registry access, ACPI calls can be processed in this function.
*/
{
    if (ChannelExtension == NULL) {
        return;
    }

    AhciDeviceInitialize(ChannelExtension);

    return;
}


__inline
BOOLEAN
IsLpmModeSetting(
    _In_ PSTOR_POWER_SETTING_INFO PowerInfo
    )
{
    if (PowerInfo->PowerSettingGuid.Data1 == 0x0b2d69d7) {
        if (PowerInfo->PowerSettingGuid.Data2 == 0xa2a1){
            if (PowerInfo->PowerSettingGuid.Data3 == 0x449c){
                if (PowerInfo->PowerSettingGuid.Data4[0] == 0x96){
                    if (PowerInfo->PowerSettingGuid.Data4[1] == 0x80){
                        if (PowerInfo->PowerSettingGuid.Data4[2] == 0xf9){
                            if (PowerInfo->PowerSettingGuid.Data4[3] == 0x1c){
                                if (PowerInfo->PowerSettingGuid.Data4[4] == 0x70){
                                    if (PowerInfo->PowerSettingGuid.Data4[5] == 0x52){
                                        if (PowerInfo->PowerSettingGuid.Data4[6] == 0x1c){
                                            if (PowerInfo->PowerSettingGuid.Data4[7] == 0x60){
                                                return TRUE;
                                            }  }  }  }  }  }  } }  }  }  }

    return FALSE;
}

__inline
BOOLEAN
IsLpmAdaptiveSetting(
    _In_ PSTOR_POWER_SETTING_INFO PowerInfo
    )
{
    if (PowerInfo->PowerSettingGuid.Data1 == 0xDAB60367) {
        if (PowerInfo->PowerSettingGuid.Data2 == 0x53FE){
            if (PowerInfo->PowerSettingGuid.Data3 == 0x4fbc){
                if (PowerInfo->PowerSettingGuid.Data4[0] == 0x82){
                    if (PowerInfo->PowerSettingGuid.Data4[1] == 0x5E){
                        if (PowerInfo->PowerSettingGuid.Data4[2] == 0x52){
                            if (PowerInfo->PowerSettingGuid.Data4[3] == 0x1D){
                                if (PowerInfo->PowerSettingGuid.Data4[4] == 0x06){
                                    if (PowerInfo->PowerSettingGuid.Data4[5] == 0x9D){
                                        if (PowerInfo->PowerSettingGuid.Data4[6] == 0x24){
                                            if (PowerInfo->PowerSettingGuid.Data4[7] == 0x56){
                                                return TRUE;
                                            }  }  }  }  }  }  } }  }  }  }

    return FALSE;
}

UCHAR
SetAllowedLpmStates(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
   // Return Value: disabled modes;
{
    UCHAR lpm;
    AHCI_SERIAL_ATA_CONTROL sctl;

    // 0h  No interface restrictions
    // 1h  Transitions to the Partial state disabled
    // 2h  Transitions to the Slumber state disabled
    // 3h  Transitions to both Partial and Slumber states disabled
    // disable LPM for eSATA port as hot-plug cannot be detected in partial or slumber state.
    if ((ChannelExtension->LastUserLpmPowerSetting == 0) ||
        !IsLPMCapablePort(ChannelExtension)) {
        lpm = 0x03; // slumber and partial disallowed
    } else {
        AHCI_COMMAND cmd;
        cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);

        if ((ChannelExtension->AutoPartialToSlumberInterval == 0) &&    // No software auto partial to slumber
             ( (ChannelExtension->AdapterExtension->CAP2.APST == 0) ||      // Host auto Partial to Slumber is not supported.
               (cmd.APSTE == 0) ) ) {                                       // Host auto Partial to Slumber is not enabled.
            lpm = 0x02; // partial allowed; slumber disallowed
        } else {
            lpm = 0x00; // partial allowed; slumber allowed
        }

    }

    if (ChannelExtension->AdapterExtension->CAP.SSC == 0) {
        // disable Slumber if controller does not support it.
        lpm |= 0x02;
    }

    if (ChannelExtension->AdapterExtension->CAP.PSC == 0) {
        // storahci LPM is to put device into partial, then transit into slumber according to defined interval value.
        // do not enable LPM if partial is not supported.
        // the case of device supporting slumber but not partial is very rare.
        lpm = 0x03;
    }

    //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions.
    sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong);
    sctl.IPM = lpm;
    StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong);

    return lpm;
}

BOOLEAN
AhciLpmSettingsModes(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ AHCI_LPM_POWER_SETTINGS LpmMode
    )
/*
    NOTE: this routine may prepared command in Local.Srb. Caller of this routine should try to start IO process.

Return Value:
    TRUE: the caller should start IO process
*/
{
    AHCI_COMMAND cmd;
    BOOLEAN needStartIo = FALSE;
    UCHAR sctlIpm = 0;

    RecordExecutionHistory(ChannelExtension, 0x00000047); // AhciLpmSettingsModes

    //Make sure the configuration supports LPM, otherwise, don't touch anything.
    if (NoLpmSupport(ChannelExtension) || !IsLPMCapablePort(ChannelExtension)) {
        return needStartIo;
    }

    ChannelExtension->LastUserLpmPowerSetting = (UCHAR)LpmMode.AsUlong;

    if (LpmMode.AsUlong == 0) {
        // Active Mode.
        //Turn LPM off as Active is chosen
        if (ChannelExtension->AdapterExtension->CAP.SALP == 1) {
            cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);
            if (cmd.ALPE != 0) {
                cmd.ALPE = 0;
                cmd.ASP = 0;
                StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong);
            }
        }

        //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions.
        sctlIpm = SetAllowedLpmStates(ChannelExtension);

        // Disable DIPM
        if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) {
            //The enable/disable state for device initiated power management shall persist across software reset.
            //The enable/disable state shall be reset to its default disabled state upon COMRESET.
            UpdateSetFeatureCommands(ChannelExtension,
                                    IDE_FEATURE_ENABLE_SATA_FEATURE,
                                    IDE_FEATURE_DISABLE_SATA_FEATURE,
                                    IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT,
                                    IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT);

            //Configure device with persistent configuration commands
            RestorePreservedSettings(ChannelExtension, FALSE);
            needStartIo = TRUE;
        }

    } else {
        // link power management is allowed.

        //Set PxSCTL.IPM for LPM allowed states.
        sctlIpm = SetAllowedLpmStates(ChannelExtension);

        // Setting HIPM if it's enabled.
        if ( (LpmMode.HipmEnabled > 0) &&
             (sctlIpm != 0x03) &&
             (ChannelExtension->AdapterExtension->CAP.SALP == 1) &&
             IsDeviceSupportsHIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData) ) {
            // If Partial is capable and device supports HIPM.

            // Turn on LPM and set it for Partial
            cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);
            cmd.ALPE = 1;
            cmd.ASP = 0; //0 = partial, 1 = slumber
            StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong);

        } else if (ChannelExtension->AdapterExtension->CAP.SALP == 1) {
            //Turn off HIPM if it's supported
            cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);
            if (cmd.ALPE != 0) {
                cmd.ALPE = 0;
                cmd.ASP = 0;
                StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong);
            }
        }

        // Setting DIPM if it's enabled.
        if ((LpmMode.DipmEnabled > 0) &&
            (sctlIpm != 0x03)) {
            // Enable DIPM feature.
            if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) {

                //The enable/disable state for device initiated power management shall persist across software reset.
                //The enable/disable state shall be reset to its default disabled state upon COMRESET.
                UpdateSetFeatureCommands(ChannelExtension,
                                        IDE_FEATURE_DISABLE_SATA_FEATURE,
                                        IDE_FEATURE_ENABLE_SATA_FEATURE,
                                        IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT,
                                        IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT);

                //Configure device with persistent configuration commands
                RestorePreservedSettings(ChannelExtension, FALSE);
                needStartIo = TRUE;
            }

        } else {
            // Disable DIPM feature.
            if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) {

                //The enable/disable state for device initiated power management shall persist across software reset.
                //The enable/disable state shall be reset to its default disabled state upon COMRESET.
                UpdateSetFeatureCommands(ChannelExtension,
                                        IDE_FEATURE_ENABLE_SATA_FEATURE,
                                        IDE_FEATURE_DISABLE_SATA_FEATURE,
                                        IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT,
                                        IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT);

                //Configure device with persistent configuration commands
                RestorePreservedSettings(ChannelExtension, FALSE);
                needStartIo = TRUE;
            }
        }

    }

    ++(ChannelExtension->TotalCountPowerSettingNotification);
    AhciTelemetryLogPowerSettingChange(ChannelExtension,
                                       (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                       AhciTelemetryEventIdLpmSettingsModes,
                                       "AhciLpmSettingsModes",
                                       0,
                                       sctlIpm,
                                       LpmMode.AsUlong
                                       );

    RecordExecutionHistory(ChannelExtension, 0x10000047); // AhciLpmSettingsModes Exit

    return needStartIo;
}


BOOLEAN
AhciPortPowerSettingNotification(
    IN PAHCI_CHANNEL_EXTENSION ChannelExtension,
    IN PSTOR_POWER_SETTING_INFO PowerInfo
    )
{                
    // do nothing if there is no device connected
    if ( (ChannelExtension->StartState.ChannelNextStartState == StartFailed) ||
         (ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist) ) {
        return FALSE;
    }

    //Make sure the configuration supports LPM, otherwise, don't touch anything.
    if (NoLpmSupport(ChannelExtension) || !IsLPMCapablePort(ChannelExtension)) {
        return FALSE;
    }

    // Validate input LPM data from the Power Manager
    if (PowerInfo->ValueLength != sizeof(ULONG)) {
        return FALSE;
    }

    if (!IsLpmModeSetting(PowerInfo) &&
        !IsLpmAdaptiveSetting(PowerInfo)) {
        // invalid power policy.
        return FALSE;
    }

    if (IsLpmAdaptiveSetting(PowerInfo)) {
        // max allowed value: 5 minutes (in ms)
        ULONG interval = (ULONG)*((PULONG)PowerInfo->Value);

        if (interval <= 300000) {
            UCHAR sctlIpm = 0;

            ChannelExtension->AutoPartialToSlumberInterval = interval;

            //Set PxSCTL.IPM register for LPM allowed states.
            sctlIpm = SetAllowedLpmStates(ChannelExtension);

            ++(ChannelExtension->TotalCountPowerSettingNotification);
            AhciTelemetryLogPowerSettingChange(ChannelExtension,
                                               (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                               AhciTelemetryEventIdLpmAdaptiveSetting,
                                               "LpmAdaptiveSetting",
                                               0,
                                               sctlIpm,
                                               ChannelExtension->AutoPartialToSlumberInterval
                                               );
        }

    } else if (IsLpmModeSetting(PowerInfo)) {
        BOOLEAN                 needRestartIo;
        AHCI_LPM_POWER_SETTINGS userLpmPowerSettings;

        userLpmPowerSettings.AsUlong = (ULONG)*((PULONG)PowerInfo->Value);

        needRestartIo = AhciLpmSettingsModes(ChannelExtension, userLpmPowerSettings);

        if (needRestartIo) {
            ActivateQueue(ChannelExtension, FALSE);
        }
    }

    return TRUE;
}

VOID
AhciAutoPartialToSlumber(
    _In_ PVOID AdapterExtension,
    _In_opt_ PVOID ChannelExtension
)
/*
    NOTE: input parameter - Context is required as this is a callback function. But it's not used by this function.
*/
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)ChannelExtension;

    AHCI_SERIAL_ATA_STATUS  ssts;
    AHCI_COMMAND            cmd;
    ULONG                   ci;
    ULONG                   sact;

    if (channelExtension == NULL) {
        NT_ASSERT(FALSE);
        return;
    }

    if (channelExtension->Px == NULL) {

        // The port has been stopped. Do not touch its registers.    
        // There is no need to transit the link power state to Slumber state.
        //
        // Note: 
        // Px is set to NULL in AhciPortStop function. StartIo spin lock is utilized to
        // prevent race condition with AhciPortStop function. StartIo spin lock is acquired 
        // before AhciPortStop is called. When we are here in AhciAutoPartialToSlumber, because
        // it is a timer callback function, StartIo spin lock is already held - Storport holds
        // StartIo spin lock before invoking miniport timer callback function.
        //       

        return;
    }


    NT_ASSERT(AdapterExtension == (PVOID)(channelExtension->AdapterExtension));

    UNREFERENCED_PARAMETER(AdapterExtension);

    // 1.1 check the Link Power State should be enabled.
    cmd.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong);
    ci = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->CI);
    sact = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SACT);

    if (((ci | sact) != 0) ||
        !PartialToSlumberTransitionIsAllowed(channelExtension, &cmd)) {
        // validate again in case any condition changed that not allowing StorAHCI to perform Partial to Slumber transition.
        StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - bailed out, request outstanding: CI: 0x%08X, SACT: 0x%08X \n", channelExtension->PortNumber, ci, sact);

        return;
    }

    ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong);

    // 1.3 check the Link Power State, should be Partial (value 2).
    if (ssts.IPM != 2) {
        StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - bailed out, current link state is not Partial: %1x \n", channelExtension->PortNumber, ssts.IPM);

        return;
    }

    // 2. Change LPM State.
    // Link should be in idle state (able to accept new interface commands).
    if (cmd.ICC == 0) {
        ULONG waitTime;
        ULONG waitTimeLimit = AHCI_LINK_POWER_STATE_CHANGE_TIMEOUT_US;
        UCHAR iccAttempts = 0;

        AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.InterfaceReady));

        //
        // Attempt to transition the link to Active.
        // By spec, Partial to Active transition should be completed in 10us. Reading register already takes sometime.
        // Poll for a little bit to give the link some time to go Active.
        // 
        cmd.ICC = 1;
        StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong);
        ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong);
        for (waitTime = 0; (waitTime < waitTimeLimit) && (ssts.IPM != 1); waitTime += 10) {
            //
            // Make a few attempts to program ICC if we haven't transitioned yet.
            //
            if (iccAttempts++ < 3) {
                cmd.ICC = 1;
                StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong);
            }

            StorPortStallExecution(10);  //10 microseconds
            ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong);
        }

        if (ssts.IPM != 1) {
            AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.ActiveFailCount));
            StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Failed to go to Active, SSTS.IPM = %u \n", channelExtension->PortNumber, ssts.IPM);
            return;
        }

        AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.ActiveSuccessCount));

        //
        // Attempt to transition the link to Slumber.
        // Poll for a little bit to give the link some time to go Slumber.
        //
        iccAttempts = 0;
        cmd.ICC = 6;
        StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong);
        ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong);
        for (waitTime = 0; (waitTime < waitTimeLimit) && (ssts.IPM != 6); waitTime += 10) {
            //
            // Make a few attempts to program ICC if we haven't transitioned yet.
            //
            if (iccAttempts++ < 3) {
                cmd.ICC = 6;
                StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong);
            }

            StorPortStallExecution(10);  //10 microseconds
            ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong);
        }

        if (ssts.IPM == 6) {
            AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.SlumberSuccessCount));
            StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Succeeded \n", channelExtension->PortNumber);
        } else {
            AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.SlumberFailCount));
            StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Failed, SSTS.IPM = %u \n", channelExtension->PortNumber, ssts.IPM);
        }
    } else {
        AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.InterfaceNotReady));
    }

    return;
}

BOOLEAN
AhciAdapterPowerSettingNotification(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension,
    _In_ PSTOR_POWER_SETTING_INFO PowerSettingInfo
    )
{
    ULONG i;
    for (i = 0; i <= AdapterExtension->HighestPort; i++) {
        if (AdapterExtension->PortExtension[i] != NULL) {
            AhciPortPowerSettingNotification(AdapterExtension->PortExtension[i], PowerSettingInfo);
        }
    }

    return TRUE;
}


VOID
AhciPortGetInitCommands(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
    // Read _GTF from ACPI
    ULONG                    status = STOR_STATUS_SUCCESS;
    ACPI_EVAL_INPUT_BUFFER   inputData = {0};
    PACPI_EVAL_OUTPUT_BUFFER acpiData = NULL;
    PACPI_METHOD_ARGUMENT    argument = NULL;
    ULONG                    acpiDataSize = 256;     // initial size, should be good enough for most cases
    ULONG                    returnedLength = 0;
    UCHAR                    gtfCommandCount = 0;

    // send SECURE_FREEZE_LOCK by default
    BOOLEAN                  sendSecureFreezeLock = TRUE;

    // clear Init Commands area. need to do this for device removed previously (StorAHCI only knows about adapter removal, not device removal)
    ChannelExtension->DeviceInitCommands.CommandCount = 0;
    ChannelExtension->DeviceInitCommands.ValidCommandCount = 0;
    ChannelExtension->DeviceInitCommands.CommandToSend = 0;
    if (ChannelExtension->DeviceInitCommands.CommandTaskFile != NULL) {
        StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)ChannelExtension->DeviceInitCommands.CommandTaskFile);
        ChannelExtension->DeviceInitCommands.CommandTaskFile = NULL;
    }


    inputData.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
    inputData.MethodNameAsUlong = ACPI_METHOD_GTF;

    status = StorPortAllocatePool(ChannelExtension->AdapterExtension,
                                  acpiDataSize,
                                  AHCI_POOL_TAG,
                                  (PVOID*)&acpiData);

    if (acpiData != NULL) {
        // call API to get required buffer size
        status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension,
                                          (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress,
                                          ACPI_METHOD_GTF,
                                          &inputData,
                                          sizeof(ACPI_EVAL_INPUT_BUFFER),
                                          (PVOID)acpiData,
                                          acpiDataSize,
                                          &returnedLength
                                          );

        // in case of the allocate buffer is too small, re-allocate buffer and retry the call
        if ( (status == STOR_STATUS_BUFFER_TOO_SMALL) && (acpiData->Length > acpiDataSize) ) {
            acpiDataSize = acpiData->Length;
            StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)acpiData);
            acpiData = NULL;
            // re-allocate a bigger buffer
            status = StorPortAllocatePool(ChannelExtension->AdapterExtension,
                                          acpiDataSize,
                                          AHCI_POOL_TAG,
                                          (PVOID*)&acpiData);

            if (acpiData != NULL) {
                status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension,
                                                  (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress,
                                                  ACPI_METHOD_GTF,
                                                  &inputData,
                                                  sizeof(ACPI_EVAL_INPUT_BUFFER),
                                                  (PVOID)acpiData,
                                                  acpiDataSize,
                                                  &returnedLength
                                                  );
            }
        }
    }

    // get _GTF commands count
    if ( (status == STOR_STATUS_SUCCESS) &&
         (acpiData != NULL) &&
         (acpiData->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) &&
         (acpiData->Count == 1) ) {

        argument = acpiData->Argument;

        if (argument->Type == ACPI_METHOD_ARGUMENT_BUFFER) {
            NT_ASSERT ((argument->DataLength % sizeof(ACPI_GTF_IDE_REGISTERS)) == 0);
            gtfCommandCount = (UCHAR)(argument->DataLength / sizeof(ACPI_GTF_IDE_REGISTERS));
        } else {
            NT_ASSERT(argument->Type == ACPI_METHOD_ARGUMENT_BUFFER);
        }
    }

    // Get Init Command count for devices

    //
    // calculate possible command count for memory allocation
    // an IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON command will be sent to device anyway.
    //
    ChannelExtension->DeviceInitCommands.CommandCount = gtfCommandCount + 1;

    if (IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters)) {
        if (ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) {
            ChannelExtension->DeviceInitCommands.CommandCount++;
        }
        
        if (sendSecureFreezeLock) {
            ChannelExtension->DeviceInitCommands.CommandCount++;
        }

        if (NeedsPuisSpinUpOnPowerUp(ChannelExtension)) {
            ChannelExtension->DeviceInitCommands.CommandCount++;
        }
    }

    // copy _GTF commands into buffer
    if (ChannelExtension->DeviceInitCommands.CommandCount > 0) {
        PATA_TASK_FILE taskFile;
        ULONG i = 0, gtfIndex = 0;

        status = StorPortAllocatePool(ChannelExtension->AdapterExtension,
                                      ChannelExtension->DeviceInitCommands.CommandCount * sizeof(ATA_TASK_FILE),
                                      AHCI_POOL_TAG,
                                      (PVOID*)&ChannelExtension->DeviceInitCommands.CommandTaskFile);

        if ( (status != STOR_STATUS_SUCCESS) || (ChannelExtension->DeviceInitCommands.CommandTaskFile == NULL) ) {
            goto exit;
        }

        AhciZeroMemory((PCHAR)ChannelExtension->DeviceInitCommands.CommandTaskFile, ChannelExtension->DeviceInitCommands.CommandCount * sizeof(ATA_TASK_FILE));

        //
        // If we may need to send the PUIS spin-up command to this device later
        // then insert it here.  It needs to be first so that the drive is spun
        // up and subsequent commands can succeed.
        //
        if (NeedsPuisSpinUpOnPowerUp(ChannelExtension) &&
            (ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) {
            taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i;
            taskFile->Current.bCommandReg = IDE_COMMAND_SET_FEATURE;
            taskFile->Current.bFeaturesReg = IDE_FEATURE_PUIS_SPIN_UP;
            i++;
        }

        //
        // Now copy over the _GTF commands.
        //
        for (gtfIndex = 0; gtfIndex < gtfCommandCount; gtfIndex++, i++) {
            StorPortCopyMemory(ChannelExtension->DeviceInitCommands.CommandTaskFile + i,
                               argument->Data + (gtfIndex * sizeof(ACPI_GTF_IDE_REGISTERS)),
                               sizeof(ACPI_GTF_IDE_REGISTERS)
                               );
        }

        // add IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON for all devices
        if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) {
            taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i;
            taskFile->Current.bFeaturesReg = IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON;
            taskFile->Current.bCommandReg = IDE_COMMAND_SET_FEATURE;
            i++;
        }

        // add more commands for ATA devices
        if (IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters)) {

            if (sendSecureFreezeLock) {
                
                if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) {
                    taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i;
                    taskFile->Current.bCommandReg = IDE_COMMAND_SECURITY_FREEZE_LOCK;
                    i++;
                }
            }

            if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) {
                if (ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) {
                    taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i;
                    //setup TaskFile
                    BuildSetDateAndTimeTaskFile(taskFile);
                    i++;
                }
            }
        }

        ChannelExtension->DeviceInitCommands.ValidCommandCount = (UCHAR)i;
    }

exit:
    if (acpiData != NULL) {
        StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)acpiData);
        acpiData = NULL;
    }

    return;
}

VOID
AhciPortEvaluateSDDMethod(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
    ULONG   status = STOR_STATUS_SUCCESS;
    ULONG   returnedLength = 0;
    PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData;
    PACPI_METHOD_ARGUMENT           argument;
    ULONG                           inputDataSize;

    // get the memory we need
    inputDataSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + sizeof(IDENTIFY_DEVICE_DATA);

    status = StorPortAllocatePool(ChannelExtension->AdapterExtension,
                                  inputDataSize,
                                  AHCI_POOL_TAG,
                                  (PVOID*)&inputData);

    if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) {
        goto Exit;
    }

    AhciZeroMemory((PCHAR)inputData, inputDataSize);

    inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    inputData->MethodNameAsUlong = ACPI_METHOD_SDD;
    inputData->Size = inputDataSize;
    inputData->ArgumentCount = 1;

    argument = inputData->Argument;
    argument->Type = ACPI_METHOD_ARGUMENT_BUFFER;
    argument->DataLength = sizeof(IDENTIFY_DEVICE_DATA);
    StorPortCopyMemory(argument->Data, ChannelExtension->DeviceExtension[0].IdentifyDeviceData, sizeof(IDENTIFY_DEVICE_DATA));

    status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension,
                                      (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress,
                                      ACPI_METHOD_SDD,
                                      (PVOID)inputData,
                                      inputDataSize,
                                      NULL,
                                      0,
                                      &returnedLength
                                      );

Exit:
    // we don't care about the return status
    UNREFERENCED_PARAMETER(status);

    if (inputData != NULL) {
        StorPortFreePool(ChannelExtension->AdapterExtension, inputData);
    }

    return;
}

VOID
AhciAdapterEvaluateDSMMethod(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
{
    ULONG   status = STOR_STATUS_SUCCESS;
    ULONG   returnedLength = 0;

    PACPI_METHOD_ARGUMENT           argument;
    PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData = NULL;
    ULONG                           inputDataSize;

    PACPI_EVAL_OUTPUT_BUFFER        outputData = NULL;
    ULONG                           outputDataSize;

    // 0. get output buffer ready, make sure the buffer is big enough.
    outputDataSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
                     AHCI_MAX_PORT_COUNT * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

    status = StorPortAllocatePool(AdapterExtension,
                                  outputDataSize,
                                  AHCI_POOL_TAG,
                                  (PVOID*)&outputData);

    if ( (status != STOR_STATUS_SUCCESS) || (outputData == NULL) ) {
        goto Exit;
    }

    AhciZeroMemory((PCHAR)outputData, outputDataSize);

    // 1. check if Link Power Management is supported in ACPI

    // get the memory we need

    // N.B. 4 arguments are stored in the ACPI_EVAL_INPUT_BUFFER_COMPLEX
    //      and passed to acpi.sys to eval _DSM.
    //
    //      ACPI_EVAL_INPUT_BUFFER_COMPLEX with 4 arguments.
    //      0 - GUID
    //      1 - ULONG  (revision id)
    //      2 - ULONG  (function index)
    //      3 - unused (package)
    //
    inputDataSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

    status = StorPortAllocatePool(AdapterExtension,
                                  inputDataSize,
                                  AHCI_POOL_TAG,
                                  (PVOID*)&inputData);

    if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) {
        goto Exit;
    }

    AhciZeroMemory((PCHAR)inputData, inputDataSize);

    inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    inputData->MethodNameAsUlong = ACPI_METHOD_DSM;
    inputData->Size = inputDataSize;
    inputData->ArgumentCount = 4;

    // argument 0 - Interface GUID
    argument = &inputData->Argument[0];
    argument->Type = ACPI_METHOD_ARGUMENT_BUFFER;
    argument->DataLength = sizeof(GUID);
    StorPortCopyMemory(&argument->Data[0], &LINK_POWER_ACPI_DSM_GUID, sizeof(GUID));

    // argument 1 - Revision number
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument,
                                     ACPI_METHOD_DSM_LINKPOWER_REVISION);

    // argument 2 - Function Index
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument,
                                     ACPI_METHOD_DSM_LINKPOWER_FUNCTION_SUPPORT);

    // argument 3 - Function-dependent package. not used for ACPI_METHOD_DSM_LINKPOWER_FUNCTION_SUPPORT
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, 0);
    argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE;


    status = StorPortInvokeAcpiMethod(AdapterExtension,
                                      NULL,             // NULL for Address field means the request is for Adapter
                                      ACPI_METHOD_DSM,
                                      (PVOID)inputData,
                                      inputDataSize,
                                      outputData,
                                      outputDataSize,
                                      &returnedLength
                                      );

    if ( (status != STOR_STATUS_SUCCESS) ||
         (returnedLength < (FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)))) ) {
        goto Exit;
    }

    argument = outputData->Argument;

    if ( ((argument->Argument & ACPI_METHOD_DSM_LINKPOWER_FUNCTION_QUERY) != 0) &&
         ((argument->Argument & ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL) != 0) ) {

        // If ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL is supported, it supports value (-1) that apply action to all ports.
        // Mark adapter supports _DSM. This indicates that it has capability to power on all ports and connected devices.
        if (AdapterExtension->StateFlags.SupportsAcpiDSM != TRUE) {
            AdapterExtension->StateFlags.SupportsAcpiDSM = TRUE;
        }


    }

Exit:
    if (inputData != NULL) {
        StorPortFreePool(AdapterExtension, inputData);
    }

    if (outputData != NULL) {
        StorPortFreePool(AdapterExtension, outputData);
    }

    return;
}


VOID
AhciPortAcpiDSMControl(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension,
    _In_ ULONG                   PortNumber,
    _In_ BOOLEAN                 Sleep
  )
{
    ULONG   status = STOR_STATUS_SUCCESS;
    ULONG   returnedLength = 0;

    PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData;
    PACPI_METHOD_ARGUMENT           argument;
    ULONG                           inputDataSize;

    // get the memory we need
    inputDataSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
                    ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
                    FIELD_OFFSET(ACPI_METHOD_ARGUMENT, Argument) +
                    2* ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));

    status = StorPortAllocatePool(AdapterExtension,
                                  inputDataSize,
                                  AHCI_POOL_TAG,
                                  (PVOID*)&inputData);

    if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) {
        goto Exit;
    }

    AhciZeroMemory((PCHAR)inputData, inputDataSize);

    inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    inputData->MethodNameAsUlong = ACPI_METHOD_DSM;
    inputData->Size = inputDataSize;
    inputData->ArgumentCount = 4;

    // argument 0 - Interface GUID
    argument = &inputData->Argument[0];
    argument->Type = ACPI_METHOD_ARGUMENT_BUFFER;
    argument->DataLength = sizeof(GUID);
    StorPortCopyMemory(&argument->Data[0], &LINK_POWER_ACPI_DSM_GUID, sizeof(GUID));

    // argument 1 - Revision number
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument,
                                     ACPI_METHOD_DSM_LINKPOWER_REVISION);

    // argument 2 - Function Index
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument,
                                     ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL);

    // argument 3 - Function-dependent package.
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE_EX;
    argument->DataLength = 2 * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));
    // argument 3 - Package entry 0
    argument = (PACPI_METHOD_ARGUMENT)argument->Data;
    if (PortNumber == (ULONG)-1) {
        // all 1s indicates power on operation is for all ports/devices
        NT_ASSERT(Sleep == FALSE);
        ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, PortNumber);
    } else {
        // convert PortNumber to be ACPI format of Address
        ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, (PortNumber << 16) | 0xFFFF);
    }
    // argument 3 - Package entry 1
    argument = ACPI_METHOD_NEXT_ARGUMENT(argument);
    ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, Sleep ? 0 : 1);

    status = StorPortInvokeAcpiMethod(AdapterExtension,
                                      NULL,             // NULL for Address field means the request is for Adapter
                                      ACPI_METHOD_DSM,
                                      (PVOID)inputData,
                                      inputDataSize,
                                      NULL,
                                      0,
                                      &returnedLength
                                      );

Exit:
    UNREFERENCED_PARAMETER(status);

    if (inputData != NULL) {
        StorPortFreePool(AdapterExtension, inputData);
    }

    return;
}


#pragma warning(pop) // un-sets any local warning changes



<|start_filename|>avstream/avscamera/mft0/MFT0Impl.h<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2013, Microsoft Corporation.

    File:

        Mft0Impl.h

    Abstract:

        The CSocMft0 class definition - an implementation of the iSocMft0
        interface found in Mft0.idl.

    History:

        created 4/26/2013

**************************************************************************/


#pragma once
#include "Mft0.h"
#include "SampleHelpers.h"
#include "Mft0clsid.h"

#include <wrl.h>

// CSocMft0
#define FaceDetectionDelayMax 2  //frames between emitting facedetection data

ULONG DllAddRef();
ULONG DllRelease();

using namespace Microsoft::WRL;

class CSocMft0:
    public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>,
    ISocMft0,
    IMFTransform,
    IInspectable>
{

public:

    static HRESULT CreateInstance(REFIID iid, void **ppMFT);

    CSocMft0():
        m_bEnableEffects(TRUE),
        m_bEnableVideoStabilization(FALSE),
        m_uiSourceStreamId(0),
        m_uiInternalState(0),
        m_uThumbnailScaleFactor(4)
    {
        InitializeCriticalSection(&m_critSec);
        m_stThumbnailFormat = MFVideoFormat_ARGB32;
        DllAddRef();

    }

    HRESULT FinalConstruct();

public:

    /*ISocMft0*/
    STDMETHOD(SetState)(UINT32 state);
    STDMETHOD(GetState)(UINT32 *pState);

    /*IInspectable*/
    STDMETHOD(GetIids)(
        _Out_ ULONG *iidCount,
        _Outptr_result_buffer_maybenull_(*iidCount)
        IID **iids
    );

    STDMETHOD(GetRuntimeClassName)(
        _Outptr_result_maybenull_ HSTRING *pClassName
    );

    STDMETHOD(GetTrustLevel)(
        _Out_ TrustLevel *trustLevel
    );

    /*IMFTransform*/
    STDMETHOD(GetStreamLimits)(
        _Out_ DWORD *pdwInputMinimum,
        _Out_ DWORD *pdwInputMaximum,
        _Out_ DWORD *pdwOutputMinimum,
        _Out_ DWORD *pdwOutputMaximum
    );

    STDMETHOD(GetStreamCount)(
        _Out_ DWORD *pcInputStreams,
        _Out_ DWORD *pcOutputStreams
    );

    STDMETHOD(GetStreamIDs)(
        DWORD dwInputIDArraySize,
        _Out_writes_(dwInputIDArraySize) DWORD *pdwInputIDs,
        DWORD dwOutputIDArraySize,
        _Out_writes_(dwOutputIDArraySize) DWORD *pdwOutputIDs
    );

    STDMETHOD(GetInputStreamInfo)(
        DWORD dwInputStreamID,
        _Out_ MFT_INPUT_STREAM_INFO *pStreamInfo
    );

    STDMETHOD(GetOutputStreamInfo)(
        DWORD dwOutputStreamID,
        _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo
    );

    STDMETHOD(GetAttributes)(
        _Outptr_result_maybenull_ IMFAttributes **pAttributes
    );

    STDMETHOD(GetInputStreamAttributes)(
        DWORD dwInputStreamID,
        _Outptr_result_maybenull_ IMFAttributes **pAttributes
    );

    STDMETHOD(GetOutputStreamAttributes)(
        DWORD dwOutputStreamID,
        _Outptr_result_maybenull_
        IMFAttributes **pAttributes
    );

    STDMETHOD(DeleteInputStream)(
        DWORD dwStreamID
    );

    STDMETHOD(AddInputStreams)(
        DWORD cStreams,
        _In_ DWORD *adwStreamIDs
    );

    STDMETHOD(GetInputAvailableType)(
        DWORD dwInputStreamID,
        DWORD dwTypeIndex,
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );

    STDMETHOD(GetOutputAvailableType)(
        DWORD dwOutputStreamID,
        DWORD dwTypeIndex,
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );

    STDMETHOD(SetInputType)(
        DWORD dwInputStreamID,
        _In_opt_ IMFMediaType *pType,
        DWORD dwFlags);

    STDMETHOD(SetOutputType)(
        DWORD dwOutputStreamID,
        _In_opt_ IMFMediaType *pType,
        DWORD dwFlags);

    STDMETHOD(GetInputCurrentType)(
        DWORD dwInputStreamID,
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );

    STDMETHOD(GetOutputCurrentType)(
        DWORD dwOutputStreamID,
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );

    STDMETHOD(GetInputStatus)(
        DWORD dwInputStreamID,
        _Out_ DWORD *pdwFlags
    );

    STDMETHOD(GetOutputStatus)(
        _Out_ DWORD *pdwFlags
    );

    STDMETHOD(SetOutputBounds)(
        LONGLONG hnsLowerBound,
        LONGLONG hnsUpperBound
    );

    STDMETHOD(ProcessEvent)(
        DWORD dwInputStreamID,
        _In_opt_ IMFMediaEvent *pEvent
    );

    STDMETHOD(ProcessMessage)(
        MFT_MESSAGE_TYPE eMessage,
        ULONG_PTR ulParam
    );

    STDMETHOD(ProcessInput)(
        DWORD dwInputStreamID,
        IMFSample *pSample,
        DWORD dwFlags
    );

    STDMETHOD(ProcessOutput)(
        DWORD dwFlags,
        DWORD cOutputBufferCount,
        MFT_OUTPUT_DATA_BUFFER *pOutputSamples,
        DWORD *pdwStatus
    );

protected:
    virtual ~CSocMft0()
    {
        DeleteCriticalSection(&m_critSec);
        DllRelease();
    }

    STDMETHOD(GetMediaType)(
        _In_ DWORD  dwStreamID,
        _In_ DWORD dwTypeIndex,
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );

    STDMETHOD(IsMediaTypeSupported)(
        _In_ UINT uiStreamId,
        _In_opt_ IMFMediaType *pIMFMediaType,
        _Outptr_opt_result_maybenull_ IMFMediaType **ppIMFMediaTypeFull = NULL
    );

    STDMETHOD(GenerateMFMediaTypeListFromDevice)();

    _Success_(return == 0)
    STDMETHOD(FindMediaIndex)(
        _In_ UINT uiStreamId,
        _In_ IMFMediaType *pIMFMediaType,
        _Out_ UINT *puiMediaIndex
    );

    // HasPendingOutput: Returns TRUE if the MFT is holding an input sample.
    BOOL HasPendingOutput() const
    {
        return m_spSample != NULL;
    }

    HRESULT CreateOutputSample(
        _Outptr_result_maybenull_ IMFSample **ppSample
    );

    HRESULT GetPreviewMediaType(
        _Outptr_result_maybenull_ IMFMediaType **ppType
    );
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
    HRESULT ProcessMetadata();

    HRESULT ParseMetadata_PreviewAggregation(
        _In_ PKSCAMERA_METADATA_ITEMHEADER pItem,
        _In_ IMFAttributes *pMetaDataAttributes
    );

    HRESULT ParseMetadata_ImageAggregation(
        _In_ PKSCAMERA_METADATA_ITEMHEADER pItem,
        _In_ IMFAttributes *pMetaDataAttributes
    );

    HRESULT ParseMetadata_Histogram(
        _In_ PKSCAMERA_METADATA_ITEMHEADER pItem,
        _In_ IMFAttributes *pMetaDataAttributes
    );

    HRESULT ParseMetadata_FaceDetection(
        _In_ PKSCAMERA_METADATA_ITEMHEADER pItem,
        _In_ IMFAttributes *pMetaDataAttributes
    );
#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
    HRESULT FillBufferLengthFromMediaType(
        _In_ IMFMediaType *pPreviewType,
        _Inout_ IMFMediaBuffer *pBuffer
    );

    STDMETHOD(OnFlush)();

    HRESULT GetVideoStabilizationEnabled();

    HRESULT GetThumbnailResolution();

    HRESULT OnProcessImage(
        _Outptr_result_maybenull_
        IMFSample **ppIMFOutputSample
    );

    CRITICAL_SECTION            m_critSec;

    ComPtr<IMFSample>          m_spSample;                 // Input sample.
    ComPtr<IMFMediaType>       m_spInputType;              // Input media type.
    ComPtr<IMFMediaType>       m_spOutputType;             // Output media type.

    // Image transform function. (Changes based on the media type.)
    ComPtr<IMFAttributes>      m_spInputAttributes;
    ComPtr<IMFAttributes>      m_spGlobalAttributes;
    GUID                        m_stStreamType;
    ComPtr<IMFTransform>       m_spSourceTransform;
    volatile BOOL               m_bEnableEffects;
    BOOL                        m_bEnableVideoStabilization;
    UINT                        m_uiSourceStreamId;
    UINT                        m_uiInternalState;
    BYTE                        m_uThumbnailScaleFactor;
    GUID                        m_stThumbnailFormat;

    std::vector<ComPtr<IMFMediaType>>
                                  m_listOfMediaTypes;

};

inline HRESULT MFT0CreateInstance(REFIID riid, void **ppv)
{
    return CSocMft0::CreateInstance(riid, ppv);
}

//CoCreateableClass(CSocMft0);

<|start_filename|>general/SimpleMediaSource/MediaSource/SimpleMediaSource.h<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
#pragma once
#ifndef SIMPLEMEDIASOURCE_H
#define SIMPLEMEDIASOURCE_H

// Example Custom Property implemented by SimpleMediaSource
// 
// {0CE2EF73-4800-4F53-9B8E-8C06790FC0C7}
static const GUID PROPSETID_SIMPLEMEDIASOURCE_CUSTOMCONTROL =
{ 0xce2ef73, 0x4800, 0x4f53, { 0x9b, 0x8e, 0x8c, 0x6, 0x79, 0xf, 0xc0, 0xc7 } };

enum
{
    KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE = 0,
    KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_END  // all ids must define before this.
};

#define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_GRAYSCALE        0x00FFFFFFL
#define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_RED              0x00FF0000L
#define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_GREEN            0x0000FF00L
#define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_BLUE             0x000000FFL

typedef struct {
    ULONG      StremID;
    ULONG      ColorMode;
} KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_S, * PKSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_S;

namespace winrt::WindowsSample::implementation
{
    // forward declaration
    struct SimpleMediaStream;
    
    struct SimpleMediaSource : winrt::implements<SimpleMediaSource, IMFMediaSourceEx, IMFGetService, IKsControl, IMFSampleAllocatorControl>
    {
        SimpleMediaSource() = default;

    private:
        enum class SourceState
        {
            Invalid, Stopped, Started, Shutdown
        };

    public:
        // IMFMediaEventGenerator (inherits by IMFMediaSource)
        IFACEMETHODIMP BeginGetEvent(_In_ IMFAsyncCallback* pCallback, _In_ IUnknown* punkState) override;
        IFACEMETHODIMP EndGetEvent(_In_ IMFAsyncResult* pResult, _Out_ IMFMediaEvent** ppEvent) override;
        IFACEMETHODIMP GetEvent(DWORD dwFlags, _Out_ IMFMediaEvent** ppEvent) override;
        IFACEMETHODIMP QueueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, _In_ const PROPVARIANT* pvValue) override;

        // IMFMediaSource (inherits by IMFMediaSourceEx)
        IFACEMETHODIMP CreatePresentationDescriptor(_Out_ IMFPresentationDescriptor** ppPresentationDescriptor) override;
        IFACEMETHODIMP GetCharacteristics(_Out_ DWORD* pdwCharacteristics) override;
        IFACEMETHODIMP Pause() override;
        IFACEMETHODIMP Shutdown() override;
        IFACEMETHODIMP Start(_In_ IMFPresentationDescriptor* pPresentationDescriptor, _In_ const GUID* pguidTimeFormat, _In_ const PROPVARIANT* pvarStartPosition) override;
        IFACEMETHODIMP Stop() override;

        // IMFMediaSourceEx
        IFACEMETHODIMP GetSourceAttributes(_COM_Outptr_ IMFAttributes** ppAttributes) override;
        IFACEMETHODIMP GetStreamAttributes(DWORD dwStreamIdentifier, _COM_Outptr_ IMFAttributes** ppAttributes) override;
        IFACEMETHODIMP SetD3DManager(_In_opt_ IUnknown* pManager) override;

        // IMFGetService
        IFACEMETHODIMP GetService(_In_ REFGUID guidService, _In_ REFIID riid, _Out_ LPVOID* ppvObject) override;

        // IKsControl
        IFACEMETHODIMP KsProperty(
            _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
            _In_ ULONG ulPropertyLength,
            _Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
            _In_ ULONG ulDataLength,
            _Out_ ULONG* pBytesReturned) override;
        IFACEMETHODIMP KsMethod(
            _In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod,
            _In_ ULONG ulMethodLength,
            _Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pMethodData,
            _In_ ULONG ulDataLength,
            _Out_ ULONG* pBytesReturned) override;
        IFACEMETHODIMP KsEvent(
            _In_reads_bytes_opt_(ulEventLength) PKSEVENT pEvent,
            _In_ ULONG ulEventLength,
            _Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pEventData,
            _In_ ULONG ulDataLength,
            _Out_opt_ ULONG* pBytesReturned) override;

        // IMFSampleAllocatorControl
        IFACEMETHODIMP SetDefaultAllocator(
            _In_  DWORD dwOutputStreamID,
            _In_  IUnknown* pAllocator) override;

        IFACEMETHODIMP GetAllocatorUsage(
            _In_  DWORD dwOutputStreamID,
            _Out_  DWORD* pdwInputStreamID,
            _Out_  MFSampleAllocatorUsage* peUsage) override;

        // Non-Interface functions
        HRESULT Initialize();

    private:
        HRESULT _CheckShutdownRequiresLock();
        HRESULT _ValidatePresentationDescriptor(_In_ IMFPresentationDescriptor* pPresentationDescriptor);
        HRESULT _CreateSourceAttributes();
        HRESULT _GetStreamDescriptorByStreamId(_In_ DWORD dwStreamId, _Out_ DWORD* pdwStreamIdx, _Out_ bool* pSelected, _COM_Outptr_ IMFStreamDescriptor** ppStreamDescriptor);
        HRESULT _GetMediaStreamById(_In_ DWORD dwStreamId, _COM_Outptr_ SimpleMediaStream** ppMediaStream);

        winrt::slim_mutex m_Lock;
        SourceState m_sourceState{ SourceState::Invalid };

        wil::com_ptr_nothrow<IMFMediaEventQueue> m_spEventQueue;
        wil::com_ptr_nothrow<IMFPresentationDescriptor> m_spPresentationDescriptor;
        wil::com_ptr_nothrow<IMFAttributes> m_spAttributes;
        wil::unique_cotaskmem_array_ptr<wil::com_ptr_nothrow<SimpleMediaStream>> m_streamList;

        const DWORD NUM_STREAMS = 1;
    };
}

#endif


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/ServiceWin32API.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

--*/

#pragma once

VOID
WINAPI
ServiceMain(
    DWORD Argc,
    PWSTR *Argv
    );

VOID
WINAPI
ServiceCtrlHandler(
    DWORD Ctrl
    );

BOOL
UpdateServiceStatus(
    __in_opt SERVICE_STATUS_HANDLE hSvcHandle,
    __in     DWORD                 dwCurrentState,
    __in     DWORD                 dwWin32ExitCode
    );

VOID
CALLBACK
ServiceStopCallback(
    _In_ PVOID   lpParameter,
    _In_ BOOLEAN TimerOrWaitFired
    );

VOID
ServiceStop(
    _In_ DWORD ExitCode
    );

DWORD
WINAPI
ServiceRunningWorkerThread(
    _In_ PVOID lpThreadParameter
    );

DWORD
WINAPI
ServiceStopWorkerThread(
    _In_ PVOID lpThreadParameter
    );


<|start_filename|>avstream/avscamera/sys/hwsim.h<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2001, Microsoft Corporation.

    File:

        hwsim.h

    Abstract:

        This file contains the definition of the base class, CHardwareSimulation,
        used to simulate a camera's capture stream.

        Our simulation cannot mimic a real camera with any precision.  A real
        camera would likely have dedicated hardware to provide interrupts, do 
        DMA transfers of frames from an internal buffer as well as do scaling 
        and on-the-fly colorspace and planar format conversions so as to supply
        frames for preview, video and photo in varying resolutions and
        colorspaces.  
        
        This simulation does not have such hardware, so instead it synthesizes
        a frame on the fly for each local pin as required.  The frame is
        synthesized from common state information, so the images produced 
        should be comparable from pin to pin.  CHardwareSimulation is used as
        a base class for each simulated pin / hardware.

        A CHardwareSimulation contains state, a list of frame bufferss and a 
        timer that is used in place of a hardware interrupt.  The timer 
        schedules a workitem that is used to synthesize an image, select a 
        buffer and copy that image into the buffer.

        Specializations of CHardwareSimulation provide differing metadata and
        implement specialized functionality for photo sequence and variable
        photo sequence.

    History:

        created 3/9/2001

**************************************************************************/

#define SCATTER_GATHER_MAPPINGS_MAX 128

//  forward class references
class CSensor;
class CHardwareSimulation;
class CHwSimTimer;

//  internal structure definitions

//  Face metadata structure.  Face metadata can be synthesized on any pin.
class CAMERA_METADATA_FACE_DETECTION
    : public CAMERA_METADATA_FACEHEADER
{
public:
    METADATA_FACEDATA           Data[MAX_FACES];
};

//  Information about each frame buffer in the queue.
typedef struct _SCATTER_GATHER_ENTRY
{
    LIST_ENTRY ListEntry;
    PKSSTREAM_POINTER CloneEntry;
    PUCHAR Virtual;
    ULONG ByteCount;
    PHOTOCONFIRMATION_INFO              PhotoConfirmationInfo;
} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY;

//
//  A specialization of a KPassiveTimer that holds a back pointer to the
//  CHardwareSimulation that owns it.
//
class CHwSimTimer
    : public KPassiveTimer
{
    CHardwareSimulation *m_Parent;

public:
    CHwSimTimer(
        _In_    CHardwareSimulation *Parent
    );

    //
    //  Sets the timer
    //
    BOOLEAN
    Set(
        _In_    LARGE_INTEGER        DueTime
    );

private:
    static
    void
    Handler(
        _In_opt_    PVOID           Context
    );
};

class CHardwareSimulation
{
protected:
    CSynthesizer *m_Synthesizer;                // Synthesizer for various formats.
    LONGLONG m_TimePerFrame;
    ULONG m_Width;
    ULONG m_Height;
    ULONG m_ImageSize;
    LONG m_PinID;
    KMutex  m_ListLock;
    LIST_ENTRY m_ScatterGatherMappings;

    NPAGED_LOOKASIDE_LIST m_ScatterGatherLookaside;
    PIN_STATE m_PinState;
    ULONG m_ScatterGatherMappingsMax;
    ULONG m_NumMappingsCompleted;
    ULONG m_ScatterGatherMappingsQueued;
    ULONG m_ScatterGatherBytesQueued;
    LONGLONG    m_NumFramesSkipped;
    LONGLONG    m_InterruptTime;
    LARGE_INTEGER m_StartTime;

    CSensor *m_Sensor;
    CHwSimTimer  m_IsrTimer;

    PHOTOCONFIRMATION_INFO  m_PhotoConfirmationEntry;

    //  Cached values used for Locking simulated state.
    LONGLONG    m_LastReportedExposureTime;
    ULONG       m_LastReportedWhiteBalance;

    //  Used to simulate the skipping of frames when reporting face detection data.
    ULONG       m_FaceDetectionDelay;
    CAMERA_METADATA_FACE_DETECTION  m_LastFaceDetect;

    //  Copy a synthesized framebuffer.
    virtual
    NTSTATUS
    FillScatterGatherBuffers();

    //  Overloadable function for attaching metadata to a frame.
    virtual
    void
    EmitMetadata(
        _Inout_ PKSSTREAM_HEADER   pStreamHeader
    );

    virtual
    bool
    CheckForAvailableBuffer();

    virtual
    NTSTATUS
    CommitImageData(PSCATTER_GATHER_ENTRY sGEntry, ULONG stride);

    virtual
    NTSTATUS
    ValidateBuffer(PSCATTER_GATHER_ENTRY sGEntry);

    //  Helper function to generate random face metadata.
    void
    EmitFaceMetadata(
        _Inout_ PKSSTREAM_HEADER    pStreamHeader,
        _In_    ULONG               Count,
        _In_    ULONGLONG           Flags,
        _In_    ULONG               DelayLimit=30
    );

    //  Release an entry from our queue.
    void
    FreeSGEntry(
        _In_        PLIST_ENTRY Entry,
        _In_        PCWSTR      EventName=L"StreamPointer freed",
        _In_opt_    LPCGUID     Activity=NULL,
        _In_        ULONG       DataUsed=0
    );

    //  Release our entire queue.
    void
    FreeSGList(
        _In_        PLIST_ENTRY List,
        _In_        PCWSTR      EventName=L"StreamPointer freed",
        _In_opt_    LPCGUID     Activity=NULL
    );

public:

    //  Debug helper
    LONGLONG GetSkippedFrameCount()
    {
        return m_NumFramesSkipped;
    }

    //  Constructor
    CHardwareSimulation (
        _Inout_ CSensor *Sensor,
        _In_    LONG PinID
    );

    //  Destructor
    virtual
    ~CHardwareSimulation ();

    //  Periodic interrupt handling
    virtual
    void
    FakeHardware (
    );

    //  Start streaming.
    virtual
    NTSTATUS
    Start (
        CSynthesizer *ImageSynth,
        IN LONGLONG TimePerFrame,
        IN ULONG Width,
        IN ULONG Height,
        IN ULONG ImageSize
    );

    //  Pause streaming
    virtual
    NTSTATUS
    Pause (
        IN BOOLEAN Pausing
    );

    //  Stop streaming.
    virtual
    NTSTATUS
    Stop();

    //  Reset streaming.
    virtual
    NTSTATUS
    Reset(
        IN PKSPIN Pin
    );

    //  Reset streaming.
    virtual
    NTSTATUS
    Reset();

    //  Add a frame buffer to our queue.
    virtual
    ULONG
    ProgramScatterGatherMappings (
        IN PKSSTREAM_POINTER *Clone,
        IN PUCHAR *Buffer,
        IN PKSMAPPING Mappings,
        IN ULONG MappingsCount,
        IN ULONG MappingStride
    );

    //  Initialization.
    virtual
    BOOLEAN
    Initialize();

    //  Number of frames completed during this run.
    ULONG
    ReadNumberOfMappingsCompleted();

    //  Get the QPC measured in 100ns units.
    ULONGLONG
    ConvertQPCtoTimeStamp(
        _In_opt_    PLARGE_INTEGER pQpcTime
    );

    //  Get the current frame settings.
    virtual
    ISP_FRAME_SETTINGS *
    GetIspSettings(void);

    //  So a photo sim can ask a preview sim for a confirmation frame.
    NTSTATUS
    GeneratePhotoConfirmation(
        _In_ ULONG      PfsFrameNumber,
        _In_ LONGLONG   time
    );

    //  Get the exposure value for the current frame.
    LONGLONG
    GetCurrentExposureTime();

    //  Get the White balance for the current frame.
    ULONG
    GetCurrentWhiteBalance();

    //  Get the ISO Speed value for the current frame.
    ULONG
    GetCurrentISOSpeed();

    //  Get the Lens position for the current frame.
    ULONG
    GetCurrentLensPosition();

    //  Get the Scene Mode for the current frame.
    ULONG
    GetCurrentSceneMode();

    //  Translate Exposure Mode to an Exif Exposure Program value
    USHORT
    GetExposureProgram();

    //  Pin state accessor...
    PIN_STATE
    GetState()
    {
        return m_PinState;
    }

    friend class CHwSimTimer;
};

//
//  Sets the timer using our callback and our parent CHardwareSimulation.
//
inline
BOOLEAN
CHwSimTimer::
Set(
    _In_    LARGE_INTEGER        DueTime
)
{
    return KPassiveTimer::Set( DueTime, Handler, m_Parent );
}


<|start_filename|>audio/sysvad/EndpointsCommon/NewDelete.cpp<|end_filename|>
/*****************************************************************************
* NewDelete.cpp -  CPP placement new and delete operators implementation
*****************************************************************************
* Copyright (c) Microsoft Corporation All Rights Reserved
*
* Module Name:
*
* NewDelete.cpp
*
* Abstract:
*
*   Definition of placement new and delete operators.
*
*/

#ifdef _NEW_DELETE_OPERATORS_
#ifdef __cplusplus
extern "C" {
#include <wdm.h>
}
#else
#include <wdm.h>
#endif

#include "newDelete.h"
#include "sysvad.h"

#pragma code_seg()
/*****************************************************************************
* Functions
*/

/*****************************************************************************
* ::new()
*****************************************************************************
* New function for creating objects with a specified allocation tag.
*/
PVOID operator new
(
    size_t      iSize,
    POOL_FLAGS  poolFlags,
    ULONG       tag
)
{
    PVOID result = ExAllocatePool2(poolFlags, iSize, tag);

    return result;
}


/*****************************************************************************
* ::new()
*****************************************************************************
* New function for creating objects with a specified allocation tag.
*/
PVOID operator new
(
    size_t      iSize,
    POOL_FLAGS  poolFlags
)
{
    PVOID result = ExAllocatePool2(poolFlags, iSize, SYSVAD_POOLTAG);

    return result;
}


/*****************************************************************************
* ::delete()
*****************************************************************************
* Delete with tag function.
*/
void __cdecl operator delete
(
    PVOID pVoid,
    ULONG tag
)
{
    if (pVoid)
    {
        ExFreePoolWithTag(pVoid, tag);
    }
}


/*****************************************************************************
* ::delete()
*****************************************************************************
* Sized Delete function.
*/
void __cdecl operator delete
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid,
    _In_ size_t cbSize
)
{
    UNREFERENCED_PARAMETER(cbSize);

    if (pVoid)
    {
        ExFreePoolWithTag(pVoid, SYSVAD_POOLTAG);
    }
}


/*****************************************************************************
* ::delete()
*****************************************************************************
* Sized Array Delete function.
*/
void __cdecl operator delete[]
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid,
    _In_ size_t cbSize
)
{
    UNREFERENCED_PARAMETER(cbSize);

    if (pVoid)
    {
        ExFreePoolWithTag(pVoid, SYSVAD_POOLTAG);
    }
}


/*****************************************************************************
* ::delete()
*****************************************************************************
* Array Delete function.
*/
void __cdecl operator delete[]
(
    _Pre_maybenull_ __drv_freesMem(Mem) PVOID pVoid
)
{
    if (pVoid)
    {
        ExFreePoolWithTag(pVoid, SYSVAD_POOLTAG);
    }
}
#endif//_NEW_DELETE_OPERATORS_


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXNotificationFrameworkTests.cpp<|end_filename|>
#include "CAPXNotificationFrameworkTests.h"


bool CAPONotificationFrameworkTest::setUpMethod()
{
    SLGetWindowsInformationDWORD(POLICY_KERNEL_ONECORE_DEVICE_FAMILY_ID, &m_dwPlatform);

    EndpointsWithCapx(m_capxEndpoints);

    return true;
}

bool CAPONotificationFrameworkTest::tearDownMethod()
{
    return true;
}

using malloca_deleter = wil::function_deleter<decltype(&::_freea), ::_freea>;
template <typename T>
using unique_malloca_ptr = wistd::unique_ptr<T, malloca_deleter>;

class CEventParser : public IEtwEventHandler
{
public:
    int GetCountOfAPONotifications() { return m_countOfAudioProcessingNotifications; }
    bool WereOldNotificationsDetected() { return m_bOldNotificationsDetected; }

private:

    int m_countOfAudioProcessingNotifications = 0;
    DWORD m_dLastPid = 0;
    DWORD m_dLastThreadId = 0;
    bool m_bOldNotificationsDetected = false;

    std::wstring QueryProcessName(const DWORD pid)
    {
        wil::unique_handle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
        if(NULL == hProcess)
        {
            return L"err " + std::to_wstring(GetLastError());
        }
        else
        {
            WCHAR szProcessName[1024];
            DWORD cchSize = ARRAYSIZE(szProcessName);
            if (0 == QueryFullProcessImageNameW(hProcess.get(), 0, szProcessName, &cchSize))
            {
                return L"err " + std::to_wstring(GetLastError());
            }

            PWSTR fileName = szProcessName + cchSize;
            while (fileName != szProcessName && *fileName != L'\\') fileName--;
            
            return (*fileName == L'\\') ? fileName + 1 : fileName;
        }
    }

    void OnEvent(PEVENT_RECORD pEventRecord)
    {
        WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
        [&]() {
            ULONG bufferSize = 0;
            if (TdhGetEventInformation(pEventRecord, 0, nullptr, nullptr, &bufferSize) == ERROR_INSUFFICIENT_BUFFER)
            {
                unique_malloca_ptr<TRACE_EVENT_INFO> eventInfo(reinterpret_cast<PTRACE_EVENT_INFO>(_malloca(bufferSize)));
                VERIFY_IS_NOT_NULL(eventInfo);
                RETURN_IF_WIN32_ERROR(TdhGetEventInformation(pEventRecord, 0, nullptr, eventInfo.get(), &bufferSize));

                // Check for old notification types
                if (_wcsicmp(L"IAudioProcessingObjectNotifications implemented", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    m_countOfAudioProcessingNotifications++;
                    m_dLastPid = pEventRecord->EventHeader.ProcessId;
                    m_dLastThreadId = pEventRecord->EventHeader.ThreadId;
                }
                if (_wcsicmp(L"OnPropertyValueChanged", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                        m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                    {
                        m_bOldNotificationsDetected = true;
                    }
                }
                if (_wcsicmp(L"OnEndpointVolumeChanged", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                        m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                    {
                        m_bOldNotificationsDetected = true;
                    }
                }
                if (_wcsicmp(L"RegisterEndpointNotificationCallback", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0 ||
                    _wcsicmp(L"RegisterEndpointNotificationCallbackWithVirtualEnumerator", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                        m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                    {
                        m_bOldNotificationsDetected = true;
                    }
                }
                if (_wcsicmp(L"UnregisterEndpointNotificationCallback", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0 ||
                    _wcsicmp(L"UnregisterEndpointNotificationCallbackWithVirtualEnumerator", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                        m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                    {
                        m_bOldNotificationsDetected = true;
                    }
                }
            }
            return S_OK;
        }();
    }
};

// {6e7b1892-5288-5fe5-8f34-e3b0dc671fd2}
static const GUID AudioSesTelemetryProvider =
{ 0x6e7b1892, 0x5288, 0x5fe5, 0x8f, 0x34, 0xe3, 0xb0, 0xdc, 0x67, 0x1f, 0xd2 };

void CAPONotificationFrameworkTest::NotificationFrameworkTest()
{
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);

     if (m_dwPlatform != DEVICEFAMILYINFOENUM_WINDOWS_CORE && m_capxEndpoints.size() == 0)
    {
        Log::Result(TestResults::Skipped, L"There are no CAPX APOs to test");
        return;
    }

    CEtwListener listener;
    CEventParser parser;

    VERIFY_SUCCEEDED(listener.StartTraceSession(L"NotificationFrameworkTest", static_cast<IEtwEventHandler *>(&parser)));

    // Enable Microsoft.Windows.Audio.Client
    VERIFY_SUCCEEDED(listener.EnableProvider(AudioSesTelemetryProvider, TRACE_LEVEL_INFORMATION, 8));

    auto stopListener = scope_exit([&] {
        listener.StopTraceSession();
    });

    {
        for (UINT i = 0; i < m_capxEndpoints.size(); i++)
        {
            wil::com_ptr_nothrow<IMMDevice> pEndpoint = m_capxEndpoints.at(i);

            wil::unique_prop_variant var;
            wil::com_ptr_nothrow<IPropertyStore> spPropertyStore;
            VERIFY_SUCCEEDED(pEndpoint->OpenPropertyStore(STGM_READ, &spPropertyStore));
            VERIFY_SUCCEEDED(spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var));
            VERIFY_ARE_EQUAL(VT_LPWSTR, var.vt);
            LOG_OUTPUT(L"Testing endpoint: %s", var.pwszVal);

            wil::com_ptr_nothrow<IAudioEndpointVolume> aev;
            VERIFY_SUCCEEDED(pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void **)&aev));

            VERIFY_SUCCEEDED(aev->SetMasterVolumeLevelScalar((0.7f), NULL));

            Sleep(500);

            VERIFY_SUCCEEDED(aev->SetMasterVolumeLevelScalar((1.0f), NULL));

            LOG_OUTPUT(L"Number of APO notifications detected: %d", parser.GetCountOfAPONotifications());  
        }

        VERIFY_IS_FALSE(parser.WereOldNotificationsDetected());
    }
}

<|start_filename|>audio/sysvad/EndpointsCommon/AudioModuleHelper.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    AudioModuleHelper.cpp

Abstract:

    Simple helper for Audio Modules logic.

--*/

#pragma warning (disable : 4127)

#include <sysvad.h>
#include <limits.h>
#include <ntintsafe.h>
#include "SysVadShared.h"
#include "AudioModuleHelper.h"

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
AudioModule_GenericHandler_BasicSupport(
    _In_ PAUDIOMODULE_PARAMETER_INFO            ParameterInfo,
    _Out_writes_bytes_opt_(*BufferCb) PVOID     Buffer,
    _Inout_ ULONG *                             BufferCb
    )
{
    NTSTATUS    ntStatus        = STATUS_SUCCESS;
    ULONG       cbFullProperty  = 0;
    ULONG       cbDataListSize  = 0;

    DPF_ENTER(("[AudioModule_GenericHandler_BasicSupport]"));

    PAGED_CODE();

    ASSERT(ParameterInfo);
    ASSERT(BufferCb);

    //
    // Compute total size of property.
    //
    ntStatus = RtlULongMult(ParameterInfo->Size, 
                            ParameterInfo->ValidSetCount, 
                            &cbDataListSize);
    if (!NT_SUCCESS(ntStatus))
    {
        ASSERT(FALSE);
        *BufferCb = 0;
        return ntStatus;
    }

    ntStatus = RtlULongAdd(cbDataListSize, 
                           (ULONG)(sizeof(KSPROPERTY_DESCRIPTION) +
                                sizeof(KSPROPERTY_MEMBERSHEADER)),
                           &cbFullProperty);

    if (!NT_SUCCESS(ntStatus))
    {
        ASSERT(FALSE);
        *BufferCb = 0;
        return ntStatus;
    }

    //
    // Return the info the caller is asking for.
    //
    if (*BufferCb == 0)
    {
        // caller wants to know the size of the buffer.
        *BufferCb = cbFullProperty;
        ntStatus = STATUS_BUFFER_OVERFLOW;
    }
    else if (*BufferCb >= (sizeof(KSPROPERTY_DESCRIPTION)))
    {
        PKSPROPERTY_DESCRIPTION propDesc = PKSPROPERTY_DESCRIPTION(Buffer);

        propDesc->AccessFlags       = ParameterInfo->AccessFlags;
        propDesc->DescriptionSize   = cbFullProperty;
        propDesc->PropTypeSet.Set   = KSPROPTYPESETID_General;
        propDesc->PropTypeSet.Id    = ParameterInfo->VtType;
        propDesc->PropTypeSet.Flags = 0;
        propDesc->MembersListCount  = 1;
        propDesc->Reserved          = 0;

        // if return buffer can also hold a list description, return it too
        if(*BufferCb  >= cbFullProperty)
        {
            // fill in the members header
            PKSPROPERTY_MEMBERSHEADER members = 
                PKSPROPERTY_MEMBERSHEADER(propDesc + 1);

            members->MembersFlags   = KSPROPERTY_MEMBER_VALUES;
            members->MembersSize    = ParameterInfo->Size;
            members->MembersCount   = ParameterInfo->ValidSetCount;
            members->Flags          = KSPROPERTY_MEMBER_FLAG_DEFAULT;

            // fill in valid array.
            BYTE* array = (BYTE*)(members + 1);

            RtlCopyMemory(array, ParameterInfo->ValidSet, cbDataListSize);

            // set the return value size
            *BufferCb  = cbFullProperty;
        } 
        else
        {
            *BufferCb  = sizeof(KSPROPERTY_DESCRIPTION);
        }
    } 
    else if(*BufferCb  >= sizeof(ULONG))
    {
        // if return buffer can hold a ULONG, return the access flags
        PULONG accessFlags = PULONG(Buffer);

        *BufferCb  = sizeof(ULONG);
        *accessFlags = ParameterInfo->AccessFlags;
    }
    else
    {
        *BufferCb  = 0;
        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
} // PropertyHandlerBasicSupportVolume

//=============================================================================
#pragma code_seg("PAGE")
BOOLEAN
IsAudioModuleParameterValid(
    _In_ PAUDIOMODULE_PARAMETER_INFO        ParameterInfo,
    _In_reads_bytes_opt_(BufferCb) PVOID    Buffer,
    _In_  ULONG                             BufferCb
    )
{
    PAGED_CODE();

    DPF_ENTER(("[IsAudioModuleParameterValid]"));
    
    ULONG       i = 0;
    ULONG       j = 0;
    BOOLEAN     validParam = FALSE;

    //
    // Validate buffer ptr and size.
    //
    if (Buffer == NULL || BufferCb == 0)
    {
        validParam = FALSE;
        goto exit;
    }
    
    //
    // Check its size.
    //
    if (BufferCb < ParameterInfo->Size)
    {
        validParam = FALSE;
        goto exit;
    }

    //
    // Check the valid list.
    //
    if (ParameterInfo->ValidSet && ParameterInfo->ValidSetCount)
    {
        BYTE* buffer = (BYTE*)ParameterInfo->ValidSet;
        BYTE* pattern = (BYTE*)Buffer;

        //
        // Scan the valid list.
        //
        for (i = 0; i < ParameterInfo->ValidSetCount; ++i)
        {
            for (j=0; j < ParameterInfo->Size; ++j)
            {
                if (buffer[j] != pattern[j])
                {
                    break;
                }
            }

            if (j == ParameterInfo->Size)
            {
                // got a match.
                break;
            }

            buffer += ParameterInfo->Size;
        }
        
        //
        // If end of list, we didn't find the value.
        //
        if (i == ParameterInfo->ValidSetCount)
        {
            validParam = FALSE;
            goto exit;
        }
    }
    else
    {
        // 
        // Negative-testing support. Fail request if value is -1.
        //
        BYTE* buffer = (BYTE*)Buffer;

        for (i = 0; i < ParameterInfo->Size; ++i)
        {
            if (buffer[i] != 0xFF)
            {
                break;
            }
        }

        //
        // If value is -1, return error.
        //
        if (i == ParameterInfo->Size)
        {
            validParam = FALSE;
            goto exit;
        }
    }

    validParam = TRUE;
    
exit:
    return validParam;
}

//=============================================================================
#pragma code_seg("PAGE")
AUDIOMODULE *
AudioModule_FindModuleInList(
    _In_ AUDIOMODULE *          AudioModules,
    _In_ ULONG                  AudioModuleCount,
    _In_ GUID *                 ClassId,
    _In_ ULONG                  InstanceId
    )
{
    PAGED_CODE();

    for (ULONG i=0; i<AudioModuleCount; ++i)
    {
        AUDIOMODULE * module = &AudioModules[i];
        
        if (IsEqualGUIDAligned(*(module->Descriptor->ClassId), *ClassId) &&
            module->InstanceId == InstanceId)
        {
            return module;
        }
    }

    return NULL;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
AudioModule_GenericHandler(
    _In_  ULONG                                         Verb,
    _In_  ULONG                                         ParameterId,
    _In_  PAUDIOMODULE_PARAMETER_INFO                   ParameterInfo,
    _Inout_updates_bytes_(ParameterInfo->Size) PVOID    CurrentValue,
    _In_reads_bytes_opt_(InBufferCb) PVOID              InBuffer,
    _In_  ULONG                                         InBufferCb,
    _Out_writes_bytes_opt_(*OutBufferCb) PVOID          OutBuffer,
    _Inout_ ULONG *                                     OutBufferCb, 
    _In_ BOOL *                                         ParameterChanged
    )
{
    PAGED_CODE();

    DPF_ENTER(("[AudioModule_GenericHandler]"));

    UNREFERENCED_PARAMETER(ParameterId);
    
    *ParameterChanged = FALSE;

    // Handle KSPROPERTY_TYPE_BASICSUPPORT query
    if (Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        return AudioModule_GenericHandler_BasicSupport(ParameterInfo, OutBuffer, OutBufferCb);
    }

    ULONG cbMinSize = ParameterInfo->Size;
    
    if (Verb & KSPROPERTY_TYPE_GET)
    {
        // Verify module parameter supports 'get'.
        if (!(ParameterInfo->AccessFlags & KSPROPERTY_TYPE_GET))
        {
            *OutBufferCb = 0;
            return STATUS_INVALID_DEVICE_REQUEST;
        }
        
        // Verify value size
        if (*OutBufferCb == 0)
        {
            *OutBufferCb = cbMinSize;
            return STATUS_BUFFER_OVERFLOW;
        }
        if (*OutBufferCb < cbMinSize)
        {
            *OutBufferCb = 0;
            return STATUS_BUFFER_TOO_SMALL;
        }
        else
        {
            RtlCopyMemory(OutBuffer, CurrentValue, ParameterInfo->Size);
            *OutBufferCb = cbMinSize;
            return STATUS_SUCCESS;
        }
    }
    else if (Verb & KSPROPERTY_TYPE_SET)
    {
        *OutBufferCb = 0;
    
        // Verify it is a write prop.
        if (!(ParameterInfo->AccessFlags & KSPROPERTY_TYPE_SET))
        {
            return STATUS_INVALID_DEVICE_REQUEST;
        }

        // Validate parameter.
        if (!IsAudioModuleParameterValid(ParameterInfo, InBuffer, InBufferCb))
        {
            return STATUS_INVALID_PARAMETER;
        }

        if (ParameterInfo->Size != 
                RtlCompareMemory(CurrentValue, InBuffer, ParameterInfo->Size))
        {
            RtlCopyMemory(CurrentValue, InBuffer, ParameterInfo->Size);
            *ParameterChanged = TRUE;
        }
        
        return STATUS_SUCCESS;
    }
    
    return STATUS_INVALID_DEVICE_REQUEST;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
AudioModule_GenericHandler_ModulesListRequest(
    _In_ PPCPROPERTY_REQUEST    PropertyRequest,
    _In_ AUDIOMODULE *          AudioModules,
    _In_ ULONG                  AudioModuleCount
    )
{
    NTSTATUS ntStatus;
    
    // This specific APO->driver communication example is mainly added to show how this communication is done.
    // The module list only lives on the wave filter and it can have modules that are for all pins and some that 
    // are only on specific pins.

    PAGED_CODE();

    DPF_ENTER(("[AudioModule_PropertyHandlerModulesListRequest]"));

    //
    // Note: return an empty list when no modules are present.
    //
    
    // Handle KSPROPERTY_TYPE_BASICSUPPORT query
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ULONG flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT;
        return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL);
    }

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        PKSMULTIPLE_ITEM ksMultipleItem;
        ULONG cbMinSize = 0;
        PKSAUDIOMODULE_DESCRIPTOR modules = NULL;
        
        //
        // Compute min value size requirements
        //
        ntStatus = RtlULongMult(AudioModuleCount, 
                                (ULONG)sizeof(KSAUDIOMODULE_DESCRIPTOR), 
                                &cbMinSize);
        if (!NT_SUCCESS(ntStatus))
        {
            ASSERT(FALSE);
            return ntStatus;
        }
        
        ntStatus = RtlULongAdd(cbMinSize, sizeof(KSMULTIPLE_ITEM), &cbMinSize);
        if (!NT_SUCCESS(ntStatus))
        {
            ASSERT(FALSE);
            return ntStatus;
        }

        // Verify value size
        if (PropertyRequest->ValueSize == 0)
        {
            PropertyRequest->ValueSize = cbMinSize;
            return STATUS_BUFFER_OVERFLOW;
        }
        if (PropertyRequest->ValueSize < cbMinSize)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }
        
        // Value is a KSMULTIPLE_ITEM followed by list of modules.
        ksMultipleItem = (PKSMULTIPLE_ITEM)PropertyRequest->Value;
        modules = (PKSAUDIOMODULE_DESCRIPTOR)(ksMultipleItem + 1);

        // Copy modules  
        for (ULONG i = 0; i<AudioModuleCount; ++i, ++modules)
        {
            const AUDIOMODULE_DESCRIPTOR * moduleDesc = AudioModules[i].Descriptor;
            
            modules->ClassId    = *moduleDesc->ClassId;
            modules->InstanceId = AudioModules[i].InstanceId;
            
            modules->VersionMajor = moduleDesc->VersionMajor;
            modules->VersionMinor = moduleDesc->VersionMinor;

            
            RtlStringCchCopyW(modules->Name,     
                              AUDIOMODULE_MAX_NAME_CCH_SIZE, // includes null char
                              moduleDesc->Name);
        }
        
        // Miniport filled in the list of modules. Fill in the KSMULTIPLE_ITEM header.
        ksMultipleItem->Size = cbMinSize;
        ksMultipleItem->Count = AudioModuleCount;

        PropertyRequest->ValueSize = ksMultipleItem->Size;
        return STATUS_SUCCESS;
    }

    return STATUS_INVALID_DEVICE_REQUEST;
} // AudioModule_GenericHandler_ModulesListRequest

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
AudioModule_GenericHandler_ModuleCommand(
    _In_ PPCPROPERTY_REQUEST    PropertyRequest,
    _In_ AUDIOMODULE *          AudioModules,
    _In_ ULONG                  AudioModuleCount
    )
{
    AUDIOMODULE *   module = NULL;
    
    PAGED_CODE();

    DPF_ENTER(("[AudioModule_GenericHandler_ModuleCommand]"));
    
    //
    // Basic parameter validation.
    //
    if (AudioModules == NULL || AudioModuleCount == 0)
    {
        // endpoint/stream doesn't have any modules.
        return STATUS_INVALID_DEVICE_REQUEST;
    }
    
    // Verify instance data stores at least KSAUDIOMODULE_PROPERTY fields 
    // beyond KSPPROPERTY.
    if (PropertyRequest->InstanceSize < (sizeof(KSAUDIOMODULE_PROPERTY) - 
            RTL_SIZEOF_THROUGH_FIELD(KSAUDIOMODULE_PROPERTY, Property)))
    {
        return STATUS_INVALID_PARAMETER;
    }

    // Extract property descriptor from property request instance data
    PKSAUDIOMODULE_PROPERTY moduleProperty = CONTAINING_RECORD(
        PropertyRequest->Instance, KSAUDIOMODULE_PROPERTY, ClassId);

    // Get a ptr to the module.
    module = AudioModule_FindModuleInList(AudioModules, 
                                          AudioModuleCount, 
                                          &moduleProperty->ClassId,
                                          moduleProperty->InstanceId);
    //
    // Invoke the handler.
    //
    if (module == NULL || 
        module->Descriptor == NULL || 
        module->Descriptor->Handler == NULL)
    {
        return STATUS_INVALID_PARAMETER;
    }
    
    // Handle KSPROPERTY_TYPE_BASICSUPPORT query
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ULONG flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT;

        return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL);
    }

    // Only KS-get is supported.
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        NTSTATUS    status;
        ULONG       inBufferCb = 0;
        ULONG       outBufferCb = 0;
        PVOID       inBuffer = NULL;
        PVOID       outBuffer = NULL;

        outBufferCb = PropertyRequest->ValueSize;
        outBuffer = PropertyRequest->Value;
        
        ASSERT((outBufferCb != 0 && outBuffer != NULL) ||
               (outBufferCb == 0 && outBuffer == NULL));
        
        // this is guaranteed to work b/c of the validation above.
        inBufferCb = PropertyRequest->InstanceSize + 
                     sizeof(KSPROPERTY) -
                     sizeof(KSAUDIOMODULE_PROPERTY);

        if (inBufferCb != 0)
        {
            inBuffer = (PVOID)(moduleProperty+1);
        }

        status =  module->Descriptor->Handler(module->Context,
                                              inBuffer,
                                              inBufferCb,
                                              outBuffer,
                                              &outBufferCb);
        //
        // Set the size of the returned output data, or in the case of
        // buffer overflow error, return the expected buffer length.
        //
        PropertyRequest->ValueSize = outBufferCb;
        return status;
    }

    PropertyRequest->ValueSize = 0;
    return STATUS_INVALID_DEVICE_REQUEST;
} // AudioModule_GenericHandler_ModuleCommand

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
AudioModule_GenericHandler_ModuleNotificationDeviceId(
    _In_ PPCPROPERTY_REQUEST    PropertyRequest,
    _In_ const GUID *           NotificationDeviceId
    )
{
    PAGED_CODE();

    DPF_ENTER(("[AudioModule_GenericHandler_ModuleNotificationDeviceId]"));

    //
    // Basic parameter validation.
    //
    if (NotificationDeviceId == NULL)
    {
        return STATUS_INVALID_DEVICE_REQUEST;
    }

    // Handle KSPROPERTY_TYPE_BASICSUPPORT query
    if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ULONG flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT;

        return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL);
    }

    ULONG cbMinSize = sizeof(GUID);
    GUID *dataPtr = static_cast<GUID*>(PropertyRequest->Value);

    if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
    {
        // Verify value size
        if (PropertyRequest->ValueSize == 0)
        {
            PropertyRequest->ValueSize = cbMinSize;
            return STATUS_BUFFER_OVERFLOW;
        }
        if (PropertyRequest->ValueSize < cbMinSize)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }
        else
        {
            *dataPtr = *NotificationDeviceId;
            PropertyRequest->ValueSize = cbMinSize;

            return STATUS_SUCCESS;
        }
    }

    return STATUS_INVALID_DEVICE_REQUEST;
}

//=============================================================================
#pragma code_seg("PAGE")
VOID
AudioModule_SendNotification(
    _In_ PPORTCLSNOTIFICATIONS          PortNotifications,
    _In_ PVOID                          NotificationBuffer, 
    _In_ USHORT                         NotificationBufferCb
    )
{
    NTSTATUS                status = STATUS_SUCCESS;
    PPCNOTIFICATION_BUFFER  buffer = NULL;
    
    PAGED_CODE();

    DPF_ENTER(("[AudioModule_SendNotification]"));

    // Allocate a notification buffer.
    status = PortNotifications->AllocNotificationBuffer(PagedPool,
                                                        NotificationBufferCb,
                                                        &buffer);
    if (!NT_SUCCESS(status))
    {
        goto exit;
    }

    // Notification buffer is only guaranteed to be LONG aligned,
    // it is received as ULONGLONG aligned on the receiving end.
    RtlCopyMemory(buffer, NotificationBuffer, NotificationBufferCb);    

    //
    // Generate notification (async).
    //
    PortNotifications->SendNotification(&KSNOTIFICATIONID_AudioModule, buffer);

exit:
    if (buffer != NULL)
    {
        // Free notification buffer.
        PortNotifications->FreeNotificationBuffer(buffer);
        buffer = NULL;
    }
}




<|start_filename|>audio/sysvad/APO/AecApo/AecApo.h<|end_filename|>
//
// AecApo.h -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description:
//
//   Declaration of the CAecApoMFX class.
//

#pragma once

#include <audioenginebaseapo.h>
#include <BaseAudioProcessingObject.h>
#include <AecApoDll.h>

#include <commonmacros.h>
#include <devicetopology.h>

#include <audioengineextensionapo.h>

#include <wil\com.h>

_Analysis_mode_(_Analysis_code_type_user_driver_)

#pragma AVRT_VTABLES_BEGIN
// Aec APO class - MFX
class CAecApoMFX :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CAecApoMFX, &CLSID_AecApoMFX>,
    public CBaseAudioProcessingObject,
    public IAudioSystemEffects3,
    public IAudioProcessingObjectNotifications,
    public IApoAcousticEchoCancellation,
    public IApoAuxiliaryInputConfiguration,
    public IApoAuxiliaryInputRT
{
public:
    // constructor
    CAecApoMFX()
    :   CBaseAudioProcessingObject(sm_RegProperties)
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_AECAPOMFX)

BEGIN_COM_MAP(CAecApoMFX)
    COM_INTERFACE_ENTRY(IAudioSystemEffects)
    COM_INTERFACE_ENTRY(IAudioSystemEffects2)
    COM_INTERFACE_ENTRY(IAudioSystemEffects3)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectRT)
    COM_INTERFACE_ENTRY(IAudioProcessingObject)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration)
    COM_INTERFACE_ENTRY(IApoAcousticEchoCancellation)
    COM_INTERFACE_ENTRY(IApoAuxiliaryInputConfiguration)
    COM_INTERFACE_ENTRY(IApoAuxiliaryInputRT)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

public:
    STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
        APO_CONNECTION_PROPERTY** ppOutputConnections);

    STDMETHOD(GetLatency)(HNSTIME* pTime);

    STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
        UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioSystemEffects2
    STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects)  LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event);

    // IAudioProcessingObject
    STDMETHOD(IsInputFormatSupported)(IAudioMediaType *pOutputFormat, IAudioMediaType *pRequestedInputFormat, IAudioMediaType **ppSupportedInputFormat);
    STDMETHOD(IsOutputFormatSupported)(IAudioMediaType *pInputFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat);

    // IAPOAuxiliaryInputConfiguration
    STDMETHOD(AddAuxiliaryInput)(
        DWORD dwInputId,
        UINT32 cbDataSize,
        BYTE *pbyData,
        APO_CONNECTION_DESCRIPTOR *pInputConnection
        ) override;
    STDMETHOD(RemoveAuxiliaryInput)(
        DWORD dwInputId
        ) override;
    STDMETHOD(IsInputFormatSupported)(
        IAudioMediaType* pRequestedInputFormat,
        IAudioMediaType** ppSupportedInputFormat
        ) override;

    // IAPOAuxiliaryInputRT
    STDMETHOD_(void, AcceptInput)(
        DWORD dwInputId,
        const APO_CONNECTION_PROPERTY *pInputConnection
        ) override;

    // IAudioSystemEffects3
    STDMETHODIMP GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) override
    {
        UNREFERENCED_PARAMETER(effects);
        UNREFERENCED_PARAMETER(numEffects);
        UNREFERENCED_PARAMETER(event);
        return S_OK; 
    }

    STDMETHODIMP SetAudioSystemEffectState(GUID, AUDIO_SYSTEMEFFECT_STATE) override {return S_OK;}

    // IAudioProcessingObjectNotifications
    STDMETHOD(GetApoNotificationRegistrationInfo)(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count) override;
    STDMETHOD_(void, HandleNotification)(_In_ APO_NOTIFICATION* apoNotification) override;

public:
    UINT64                                  m_auxiliaryInputId = 0;
    static const CRegAPOProperties<1>       sm_RegProperties;   // registration properties
    BOOL                                    m_initializedForEffectsDiscovery = FALSE;
    GUID                                    m_audioSignalProcessingMode = GUID_NULL;

    CComPtr<IMMDevice>                      m_spCaptureDevice;
    CComPtr<IMMDevice>                      m_spLoopbackDevice;

    float                                   m_captureEndpointMasterVolume = 0;
    float                                   m_loopbackEndpointMasterVolume = 0;

private:
    wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService;
};
#pragma AVRT_VTABLES_END

OBJECT_ENTRY_AUTO(__uuidof(AecApoMFX), CAecApoMFX)




<|start_filename|>audio/sysvad/APO/SwapAPO/SwapAPO.h<|end_filename|>
//
// SwapAPO.h -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description:
//
//   Declaration of the CSwapAPO class.
//

#pragma once

#include <audioenginebaseapo.h>
#include <audioengineextensionapo.h>
#include <BaseAudioProcessingObject.h>
#include <SwapAPOInterface.h>
#include <SwapAPODll.h>

#include <commonmacros.h>
#include <devicetopology.h>
#include <rtworkq.h>

#include <wil\com.h>

_Analysis_mode_(_Analysis_code_type_user_driver_)

#define PK_EQUAL(x, y)  ((x.fmtid == y.fmtid) && (x.pid == y.pid))
#define GUID_FORMAT_STRING "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"
#define GUID_FORMAT_ARGS(guidVal)  (guidVal).Data1, (guidVal).Data2, (guidVal).Data3, (guidVal).Data4[0], (guidVal).Data4[1], (guidVal).Data4[2], (guidVal).Data4[3], (guidVal).Data4[4], (guidVal).Data4[5], (guidVal).Data4[6], (guidVal).Data4[7]
#define NUM_OF_EFFECTS 1

//
// Define a GUID identifying the type of this APO's custom effect.
//
// APOs generally should not define new GUIDs for types of effects and instead
// should use predefined effect types. Only define a new GUID if the effect is
// truly very different from all predefined types of effects.
//
// {B8EC75BA-00ED-434C-A732-064A0F00788E}
DEFINE_GUID(SwapEffectId,       0xb8ec75ba, 0x00ed, 0x434c, 0xa7, 0x32, 0x06, 0x4a, 0x0f, 0x00, 0x78, 0x8e);

// {5DB5B4C8-6C37-450E-93F5-1E275AFDF87F}
DEFINE_GUID(SWAP_APO_MFX_CONTEXT, 0x5db5b4c8, 0x6c37, 0x450e, 0x93, 0xf5, 0x1e, 0x27, 0x5a, 0xfd, 0xf8, 0x7f);

// {99817AE5-E6DC-4074-B513-8A872178DA12}
DEFINE_GUID(SWAP_APO_SFX_CONTEXT, 0x99817ae5, 0xe6dc, 0x4074, 0xb5, 0x13, 0x8a, 0x87, 0x21, 0x78, 0xda, 0x12);

LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode);

class SwapMFXApoAsyncCallback :
    public IRtwqAsyncCallback
{
private:
    DWORD m_queueId;
    volatile ULONG _refCount = 1;

public:
    SwapMFXApoAsyncCallback(DWORD queueId) : m_queueId(queueId)
    {
    }

    static HRESULT Create(_Outptr_ SwapMFXApoAsyncCallback** workItemOut, DWORD queueId);

    // IRtwqAsyncCallback
    STDMETHOD(GetParameters)(_Out_opt_ DWORD* pdwFlags, _Out_opt_ DWORD* pdwQueue)
    {
        *pdwFlags = 0;
        *pdwQueue = m_queueId;
        return S_OK;
    }
    STDMETHOD(Invoke)(_In_ IRtwqAsyncResult* asyncResult);

    // IUnknown (needed for IRtwqAsyncCallback)
    STDMETHOD(QueryInterface)(REFIID riid, __deref_out void** interfaceOut);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
};

#pragma AVRT_VTABLES_BEGIN
// Swap APO class - MFX
class CSwapAPOMFX :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CSwapAPOMFX, &CLSID_SwapAPOMFX>,
    public CBaseAudioProcessingObject,
    public IMMNotificationClient,
    public IAudioProcessingObjectNotifications,
    public IAudioSystemEffects3,
    // IAudioSystemEffectsCustomFormats may be optionally supported
    // by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    public IAudioSystemEffectsCustomFormats, 
    public ISwapAPOMFX
{
public:
    // constructor
    CSwapAPOMFX()
    :   CBaseAudioProcessingObject(sm_RegProperties)
    ,   m_hEffectsChangedEvent(NULL)
    ,   m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT)
    ,   m_fEnableSwapMFX(FALSE)
    {
        m_pf32Coefficients = NULL;
    }

    virtual ~CSwapAPOMFX();    // destructor

DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOMFX)

BEGIN_COM_MAP(CSwapAPOMFX)
    COM_INTERFACE_ENTRY(ISwapAPOMFX)
    COM_INTERFACE_ENTRY(IAudioSystemEffects)
    COM_INTERFACE_ENTRY(IAudioSystemEffects2)
    COM_INTERFACE_ENTRY(IAudioSystemEffects3)
    // IAudioSystemEffectsCustomFormats may be optionally supported
    // by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    COM_INTERFACE_ENTRY(IAudioSystemEffectsCustomFormats)
    COM_INTERFACE_ENTRY(IMMNotificationClient)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectRT)
    COM_INTERFACE_ENTRY(IAudioProcessingObject)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

public:
    STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
        APO_CONNECTION_PROPERTY** ppOutputConnections);

    STDMETHOD(GetLatency)(HNSTIME* pTime);

    STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
        UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioSystemEffects2
    STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects)  LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event);

    // IAudioSystemEffects3
    STDMETHOD(GetControllableSystemEffectsList)(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event);

    STDMETHOD(SetAudioSystemEffectState)(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state);

    virtual HRESULT ValidateAndCacheConnectionInfo(
                                    UINT32 u32NumInputConnections, 
                                    APO_CONNECTION_DESCRIPTOR** ppInputConnections, 
                                    UINT32 u32NumOutputConnections, 
                                    APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    // IMMNotificationClient
    STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) 
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        UNREFERENCED_PARAMETER(dwNewState); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
    { 
        UNREFERENCED_PARAMETER(flow); 
        UNREFERENCED_PARAMETER(role); 
        UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);

    // IAudioProcessingObjectNotifications
    STDMETHODIMP GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count);
    STDMETHODIMP_(void) HandleNotification(_In_ APO_NOTIFICATION* apoNotification);

    // IAudioSystemEffectsCustomFormats
    // This interface may be optionally supported by APOs that attach directly to the connector in the DEFAULT mode streaming graph
    STDMETHODIMP GetFormatCount(UINT* pcFormats);
    STDMETHODIMP GetFormat(UINT nFormat, IAudioMediaType** ppFormat);
    STDMETHODIMP GetFormatRepresentation(UINT nFormat, _Outptr_ LPWSTR* ppwstrFormatRep);

    // IAudioProcessingObject
    STDMETHODIMP IsOutputFormatSupported(IAudioMediaType *pOppositeFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat);

    STDMETHODIMP CheckCustomFormats(IAudioMediaType *pRequestedFormat);

    STDMETHODIMP DoWorkOnRealTimeThread();

    void HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult);
    
public:
    LONG                                    m_fEnableSwapMFX;
    GUID                                    m_AudioProcessingMode;
    wil::com_ptr_nothrow<IMMDevice>         m_device;
    CComPtr<IPropertyStore>                 m_spAPOSystemEffectsProperties;
    CComPtr<IMMDeviceEnumerator>            m_spEnumerator;
    static const CRegAPOProperties<1>       sm_RegProperties;   // registration properties
    AUDIO_SYSTEMEFFECT                      m_effectInfos[NUM_OF_EFFECTS];

    // Locked memory
    FLOAT32                                 *m_pf32Coefficients;

private:
    CCriticalSection                        m_EffectsLock;
    HANDLE                                  m_hEffectsChangedEvent;
    BOOL m_bRegisteredEndpointNotificationCallback = FALSE;
 
    wil::com_ptr_nothrow<IPropertyStore> m_userStore;
    wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService;

    DWORD m_queueId = 0;
    wil::com_ptr_nothrow<SwapMFXApoAsyncCallback> m_asyncCallback;

    HRESULT ProprietaryCommunicationWithDriver(IMMDeviceCollection *pDeviceCollection, UINT nSoftwareIoDeviceInCollection, UINT nSoftwareIoConnectorIndex);

};
#pragma AVRT_VTABLES_END


#pragma AVRT_VTABLES_BEGIN
// Swap APO class - SFX
class CSwapAPOSFX :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CSwapAPOSFX, &CLSID_SwapAPOSFX>,
    public CBaseAudioProcessingObject,
    public IMMNotificationClient,
    public IAudioProcessingObjectNotifications,
    public IAudioSystemEffects3,
    public ISwapAPOSFX
{
public:
    // constructor
    CSwapAPOSFX()
    :   CBaseAudioProcessingObject(sm_RegProperties)
    ,   m_hEffectsChangedEvent(NULL)
    ,   m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT)
    ,   m_fEnableSwapSFX(FALSE)
    ,   m_fEnableDelaySFX(FALSE)
    {
    }

    virtual ~CSwapAPOSFX();    // destructor

DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOSFX)

BEGIN_COM_MAP(CSwapAPOSFX)
    COM_INTERFACE_ENTRY(ISwapAPOSFX)
    COM_INTERFACE_ENTRY(IAudioSystemEffects)
    COM_INTERFACE_ENTRY(IAudioSystemEffects2)
    COM_INTERFACE_ENTRY(IAudioSystemEffects3)
    COM_INTERFACE_ENTRY(IMMNotificationClient)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectNotifications)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectRT)
    COM_INTERFACE_ENTRY(IAudioProcessingObject)
    COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

public:
    STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections,
        APO_CONNECTION_PROPERTY** ppOutputConnections);

    STDMETHOD(GetLatency)(HNSTIME* pTime);

    STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections,
        APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
        UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioSystemEffects2
    STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects)  LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event);

    // IAudioSystemEffects3
    STDMETHOD(GetControllableSystemEffectsList)(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event);

    STDMETHOD(SetAudioSystemEffectState)(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state);
    // IMMNotificationClient
    STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) 
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        UNREFERENCED_PARAMETER(dwNewState); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    { 
        UNREFERENCED_PARAMETER(pwstrDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
    { 
        UNREFERENCED_PARAMETER(flow); 
        UNREFERENCED_PARAMETER(role); 
        UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); 
        return S_OK; 
    }
    STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);
    
    // IAudioProcessingObjectNotifications
    STDMETHODIMP GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count);
    STDMETHODIMP_(void) HandleNotification(_In_ APO_NOTIFICATION* apoNotification);

public:
    LONG                                    m_fEnableSwapSFX;
    LONG                                    m_fEnableDelaySFX;
    GUID                                    m_AudioProcessingMode;
    wil::com_ptr_nothrow<IMMDevice>         m_device;
    CComPtr<IPropertyStore>                 m_spAPOSystemEffectsProperties;
    CComPtr<IMMDeviceEnumerator>            m_spEnumerator;
    static const CRegAPOProperties<1>       sm_RegProperties;   // registration properties
    AUDIO_SYSTEMEFFECT                      m_effectInfos[NUM_OF_EFFECTS];

    CCriticalSection                        m_EffectsLock;
    HANDLE                                  m_hEffectsChangedEvent;

private:
    wil::com_ptr_nothrow<IPropertyStore> m_userStore;
    wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService;
    BOOL m_bRegisteredEndpointNotificationCallback = FALSE;
};
#pragma AVRT_VTABLES_END

OBJECT_ENTRY_AUTO(__uuidof(SwapAPOMFX), CSwapAPOMFX)
OBJECT_ENTRY_AUTO(__uuidof(SwapAPOSFX), CSwapAPOSFX)

//
//   Declaration of the ProcessSwap routine.
//
void ProcessSwap(
    FLOAT32 *pf32OutputFrames,
    const FLOAT32 *pf32InputFrames,
    UINT32   u32ValidFrameCount,
    UINT32   u32SamplesPerFrame);

//
//   Declaration of the ProcessSwapScale routine.
//
void ProcessSwapScale(
    FLOAT32 *pf32OutputFrames,
    const FLOAT32 *pf32InputFrames,
    UINT32   u32ValidFrameCount,
    UINT32   u32SamplesPerFrame,
    FLOAT32 *pf32Coefficients );

//
//   Convenience methods
//

void WriteSilence(
    _Out_writes_(u32FrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32Frames,
    UINT32 u32FrameCount,
    UINT32 u32SamplesPerFrame );

void CopyFrames(
    _Out_writes_(u32FrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32OutFrames,
    _In_reads_(u32FrameCount * u32SamplesPerFrame)
        const FLOAT32 *pf32InFrames,
    UINT32 u32FrameCount,
    UINT32 u32SamplesPerFrame );


<|start_filename|>serial/serenum/serenum.c<|end_filename|>
/*++
Copyright (c) 1997-1998  Microsoft Corporation

Module Name:

    SERENUM.C

Abstract:

    This module contains contains the entry points for a standard bus
    PNP / WDM driver.


Environment:

    kernel mode only

Notes:



--*/

#include "pch.h"

//
// Declare some entry functions as pageable, and make DriverEntry
// discardable
//

DRIVER_INITIALIZE DriverEntry;

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, Serenum_DriverUnload)
#endif

// disable warnings

#if _MSC_VER >= 1200
#pragma warning(push)
#endif

#pragma warning(disable:4127) // conditional expression is constant

NTSTATUS
DriverEntry (
    IN  PDRIVER_OBJECT  DriverObject,
    IN  PUNICODE_STRING UniRegistryPath
    )
/*++
Routine Description:

    Initialize the entry points of the driver.

--*/
{
    ULONG i;

    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    UNREFERENCED_PARAMETER (UniRegistryPath);

    Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("Driver Entry\n"));
    Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("RegPath: %p\n", UniRegistryPath));

    //
    // Set ever slot to initially pass the request through to the lower
    // device object
    //
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
       DriverObject->MajorFunction[i] = Serenum_DispatchPassThrough;
    }

    //
    // Fill in the Dispatch slots intercepted by the filter driver.
    //
    DriverObject->MajorFunction [IRP_MJ_CREATE] =
    DriverObject->MajorFunction [IRP_MJ_CLOSE] = Serenum_CreateClose;
    DriverObject->MajorFunction [IRP_MJ_PNP] = Serenum_PnP;
    DriverObject->MajorFunction [IRP_MJ_POWER] = Serenum_Power;
    DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = Serenum_IoCtl;
    DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL]
        = Serenum_InternIoCtl;
    DriverObject->DriverUnload = Serenum_DriverUnload;
    DriverObject->DriverExtension->AddDevice = Serenum_AddDevice;


#if DBG
   SerenumLogInit();
#endif

    return STATUS_SUCCESS;
}

IO_COMPLETION_ROUTINE
SerenumSyncCompletion;

NTSTATUS
SerenumSyncCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
                      IN PVOID Context)
{
    PKEVENT SerenumSyncEvent = (PKEVENT) Context;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);

    _Analysis_assume_(SerenumSyncEvent != NULL);

    KeSetEvent(SerenumSyncEvent, IO_NO_INCREMENT, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS
Serenum_CreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
/*++
Routine Description:
    Some outside source is trying to create a file against us.

    If this is for the FDO (the bus itself) then the caller is trying to
    open the propriatary conection to tell us which serial port to enumerate.

    If this is for the PDO (an object on the bus) then this is a client that
    wishes to use the serial port.
--*/
{
    PIO_STACK_LOCATION irpStack;
    NTSTATUS status = STATUS_SUCCESS;
    KEVENT completionEvent;
    PDEVICE_OBJECT pNextDevice;
    PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;


    UNREFERENCED_PARAMETER(DeviceObject);
    status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
    if(!NT_SUCCESS(status)){
        Irp->IoStatus.Information=0;
        Irp->IoStatus.Status=status;
        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        return status;
    }


   irpStack = IoGetCurrentIrpStackLocation(Irp);

   if (commonData->IsFDO) {
      pNextDevice = ((PFDO_DEVICE_DATA)DeviceObject->DeviceExtension)
                    ->TopOfStack;
   } else {
      pNextDevice = ((PFDO_DEVICE_DATA)((PPDO_DEVICE_DATA)DeviceObject->
                                        DeviceExtension)->ParentFdo->
                     DeviceExtension)->TopOfStack;
   }

   switch (irpStack->MajorFunction) {
   case IRP_MJ_CREATE:
      Serenum_KdPrint_Def(SER_DBG_SS_TRACE, ("Create"));

      //
      // Pass on the create and the close
      //

      status = Serenum_DispatchPassThroughWithoutAcquire(DeviceObject, Irp);
      break;

   case IRP_MJ_CLOSE:
      Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("Close \n"));

      //
      // Send the close down; after it finishes we can open and take
      // over the port
      //

      IoCopyCurrentIrpStackLocationToNext(Irp);

      KeInitializeEvent(&completionEvent, SynchronizationEvent, FALSE);

      IoSetCompletionRoutine(Irp, SerenumSyncCompletion, &completionEvent,
                             TRUE, TRUE, TRUE);

      status = IoCallDriver(pNextDevice, Irp);


      if (status == STATUS_PENDING) {
         KeWaitForSingleObject(&completionEvent, Executive, KernelMode, FALSE,
                               NULL);
      }

      status = Irp->IoStatus.Status;

      IoCompleteRequest(Irp, IO_NO_INCREMENT);
      IoReleaseRemoveLock(&commonData->RemoveLock, Irp); 

      break;
   }

   return status;
}


NTSTATUS
Serenum_IoCtl (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
/*++
Routine Description:

--*/
{
    PIO_STACK_LOCATION      irpStack;
    NTSTATUS                status;
    PCOMMON_DEVICE_DATA     commonData;
    PFDO_DEVICE_DATA        fdoData;
    HANDLE                  keyHandle;
    ULONG                   actualLength;


	
    status = STATUS_SUCCESS;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    ASSERT (IRP_MJ_DEVICE_CONTROL == irpStack->MajorFunction);

    commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
    fdoData = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension;

    status = IoAcquireRemoveLock(&commonData->RemoveLock, Irp);
    if (!NT_SUCCESS(status)){
        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return status;
    }


    //buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    // We only take Device Control requests for the FDO.
    // That is the bus itself.
    //
    // The request is one of the propriatary Ioctls for
    //
    // NB We ARE a filter driver, so we DO pass on the irp if we don't handle it
    //

    //inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    //outlen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

    if (!commonData->IsFDO) {
        //
        // These commands are only allowed to go to the FDO.  Since they came
        // into the PDO, we need to fire them down to the serenum Fdo.
        //
        return Serenum_Generic_FireAndRelease_RemoveLock(((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->ParentFdo,Irp);
    }

    switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
    case IOCTL_SERENUM_GET_PORT_NAME:
        //
        // Get the port name from the registry.
        // This IOCTL is used by the modem cpl.
        //

        status = IoOpenDeviceRegistryKey(fdoData->UnderlyingPDO,
                                         PLUGPLAY_REGKEY_DEVICE,
                                         STANDARD_RIGHTS_READ,
                                         &keyHandle);

        if (!NT_SUCCESS(status)) {
           //
           // This is a fatal error.  If we can't get to our registry key,
           // we are sunk.
           //
           Serenum_KdPrint_Def (SER_DBG_PNP_ERROR,
                ("IoOpenDeviceRegistryKey failed - %x \n", status));
           Irp->IoStatus.Information = 0;
        } else {
            status = Serenum_GetRegistryKeyValue(
                keyHandle,
                L"PortName",
                sizeof(L"PortName"),
                Irp->AssociatedIrp.SystemBuffer,
                irpStack->Parameters.DeviceIoControl.OutputBufferLength,
                &actualLength);
            if ( STATUS_OBJECT_NAME_NOT_FOUND == status ||
                 STATUS_INVALID_PARAMETER  == status ) {
                status = Serenum_GetRegistryKeyValue(
                    keyHandle,
                    L"Identifier",
                    sizeof(L"Identifier"),
                    Irp->AssociatedIrp.SystemBuffer,
                    irpStack->Parameters.DeviceIoControl.OutputBufferLength,
                    &actualLength);
            }
            Irp->IoStatus.Information = actualLength;
            ZwClose (keyHandle);
        }
        break;
    default:
        //
        // This is not intended for us - fire and Protect!
        //
        return Serenum_DispatchPassThroughWithoutAcquire(
            DeviceObject,
            Irp);
    }

    IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
    Irp->IoStatus.Status = status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    return status;
}

NTSTATUS
Serenum_InternIoCtl (
    PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
/*++
Routine Description:

--*/
{
    PIO_STACK_LOCATION      irpStack;
    NTSTATUS                status;
    PCOMMON_DEVICE_DATA     commonData;
    PPDO_DEVICE_DATA        pdoData;

//    PAGED_CODE();


    commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
    status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
    if(!NT_SUCCESS(status)){
        Irp->IoStatus.Information=0;
        Irp->IoStatus.Status=status;
        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        return status;
    }
    status = STATUS_SUCCESS;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    ASSERT (IRP_MJ_INTERNAL_DEVICE_CONTROL == irpStack->MajorFunction);

    
    pdoData = (PPDO_DEVICE_DATA) DeviceObject->DeviceExtension;

    //
    // We only take Internal Device Control requests for the PDO.
    // That is the objects on the bus (representing the serial ports)
    //
    // We do pass on the irp if this comes into the fdo, but not if it comes
    // into the pdo.
    //

    if (commonData->IsFDO) {
        return Serenum_DispatchPassThroughWithoutAcquire(
            DeviceObject,
            Irp);
    }else{
        //buffer = Irp->UserBuffer;

        switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
        case IOCTL_INTERNAL_SERENUM_REMOVE_SELF:
            Serenum_KdPrint(pdoData, SER_DBG_SS_TRACE, ("Remove self\n"));

            ((PFDO_DEVICE_DATA) pdoData->ParentFdo->DeviceExtension)->
                PDOForcedRemove = TRUE;
            Serenum_PDO_EnumMarkMissing(pdoData->ParentFdo->DeviceExtension, DeviceObject->DeviceExtension);

            IoInvalidateDeviceRelations(
                ((PFDO_DEVICE_DATA) pdoData->ParentFdo->DeviceExtension)->
                UnderlyingPDO,
                BusRelations );
            status = STATUS_SUCCESS;
            Irp->IoStatus.Information = 0;
            break;

        default:
            //
            // Pass it through
            //
            return Serenum_DispatchPassThroughWithoutAcquire(DeviceObject, Irp);
        }
    }

    Irp->IoStatus.Status = status;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
    return status;
}


VOID
Serenum_DriverUnload (
    IN PDRIVER_OBJECT Driver
    )
/*++
Routine Description:
    Clean up everything we did in driver entry.

--*/
{
    UNREFERENCED_PARAMETER (Driver);
    PAGED_CODE();

    //
    // All the device objects should be gone.
    //

    ASSERT (NULL == Driver->DeviceObject);

    //
    // Here we free any resources allocated in DriverEntry
    //

#if DBG
    SerenumLogFree();
#endif

    return;
}

NTSTATUS
Serenum_DispatchPassThrough(
                           IN PDEVICE_OBJECT DeviceObject,
                           IN PIRP Irp
                           )
/*++
Routine Description:

    Passes a request on to the lower driver.

--*/
{
    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
    BOOLEAN waitForEnum = FALSE;
    NTSTATUS rval;
    PFDO_DEVICE_DATA pFdoData;
    BOOLEAN isFdo;
   
    PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
    isFdo = commonData->IsFDO;
   
    rval=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
    if(!NT_SUCCESS(rval)){
        Irp->IoStatus.Information=0;
        Irp->IoStatus.Status=rval;
        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        return rval;
    }

    if (isFdo) {
      pFdoData = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;

#if DBG
   switch (IrpStack->MajorFunction) {
   case IRP_MJ_READ:
      LOGENTRY(LOG_PASSTHROUGH, 'SFRD', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_WRITE:
      LOGENTRY(LOG_PASSTHROUGH, 'SFWR', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_DEVICE_CONTROL:
      LOGENTRY(LOG_PASSTHROUGH, 'SFDC', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_CREATE:
      LOGENTRY(LOG_PASSTHROUGH, 'SFCR', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_CLOSE:
      LOGENTRY(LOG_PASSTHROUGH, 'SFCL', DeviceObject, Irp, 0);
      break;

   default:
      break;
   }
#endif // DBG

   } else {
      pFdoData = ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->
                 ParentFdo->DeviceExtension;

#if DBG
   switch (IrpStack->MajorFunction) {
   case IRP_MJ_READ:
      LOGENTRY(LOG_PASSTHROUGH, 'SPRD', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_WRITE:
      LOGENTRY(LOG_PASSTHROUGH, 'SPWR', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_DEVICE_CONTROL:
      LOGENTRY(LOG_PASSTHROUGH, 'SPDC', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_CREATE:
      LOGENTRY(LOG_PASSTHROUGH, 'SPCR', DeviceObject, Irp, 0);
      break;

   case IRP_MJ_CLOSE:
      LOGENTRY(LOG_PASSTHROUGH, 'SPCL', DeviceObject, Irp, 0);
      break;

   default:
      break;
   }
#endif // DBG
   }

   if (IrpStack->MajorFunction == IRP_MJ_CREATE) {
      //
      // If we're doing an enumeration, we must wait here
      //

      waitForEnum = TRUE;

      LOGENTRY(LOG_PASSTHROUGH, 'SPCW', DeviceObject, Irp, 0);

      rval = KeWaitForSingleObject(&pFdoData->CreateSemaphore, Executive,
                                   KernelMode, FALSE, NULL);
      if (!NT_SUCCESS(rval)) {
         LOGENTRY(LOG_PASSTHROUGH, 'SPCF', DeviceObject, Irp, rval);
         Irp->IoStatus.Status = rval;
         IoCompleteRequest(Irp, IO_NO_INCREMENT);
         IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
         return rval;
      }
   }

   //
   // Pass the IRP to the target
   //

   

    if (waitForEnum) {
        IoSkipCurrentIrpStackLocation (Irp);
        rval = IoCallDriver(pFdoData->TopOfStack, Irp);
        KeReleaseSemaphore(&pFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
        IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
    }else{
        rval=Serenum_Generic_FireAndRelease_RemoveLock(pFdoData->TopOfStack,Irp);
    }
    return rval;
}


NTSTATUS Serenum_Generic_FireAndRelease_RemoveLock(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
    NTSTATUS status;
    // we should not block on start irp instead completion routine should do processing
    IoCopyCurrentIrpStackLocationToNext(Irp);
    // use ExIoSetCompletionRoutineEx to make sure non pnp drivers are unloaded while completion routines could still have to run
    IoSetCompletionRoutine(Irp,
                           (PIO_COMPLETION_ROUTINE)Serenum_CompletionRoutine,
                           NULL,
                           TRUE,
                           TRUE,
                           TRUE); 
    status = IoCallDriver(DeviceObject, Irp);
    return status;
}

NTSTATUS
Serenum_DispatchPassThroughWithoutAcquire(
                           IN PDEVICE_OBJECT DeviceObject,
                           IN PIRP Irp
                           )
/*++
Routine Description:

    Passes a request on to the lower driver.

--*/
{
    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
    BOOLEAN waitForEnum = FALSE;
    NTSTATUS rval;
    PFDO_DEVICE_DATA pFdoData;
    BOOLEAN isFdo;
   
    PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
    isFdo = commonData->IsFDO;

    if (isFdo) {
        pFdoData = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;

#if DBG
        switch (IrpStack->MajorFunction) {
        case IRP_MJ_READ:
            LOGENTRY(LOG_PASSTHROUGH, 'SFRD', DeviceObject, Irp, 0);
            break;

        case IRP_MJ_WRITE:
            LOGENTRY(LOG_PASSTHROUGH, 'SFWR', DeviceObject, Irp, 0);
            break;

        case IRP_MJ_DEVICE_CONTROL:
            LOGENTRY(LOG_PASSTHROUGH, 'SFDC', DeviceObject, Irp, 0);
            break;

        case IRP_MJ_CREATE:
            LOGENTRY(LOG_PASSTHROUGH, 'SFCR', DeviceObject, Irp, 0);
            break;

        case IRP_MJ_CLOSE:
            LOGENTRY(LOG_PASSTHROUGH, 'SFCL', DeviceObject, Irp, 0);
            break;

        default:
            break;
    }
#endif // DBG

    } else {
        pFdoData = ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->ParentFdo->DeviceExtension;

#if DBG
        switch (IrpStack->MajorFunction) {
            case IRP_MJ_READ:
                LOGENTRY(LOG_PASSTHROUGH, 'SPRD', DeviceObject, Irp, 0);
                break;

            case IRP_MJ_WRITE:
                LOGENTRY(LOG_PASSTHROUGH, 'SPWR', DeviceObject, Irp, 0);
                break;

            case IRP_MJ_DEVICE_CONTROL:
                LOGENTRY(LOG_PASSTHROUGH, 'SPDC', DeviceObject, Irp, 0);
                break;

            case IRP_MJ_CREATE:
                LOGENTRY(LOG_PASSTHROUGH, 'SPCR', DeviceObject, Irp, 0);
                break;

            case IRP_MJ_CLOSE:
                LOGENTRY(LOG_PASSTHROUGH, 'SPCL', DeviceObject, Irp, 0);
                break;

            default:
                break;
        }
#endif // DBG
    }

    if (IrpStack->MajorFunction == IRP_MJ_CREATE) {
        //
        // If we're doing an enumeration, we must wait here
        //

        waitForEnum = TRUE;

        LOGENTRY(LOG_PASSTHROUGH, 'SPCW', DeviceObject, Irp, 0);

        rval = KeWaitForSingleObject(&pFdoData->CreateSemaphore, Executive,
                                   KernelMode, FALSE, NULL);
        if (!NT_SUCCESS(rval)) {
            LOGENTRY(LOG_PASSTHROUGH, 'SPCF', DeviceObject, Irp, rval);
            Irp->IoStatus.Status = rval;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
            return rval;
        }
    }

    //
    // Pass the IRP to the target
    //

   

    if (waitForEnum){
        IoSkipCurrentIrpStackLocation (Irp);
        rval = IoCallDriver(pFdoData->TopOfStack, Irp);
        KeReleaseSemaphore(&pFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
        IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
    }else{
        rval=Serenum_Generic_FireAndRelease_RemoveLock(pFdoData->TopOfStack,Irp);
    }
    return rval;
}



void
Serenum_InitPDO (
    _In_ __drv_aliasesMem PDEVICE_OBJECT      Pdo,
    PFDO_DEVICE_DATA    FdoData
    )
/*
Description:
    Common code to initialize a newly created serenum pdo.
    Called either when the control panel exposes a device or when Serenum senses
    a new device was attached.

Parameters:
    Pdo - The pdo
    FdoData - The fdo's device extension
*/
{
    ULONG FdoFlags = FdoData->Self->Flags;
    PPDO_DEVICE_DATA pdoData = Pdo->DeviceExtension;
    KIRQL oldIrql;

    //
    // Check the IO style
    //
    if (FdoFlags & DO_BUFFERED_IO) {
        Pdo->Flags |= DO_BUFFERED_IO;
    } else if (FdoFlags & DO_DIRECT_IO) {
        Pdo->Flags |= DO_DIRECT_IO;
    }

    //
    // Increment the pdo's stacksize so that it can pass irps through
    //
    Pdo->StackSize += FdoData->Self->StackSize;

    //
    // Initialize the rest of the device extension
    //
    pdoData->IsFDO = FALSE;
    pdoData->Self = Pdo;

    pdoData->ParentFdo = FdoData->Self;

    pdoData->Started = FALSE; // irp_mn_start has yet to be received
    pdoData->Attached = TRUE; // attached to the bus
    //pdoData->Removed = FALSE; // no irp_mn_remove as of yet
    pdoData->DebugLevel = FdoData->DebugLevel;  // Copy the debug level

    pdoData->DeviceState = PowerDeviceD0;
    pdoData->SystemState = PowerSystemWorking;

    //
    // Add the pdo to serenum's list
    //

    ASSERT(FdoData->NewPDO == NULL);
    ASSERT(FdoData->NewPdoData == NULL);
    ASSERT(FdoData->NewNumPDOs == 0);

    KeAcquireSpinLock(&FdoData->EnumerationLock, &oldIrql);

    FdoData->NewPDO = Pdo;
    FdoData->NewPdoData = pdoData;
    FdoData->NewNumPDOs = 1;

    FdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;

    KeReleaseSpinLock(&FdoData->EnumerationLock, oldIrql);

    Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
    Pdo->Flags |= DO_POWER_PAGABLE;
}

#if _MSC_VER >= 1200
#pragma warning(pop)
#endif

#pragma warning(default:4127) // conditional expression is constant




<|start_filename|>audio/sysvad/EndpointsCommon/a2dphpspeakertopo.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    a2dphpspeakertopo.h

Abstract:

    Declaration of topology miniport for the A2DP Headphone speaker (external).

--*/

#ifndef _SYSVAD_A2DPHPSPEAKERTOPO_H_
#define _SYSVAD_A2DPHPSPEAKERTOPO_H_

// Function declarations.
NTSTATUS
PropertyHandler_A2dpHpSpeakerTopoFilter( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
    );

NTSTATUS
PropertyHandler_A2dpHpSpeakerTopoFilterEvent(
    _In_    PPCEVENT_REQUEST    EventRequest
    );

#endif // _SYSVAD_A2DPHPSPEAKERTOPO_H_



<|start_filename|>audio/sysvad/A2dpHpDevice.h<|end_filename|>
//
// This class represents a the Bluetooth A2DP Headphone device.
// There is one class for each interface (id = symbolic-link-name).
//
#ifdef SYSVAD_A2DP_SIDEBAND


//=====================================================================
//
//  A2DP Sideband Headphone class definition.
//

class CAdapterCommon;
class A2dpHpDevice;

struct A2dpHpWorkItemContext
{
    CAdapterCommon *    Adapter;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    A2dpHpWorkItemContext,
    GetA2dpHpWorkItemContext
)

// start/stop the device
enum eA2dpHpTaskAction
{
    eA2dpHpTaskStart    = 1,
    eA2dpHpTaskStop     = 2,
};

struct A2dpHpWorkTask
{
    LIST_ENTRY          ListEntry;
    A2dpHpDevice      * Device;
    eA2dpHpTaskAction   Action;
};


//=====================================================================
//
// Device: Bluetooth A2DP Headphone definitions.
//

// BTH A2DP device's notification work-item context.
struct A2dpHpDeviceNotificationWorkItemContext
{
    A2dpHpDevice *  A2dpHpDevice;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    A2dpHpDeviceNotificationWorkItemContext,
    GetA2dpHpDeviceNotificationWorkItemContext
)

// A2DP HP device's notification request context.
union A2dpHpDeviceNotificationBuffer
{
    SIDEBANDAUDIO_VOLUME_PARAMS          Volume;
    SIDEBANDAUDIO_MUTE_PARAMS            Mute;
    SIDEBANDAUDIO_STREAM_STATUS_PARAMS   StreamStatus;
    SIDEBANDAUDIO_CONNECTION_PARAMS      ConnectionStatus;
    SIDEBANDAUDIO_SIOP_REQUEST_PARAM     SiopUpdate;
};

struct A2dpHpDeviceNotificationReqContext
{
    A2dpHpDevice                    *A2dpHpDevice;
    LONG                            Errors;
    A2dpHpDeviceNotificationBuffer  Buffer;
    WDFMEMORY                       MemIn;
    WDFMEMORY                       MemOut;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME
(
    A2dpHpDeviceNotificationReqContext,
    GetA2dpHpDeviceNotificationReqContext
)

enum eA2dpHpState
{
    eA2dpHpStateInvalid         = 0,
    eA2dpHpStateInitializing    = 1,
    eA2dpHpStateRunning         = 2,
    eA2dpHpStateStopping        = 3,
    eA2dpHpStateStopped         = 4,
    eA2dpHpStateFailed          = 5,
};

// To support event notification.
struct A2dpHpEventCallback
{
    PFNEVENTNOTIFICATION    Handler;
    PVOID                   Context;
};

typedef struct _AVDTP_CATEGORY_HEADER
{
    UCHAR SvcCategory;
    UCHAR Losc;     //Length Of Service Capability
} AVDTP_CATEGORY_HEADER;

//
// Contains codec information specific for the Subband Codec.
// Structure is defined in section 4.3.2 of the Bluetooth A2DP 1.3.2 specification.
//
typedef struct _A2DP_SBC_MEDIA_HEADER
{
    AVDTP_CATEGORY_HEADER CategoryCodecHeader;

    UCHAR Rfa : 4;
    UCHAR MediaType : 4;
    UCHAR CodecType;

    UCHAR ChannelMode : 4;
    UCHAR SamplingFrequency : 4;

    UCHAR AllocMethod : 2;
    UCHAR Subbands : 2;
    UCHAR BlockLength : 4;

    UCHAR MinBitpoolVal;
    UCHAR MaxBitpoolVal;

    // return the appropriate Losc value for this structure
    static unsigned char CorrectLosc()
    {
        return static_cast<unsigned char>((sizeof(_A2DP_SBC_MEDIA_HEADER) - sizeof(AVDTP_CATEGORY_HEADER)));
    }
} A2DP_SBC_MEDIA_HEADER;

//
// Contains codec information specific for the Advanced Audio Codec (AAC)
// Structure is defined in section 4.5.2 of the Bluetooth A2DP 1.3.2 specification.
//
typedef struct _A2DP_AAC_MEDIA_HEADER
{
    AVDTP_CATEGORY_HEADER CategoryCodecHeader;

    UCHAR Rfa : 4;
    UCHAR MediaType : 4;
    UCHAR CodecType;

    // Octet0
    UCHAR ObjectType;

    // Octet1
    UCHAR SamplingFrequencyUpper8Bits;

    // Octet2
    UCHAR RFA : 2;
    UCHAR Channels : 2;
    UCHAR SamplingFrequencyLower4Bits : 4;

    // Octet3
    UCHAR BitRateUpper7Bits : 7;
    UCHAR VariableBitRate : 1;

    // Octet4
    UCHAR BitRateMiddle8Bits;

    // Octet5
    UCHAR BitRateLower8Bits;

    // return the appropriate Losc value for this structure
    static UCHAR CorrectLosc()
    {
        return static_cast<UCHAR>((sizeof(A2DP_AAC_MEDIA_HEADER) - sizeof(AVDTP_CATEGORY_HEADER)));
    }
} A2DP_AAC_MEDIA_HEADER;

typedef union
{
    A2DP_SBC_MEDIA_HEADER SbcHeader;
    A2DP_AAC_MEDIA_HEADER AacHeader;
}A2DPHPDEVICE_CODEC_CAPABILITIES, *PA2DPHPDEVICE_CODEC_CAPABILITIES;

typedef struct _A2DPHPDEVICE_EP_TRANSPORT_RESOURCES
{
    ULONG                       A2dpHwTransportResource;
}A2DPHPDEVICE_EP_TRANSPORT_RESOURCES, *PA2DPHPDEVICE_EP_TRANSPORT_RESOURCES;

#endif // SYSVAD_A2DP_SIDEBAND
class A2dpHpDevice : 
    ISidebandDeviceCommon,
    public CUnknown    
{
    private:
        eA2dpHpState            m_State;
            
        IAdapterCommon        * m_Adapter;
        WDFIOTARGET             m_WdfIoTarget;
        
        LIST_ENTRY              m_ListEntry;
        UNICODE_STRING          m_SymbolicLinkName;
        WCHAR                   m_SpeakerWaveNameBuffer[A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_SpeakerWaveRefString;
        WCHAR                   m_SpeakerTopologyNameBuffer[A2DPHP_INTERFACE_REFSTRING_MAX_LENGTH];
        UNICODE_STRING          m_SpeakerTopologyRefString;
        
        //
        // The Topo Filter Desc and Topo Pins structures referenced from
        // the Miniports structure are deep copies: they start as copies of the 
        // static structures and are modified to allow A2DP-Device pin 
        // categories based on the info obtained from 
        // IOCTL_SBAUD_GET_ENDPOINT_DESCRIPTOR
        //
        PENDPOINT_MINIPAIR                      m_SpeakerMiniports;
        
        PUNKNOWN                                m_UnknownSpeakerTopology;
        PUNKNOWN                                m_UnknownSpeakerWave;

        PSIDEBANDAUDIO_DEVICE_DESCRIPTOR        m_Descriptor;
        PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     m_pSpeakerDescriptor;
        PKSPROPERTY_DESCRIPTION                 m_SpeakerVolumePropValues;
        LONG                                    m_SpeakerVolumeLevel;
        PA2DPHPDEVICE_CODEC_CAPABILITIES        m_ConnectedCodecCaps;
        FAST_MUTEX                              m_ConnectedCodecLock;
        union {
            NTSTATUS                            m_ConnectionStatus;
            LONG                                m_ConnectionStatusLong;
        }; // unnamed.

        PKSPROPERTY_DESCRIPTION                 m_SpeakerMutePropValues;
        LONG                                    m_SpeakerMute;


        ULONG                                   m_SpeakerEpIndex;
        
        union{
          NTSTATUS                              m_SpeakerStreamStatus;
          LONG                                  m_SpeakerStreamStatusLong;
        }; // unnamed.

        KEVENT                                  m_SpeakerStreamStatusEvent;

        PSIDEBANDAUDIO_SUPPORTED_FORMATS        m_pSpeakerSupportedFormatsIntersection;
        ULONG                                   m_SpeakerSelectedFormat;

        A2DPHPDEVICE_EP_TRANSPORT_RESOURCES     m_SpeakerTransportResources;

        WDFREQUEST              m_SpeakerStreamReq;
        WDFREQUEST              m_SpeakerVolumeReq;
        WDFREQUEST              m_SpeakerMuteReq;
        WDFREQUEST              m_ConnectionReq;
        WDFREQUEST              m_SiopUpdateReq;
        WDFWORKITEM             m_WorkItem;
        WDFCOLLECTION           m_ReqCollection;
        KSPIN_LOCK              m_Lock;

        LONG                    m_nSpeakerStreams; // # of open streams.
        LONG                    m_nSpeakerStartedStreams;

        A2dpHpEventCallback     m_SpeakerVolumeCallback;

        A2dpHpEventCallback     m_SpeakerMuteCallback;

        A2dpHpEventCallback     m_SpeakerConnectionStatusCallback;

        A2dpHpEventCallback     m_SpeakerFormatChangeCallback;

    public:
        //=====================================================================
        // Default CUnknown
        DECLARE_STD_UNKNOWN();
        DEFINE_STD_CONSTRUCTOR(A2dpHpDevice);
        ~A2dpHpDevice();

        NTSTATUS Init
        (
            _In_ IAdapterCommon     * Adapter, 
            _In_ PUNICODE_STRING      SymbolicLinkName
        );

    public:
        //=====================================================================
        //
        // Public functions used by CAdapterCommon object.
        //
        VOID Start();
        VOID Stop();

        PLIST_ENTRY GetListEntry()
        {
            return &m_ListEntry;
        }

        PUNICODE_STRING GetSymbolicLinkName()
        {
            return &m_SymbolicLinkName;
        }

        static
        A2dpHpDevice * 
        GetA2dpHpDevice
        (
            _In_ PLIST_ENTRY le
        )
        {
            return CONTAINING_RECORD(le, A2dpHpDevice, m_ListEntry);
        }

    public:
        //=====================================================================
        //
        // ISidebandDeviceCommon functions. 
        //
        STDMETHODIMP_(BOOL)                 IsVolumeSupported
        (
            _In_        eDeviceType             deviceType
        );
        
        STDMETHODIMP_(PVOID)                GetVolumeSettings
        (
            _In_        eDeviceType             deviceType,
            _Out_       PULONG                  Size 
        );
    
        STDMETHODIMP_(NTSTATUS)             GetVolume
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _Out_       LONG                    *pVolume
        );
     
        STDMETHODIMP_(NTSTATUS)             SetVolume
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _In_        LONG                    Volume
        );

        STDMETHODIMP_(BOOL)                 IsMuteSupported
        (
            _In_        eDeviceType             deviceType
        );
        
        STDMETHODIMP_(PVOID)                GetMuteSettings
        (
            _In_        eDeviceType             deviceType,
            _Out_       PULONG                  Size 
        );
    
        STDMETHODIMP_(LONG)                 GetMute
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel
        );
     
        STDMETHODIMP_(NTSTATUS)             SetMute
        (
            _In_        eDeviceType             deviceType,
            _In_        LONG                    Channel,
            _In_        LONG                    Mute
        );

        STDMETHODIMP_(BOOL)                 GetConnectionStatus();

        STDMETHODIMP_(NTSTATUS)             Connect();

        STDMETHODIMP_(NTSTATUS)             Disconnect();
    
        STDMETHODIMP_(BOOL)                 GetStreamStatus(_In_ eDeviceType deviceType);
        
        STDMETHODIMP_(NTSTATUS)             StreamOpen(_In_ eDeviceType deviceType);

        STDMETHODIMP_(NTSTATUS)             StreamStart(_In_ eDeviceType deviceType);
    
        STDMETHODIMP_(NTSTATUS)             StreamSuspend(_In_ eDeviceType deviceType);

        STDMETHODIMP_(NTSTATUS)             StreamClose(_In_ eDeviceType deviceType);
        
        STDMETHODIMP_(GUID)                 GetContainerId(_In_ eDeviceType deviceType);

        STDMETHODIMP_(VOID)                 SetVolumeHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetMuteHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetConnectionStatusHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(VOID)                 SetFormatChangeHandler
        (
            _In_        eDeviceType             deviceType,
            _In_opt_    PFNEVENTNOTIFICATION    EventHandler,
            _In_opt_    PVOID                   EventHandlerContext
        );

        STDMETHODIMP_(PPIN_DEVICE_FORMATS_AND_MODES)    GetFormatsAndModes
        (
            _In_        eDeviceType             deviceType
        );
        
        _IRQL_requires_max_(DISPATCH_LEVEL)
        STDMETHODIMP_(BOOL)                 IsNRECSupported();
        
        STDMETHODIMP_(BOOL)                 GetNRECDisableStatus();

    private:
        //=====================================================================
        //
        // Helper functions.
        //
        NTSTATUS    SendIoCtrlSynchronously
        (
            _In_opt_    WDFREQUEST  Request,
            _In_        ULONG       IoControlCode,
            _In_        ULONG       InLength,
            _In_        ULONG       OutLength,
            _When_(InLength > 0 || OutLength > 0, _In_)
            _When_(InLength == 0 && OutLength == 0, _In_opt_)
                        PVOID       Buffer
        );

        NTSTATUS    SendIoCtrlSynchronously
        (
            _In_opt_    WDFREQUEST  Request,
            _In_        ULONG       IoControlCode,
            _In_        ULONG       InLength,
            _In_        ULONG       OutLength,
            _When_(InLength > 0, _In_)
            _When_(InLength == 0, _In_opt_)
                        PVOID       InBuffer,
            _When_(OutLength > 0, _In_)
            _When_(OutLength == 0, _In_opt_)
                        PVOID       OutBuffer
        );
        
        NTSTATUS    SendIoCtrlAsynchronously
        (
            _In_        WDFREQUEST      Request,
            _In_        ULONG           IoControlCode,
            _In_opt_    WDFMEMORY       MemIn,
            _In_opt_    WDFMEMORY       MemOut,
            _In_        PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
            _In_        WDFCONTEXT      Context
        );

        NTSTATUS    SetA2dpHpDeviceCapabilities
        (
        );
        
        NTSTATUS    GetA2dpHpDescriptor
        (
            _Out_ PSIDEBANDAUDIO_DEVICE_DESCRIPTOR *Descriptor
        );
        
        NTSTATUS    GetA2dpHpEndpointDescriptor
        (
            _In_    ULONG                                   EpIndex,
            _Out_   PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2     *EpDescriptor
        );

        NTSTATUS    GetA2dpHpVolumePropertyValues
        (
            _In_  ULONG                     EpIndex,
            _In_  ULONG                     Length,
            _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
        );
        
        NTSTATUS    SetA2dpHpSpeakerVolume
        (
            _In_ LONG   Channel,
            _In_ LONG   Volume  
        );
        
        NTSTATUS    GetA2dpHpSpeakerVolume
        (
            _In_  LONG  Channel,
            _Out_ LONG  *Volume    
        );
        
        NTSTATUS    EnableA2dpHpSpeakerVolumeStatusNotification();
        
        NTSTATUS    GetA2dpHpMutePropertyValues
        (
            _In_  ULONG                     EpIndex,
            _In_  ULONG                     Length,
            _Out_ PKSPROPERTY_DESCRIPTION   *PropValues
        );
        
        NTSTATUS    SetA2dpHpSpeakerMute
        (
            _In_ LONG   Channel,
            _In_ LONG   Mute
        );
        
        NTSTATUS    GetA2dpHpSpeakerMute
        (
            _In_  LONG  Channel,
            _Out_ LONG  *Mute
        );
        
        NTSTATUS    EnableA2dpHpSpeakerMuteStatusNotification();

        NTSTATUS    GetA2dpHpConnectionStatus
        (
            _Out_ BOOL *ConnectionStatus
        );

        NTSTATUS    EnableA2dpHpConnectionStatusNotification();


        NTSTATUS    GetA2dpHpEndpointFormatsIntersection
        (
            _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS        pDeviceFormats,
            _Out_   PSIDEBANDAUDIO_SUPPORTED_FORMATS        *ppSupportedFormatsIntersection
        );

        NTSTATUS    VerifyEndpointFormatCompatibility
        (
            _In_    PSIDEBANDAUDIO_SUPPORTED_FORMATS        EpSupportedFormats,
            _In_    PKSDATAFORMAT_WAVEFORMATEXTENSIBLE      pHardwareSupportedFormats,
            _In_    ULONG                                   NumHardwareSupportedFormats,
            _Out_   PULONG                                  pHardwareFormatIndex
        );

        NTSTATUS    GetA2dpHpEndpointTransportResources
        (
            _In_    ULONG                                   EpIndex,
            _In_    BOOL                                    bFeedback,
            _Out_   PA2DPHPDEVICE_EP_TRANSPORT_RESOURCES    pTransportResources
        );
        
        static
        NTSTATUS    CreateCustomEndpointMinipair
        (
            _In_                                            PENDPOINT_MINIPAIR pBaseMinipair,
            _In_                                            PUNICODE_STRING FriendlyName,
            _In_                                            PGUID pCategory,
            _In_                                            ULONG customFilterInterfacePropertyCount,
            _In_count_(customFilterInterfacePropertyCount)  DEVPROPERTY* customFilterInterfaceProperties,
            _Outptr_                                        PENDPOINT_MINIPAIR* ppCustomMinipair
        );

        static
        NTSTATUS    UpdateCustomEndpointCategory
        (
            _In_        PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter,
            _In_        PPCPIN_DESCRIPTOR pCustomMinipairTopoPins,
            _In_        PGUID pCategory
        );

        static
        VOID        DeleteCustomEndpointMinipair
        (
            _In_        PENDPOINT_MINIPAIR CustomMinipair
        );

        NTSTATUS    GetA2dpHpCodecCaps
        (
            _Outptr_    PA2DPHPDEVICE_CODEC_CAPABILITIES* ppCodecCaps
        );

        NTSTATUS    UpdateA2dpHpCodecCaps
        (
            _In_        PA2DPHPDEVICE_CODEC_CAPABILITIES pCodecCaps
        );

        NTSTATUS    SetSidebandClaimed(_In_ BOOL bClaimed);
        
        NTSTATUS    SpeakerStreamOpen();

        NTSTATUS    SpeakerStreamStart();

        NTSTATUS    SpeakerStreamSuspend();

        NTSTATUS    SpeakerStreamClose();

        NTSTATUS    SetA2dpHpStreamOpen(ULONG EpIndex, PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pStreamFormat, PSIDEBANDAUDIO_ENDPOINT_DESCRIPTOR2 pEndpointDescriptor);

        NTSTATUS    SetA2dpHpStreamStart();

        NTSTATUS    SetA2dpHpStreamSuspend();

        NTSTATUS    SetA2dpHpStreamClose();

        NTSTATUS    EnableA2dpHpSpeakerStreamStatusNotification();

        NTSTATUS    EnableA2dpHpSpeakerSiopNotification();
        
        NTSTATUS    StopA2dpHpSpeakerStreamStatusNotification();
        
        NTSTATUS    CreateFilterNames(
            _In_ PUNICODE_STRING A2dpHpDeviceSymbolicLinkName
        );
        
        //
        // WDF I/O Target callback.
        //
        static
        EVT_WDF_IO_TARGET_QUERY_REMOVE    EvtA2dpHpTargetQueryRemove;

        static
        EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtA2dpHpTargetRemoveCanceled;

        static
        EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtA2dpHpTargetRemoveComplete;

        //
        // Status notifications callbacks.
        //
        static 
        EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtA2dpHpDeviceStreamStatusCompletion;

        static 
        EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtA2dpHpDeviceNotificationStatusCompletion;
        
        static
        EVT_WDF_WORKITEM                   EvtA2dpHpDeviceNotificationStatusWorkItem;
};


<|start_filename|>serial/serial/ioctl.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation

Module Name:

    ioctl.c

Abstract:

    This module contains the ioctl dispatcher as well as a couple
    of routines that are generally just called in response to
    ioctl calls.

Environment:

    Kernel mode

--*/

#include "precomp.h"

#if defined(EVENT_TRACING)
#include "ioctl.tmh"
#endif

EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetModemUpdate;
EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetCommStatus;
EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetEscapeChar;

PCHAR
SerialGetIoctlName(
    IN ULONG      IoControlCode
    )
/*++

Routine Description:
    SerialGetIoctlName returns the name of the ioctl

--*/
{
    switch (IoControlCode)
    {
    case IOCTL_SERIAL_SET_BAUD_RATE : return "IOCTL_SERIAL_SET_BAUD_RATE";
    case IOCTL_SERIAL_GET_BAUD_RATE: return "IOCTL_SERIAL_GET_BAUD_RATE";
    case IOCTL_SERIAL_GET_MODEM_CONTROL: return "IOCTL_SERIAL_GET_MODEM_CONTROL";
    case IOCTL_SERIAL_SET_MODEM_CONTROL: return "IOCTL_SERIAL_SET_MODEM_CONTROL";
    case IOCTL_SERIAL_SET_FIFO_CONTROL: return "IOCTL_SERIAL_SET_FIFO_CONTROL";
    case IOCTL_SERIAL_SET_LINE_CONTROL: return "IOCTL_SERIAL_SET_LINE_CONTROL";
    case IOCTL_SERIAL_GET_LINE_CONTROL: return "IOCTL_SERIAL_GET_LINE_CONTROL";
    case IOCTL_SERIAL_SET_TIMEOUTS: return "IOCTL_SERIAL_SET_TIMEOUTS";
    case IOCTL_SERIAL_GET_TIMEOUTS: return "IOCTL_SERIAL_GET_TIMEOUTS";
    case IOCTL_SERIAL_SET_CHARS: return "IOCTL_SERIAL_SET_CHARS";
    case IOCTL_SERIAL_GET_CHARS: return "IOCTL_SERIAL_GET_CHARS";
    case IOCTL_SERIAL_SET_DTR: return "IOCTL_SERIAL_SET_DTR";
    case IOCTL_SERIAL_CLR_DTR: return "IOCTL_SERIAL_SET_DTR";
    case IOCTL_SERIAL_RESET_DEVICE: return "IOCTL_SERIAL_RESET_DEVICE";
    case IOCTL_SERIAL_SET_RTS: return "IOCTL_SERIAL_SET_RTS";
    case IOCTL_SERIAL_CLR_RTS: return "IOCTL_SERIAL_CLR_RTS";
    case IOCTL_SERIAL_SET_XOFF: return "IOCTL_SERIAL_SET_XOFF";
    case IOCTL_SERIAL_SET_XON: return "IOCTL_SERIAL_SET_XON";
    case IOCTL_SERIAL_SET_BREAK_ON: return "IOCTL_SERIAL_SET_BREAK_ON";
    case IOCTL_SERIAL_SET_BREAK_OFF: return "IOCTL_SERIAL_SET_BREAK_OFF";
    case IOCTL_SERIAL_SET_QUEUE_SIZE: return "IOCTL_SERIAL_SET_QUEUE_SIZE";
    case IOCTL_SERIAL_GET_WAIT_MASK: return "IOCTL_SERIAL_GET_WAIT_MASK";
    case IOCTL_SERIAL_SET_WAIT_MASK: return "IOCTL_SERIAL_SET_WAIT_MASK";
    case IOCTL_SERIAL_WAIT_ON_MASK: return "IOCTL_SERIAL_WAIT_ON_MASK";
    case IOCTL_SERIAL_IMMEDIATE_CHAR: return "IOCTL_SERIAL_IMMEDIATE_CHAR";
    case IOCTL_SERIAL_PURGE: return "IOCTL_SERIAL_PURGE";
    case IOCTL_SERIAL_GET_HANDFLOW: return "IOCTL_SERIAL_GET_HANDFLOW";
    case IOCTL_SERIAL_SET_HANDFLOW: return "IOCTL_SERIAL_SET_HANDFLOW";
    case IOCTL_SERIAL_GET_MODEMSTATUS: return "IOCTL_SERIAL_GET_MODEMSTATUS";
    case IOCTL_SERIAL_GET_DTRRTS: return "IOCTL_SERIAL_GET_DTRRTS";
    case IOCTL_SERIAL_GET_COMMSTATUS: return "IOCTL_SERIAL_GET_COMMSTATUS";
    case IOCTL_SERIAL_GET_PROPERTIES: return "IOCTL_SERIAL_GET_PROPERTIES";
    case IOCTL_SERIAL_XOFF_COUNTER: return "IOCTL_SERIAL_XOFF_COUNTER";
    case IOCTL_SERIAL_LSRMST_INSERT: return "IOCTL_SERIAL_LSRMST_INSERT";
    case IOCTL_SERIAL_CONFIG_SIZE: return "IOCTL_SERIAL_CONFIG_SIZE";
    case IOCTL_SERIAL_GET_STATS: return "IOCTL_SERIAL_GET_STATS";
    case IOCTL_SERIAL_CLEAR_STATS: return "IOCTL_SERIAL_CLEAR_STATS";
    default: return "UnKnown ioctl";
    }
}



BOOLEAN
SerialGetStats(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    In sync with the interrpt service routine (which sets the perf stats)
    return the perf stats to the caller.


Arguments:

    Context - Pointer to a the request.

Return Value:

    This routine always returns FALSE.

--*/

{
    PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
    PSERIALPERF_STATS sp = reqContext->SystemBuffer;

    UNREFERENCED_PARAMETER(Interrupt);

    *sp = extension->PerfStats;
    return FALSE;

}


BOOLEAN
SerialClearStats(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    In sync with the interrpt service routine (which sets the perf stats)
    clear the perf stats.


Arguments:

    Context - Pointer to a the extension.

Return Value:

    This routine always returns FALSE.

--*/

{
    UNREFERENCED_PARAMETER(Interrupt);

    RtlZeroMemory(
        &((PSERIAL_DEVICE_EXTENSION)Context)->PerfStats,
        sizeof(SERIALPERF_STATS)
        );

    RtlZeroMemory(&((PSERIAL_DEVICE_EXTENSION)Context)->WmiPerfData,
                 sizeof(SERIAL_WMI_PERF_DATA));

    return FALSE;
}



BOOLEAN
SerialSetChars(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This routine is used to set the special characters for the
    driver.

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a special characters
              structure.

Return Value:

    This routine always returns FALSE.

--*/

{
    UNREFERENCED_PARAMETER(Interrupt);

    ((PSERIAL_IOCTL_SYNC)Context)->Extension->SpecialChars =
        *((PSERIAL_CHARS)(((PSERIAL_IOCTL_SYNC)Context)->Data));

    return FALSE;
}


BOOLEAN
SerialSetBaud(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This routine is used to set the baud rate of the device.

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and what should be the current
              baud rate.

Return Value:

    This routine always returns FALSE.

--*/

{
    PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
    USHORT Appropriate = PtrToUshort(((PSERIAL_IOCTL_SYNC)Context)->Data);

    UNREFERENCED_PARAMETER(Interrupt);

    WRITE_DIVISOR_LATCH(
        Extension,
        Extension->Controller,
        Appropriate
        );

    return FALSE;
}


BOOLEAN
SerialSetLineControl(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This routine is used to set the buad rate of the device.

Arguments:

    Context - Pointer to the device extension.

Return Value:

    This routine always returns FALSE.

--*/

{
    PSERIAL_DEVICE_EXTENSION Extension = Context;

    UNREFERENCED_PARAMETER(Interrupt);

    WRITE_LINE_CONTROL(Extension,
        Extension->Controller,
        Extension->LineControl
        );

    return FALSE;
}


BOOLEAN
SerialGetModemUpdate(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This routine is simply used to call the interrupt level routine
    that handles modem status update.

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a ulong.

Return Value:

    This routine always returns FALSE.

--*/

{
    PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
    ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);

    UNREFERENCED_PARAMETER(Interrupt);

    *Result = SerialHandleModemUpdate(
                  Extension,
                  FALSE
                  );

    return FALSE;
}



BOOLEAN
SerialSetMCRContents(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )
/*++

Routine Description:

    This routine is simply used to set the contents of the MCR

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a ulong.

Return Value:

    This routine always returns FALSE.

--*/
{
   PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
   ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);

   UNREFERENCED_PARAMETER(Interrupt);

   //
   // This is severe casting abuse!!!
   //
   WRITE_MODEM_CONTROL(Extension, Extension->Controller, (UCHAR)PtrToUlong(Result));

   return FALSE;
}




BOOLEAN
SerialGetMCRContents(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This routine is simply used to get the contents of the MCR

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a ulong.

Return Value:

    This routine always returns FALSE.

--*/

{
    PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
    ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);

    UNREFERENCED_PARAMETER(Interrupt);

    *Result = READ_MODEM_CONTROL(Extension, Extension->Controller);

    return FALSE;
}




BOOLEAN
SerialSetFCRContents(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )
/*++

Routine Description:

    This routine is simply used to set the contents of the FCR

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a ulong.

Return Value:

    This routine always returns FALSE.

--*/
{
   PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
   ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);

   UNREFERENCED_PARAMETER(Interrupt);

   //
   // This is severe casting abuse!!!
   //
   WRITE_FIFO_CONTROL(Extension, Extension->Controller, (UCHAR)*Result);

   return FALSE;
}



BOOLEAN
SerialGetCommStatus(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This is used to get the current state of the serial driver.

Arguments:

    Context - Pointer to a structure that contains a pointer to
              the device extension and a pointer to a serial status
              record.

Return Value:

    This routine always returns FALSE.

--*/

{
    PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
    PSERIAL_STATUS Stat = ((PSERIAL_IOCTL_SYNC)Context)->Data;

    UNREFERENCED_PARAMETER(Interrupt);

    Stat->Errors = Extension->ErrorWord;
    Extension->ErrorWord = 0;

    //
    // Eof isn't supported in binary mode
    //
    Stat->EofReceived = FALSE;

    Stat->AmountInInQueue = Extension->CharsInInterruptBuffer;

    Stat->AmountInOutQueue = Extension->TotalCharsQueued;

    if (Extension->WriteLength) {

        //
        // By definition if we have a writelength the we have
        // a current write request.
        //
     PREQUEST_CONTEXT reqContext = NULL;

        ASSERT(Extension->CurrentWriteRequest);
        ASSERT(Stat->AmountInOutQueue >= Extension->WriteLength);

     reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
        Stat->AmountInOutQueue -= reqContext->Length - (Extension->WriteLength);

    }

    Stat->WaitForImmediate = Extension->TransmitImmediate;

    Stat->HoldReasons = 0;
    if (Extension->TXHolding) {

        if (Extension->TXHolding & SERIAL_TX_CTS) {

            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;

        }

        if (Extension->TXHolding & SERIAL_TX_DSR) {

            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;

        }

        if (Extension->TXHolding & SERIAL_TX_DCD) {

            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;

        }

        if (Extension->TXHolding & SERIAL_TX_XOFF) {

            Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;

        }

        if (Extension->TXHolding & SERIAL_TX_BREAK) {

            Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;

        }

    }

    if (Extension->RXHolding & SERIAL_RX_DSR) {

        Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;

    }

    if (Extension->RXHolding & SERIAL_RX_XOFF) {

        Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;

    }

    return FALSE;
}


BOOLEAN
SerialSetEscapeChar(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID         Context
    )

/*++

Routine Description:

    This is used to set the character that will be used to escape
    line status and modem status information when the application
    has set up that line status and modem status should be passed
    back in the data stream.

Arguments:

    Context - Pointer to the request that is specify the escape character.
              Implicitly - An escape character of 0 means no escaping
              will occur.

Return Value:

    This routine always returns FALSE.

--*/

{

    PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));

    UNREFERENCED_PARAMETER(Interrupt);

    extension->EscapeChar = *(PUCHAR)reqContext->SystemBuffer;

    return FALSE;
}

VOID
SerialEvtIoDeviceControl(
    IN WDFQUEUE     Queue,
    IN WDFREQUEST   Request,
    IN size_t       OutputBufferLength,
    IN size_t       InputBufferLength,
    IN ULONG        IoControlCode
    )

/*++

Routine Description:

    This routine provides the initial processing for all of the
    Ioctrls for the serial device.

Arguments:

    Request - Pointer to the WDFREQUEST for the current request

Return Value:

    The function value is the final status of the call

--*/

{
    //
    // The status that gets returned to the caller and
    // set in the Request.
    //
    NTSTATUS Status;

    //
    // Just what it says.  This is the serial specific device
    // extension of the device object create for the serial driver.
    //
    PSERIAL_DEVICE_EXTENSION Extension = NULL;

    PVOID buffer;
    PREQUEST_CONTEXT reqContext;
    size_t  bufSize;

    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

    reqContext = SerialGetRequestContext(Request);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "%s for: %p\n",
                                    SerialGetIoctlName(IoControlCode), Request);

    Extension = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));

    //
    // We expect to be open so all our pages are locked down.  This is, after
    // all, an IO operation, so the device should be open first.
    //

    if (Extension->DeviceIsOpened != TRUE) {
       SerialCompleteRequest(Request, STATUS_INVALID_DEVICE_REQUEST, 0);
       return;
    }


    if (SerialCompleteIfError(Extension, Request) != STATUS_SUCCESS) {

       SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS,
                    "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
       return;

    }

    reqContext = SerialGetRequestContext(Request);
    reqContext->Information = 0;
    reqContext->Status = STATUS_SUCCESS;
    reqContext->MajorFunction = IRP_MJ_DEVICE_CONTROL;


    Status = STATUS_SUCCESS;

    switch (IoControlCode) {

        case IOCTL_SERIAL_SET_BAUD_RATE : {

            ULONG BaudRate;
            //
            // Will hold the value of the appropriate divisor for
            // the requested baud rate.  If the baudrate is invalid
            // (because the device won't support that baud rate) then
            // this value is undefined.
            //
            // Note: in one sense the concept of a valid baud rate
            // is cloudy.  We could allow the user to request any
            // baud rate.  We could then calculate the divisor needed
            // for that baud rate.  As long as the divisor wasn't less
            // than one we would be "ok".  (The percentage difference
            // between the "true" divisor and the "rounded" value given
            // to the hardware might make it unusable, but... )  It would
            // really be up to the user to "Know" whether the baud rate
            // is suitable.  So much for theory, *We* only support a given
            // set of baud rates.
            //
            SHORT AppropriateDivisor;

            Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            BaudRate = ((PSERIAL_BAUD_RATE)(buffer))->BaudRate;


            //
            // Get the baud rate from the request.  We pass it
            // to a routine which will set the correct divisor.
            //

            Status = SerialGetDivisorFromBaud(
                         Extension->ClockRate,
                         BaudRate,
                         &AppropriateDivisor
                         );


            if (NT_SUCCESS(Status)) {

                SERIAL_IOCTL_SYNC S;


                Extension->CurrentBaud = BaudRate;
                Extension->WmiCommData.BaudRate = BaudRate;

                S.Extension = Extension;
                S.Data = (PVOID) (ULONG_PTR) AppropriateDivisor;
                WdfInterruptSynchronize(
                    Extension->WdfInterrupt,
                    SerialSetBaud,
                    &S
                    );

            }

            break;
        }

        case IOCTL_SERIAL_GET_BAUD_RATE: {

            PSERIAL_BAUD_RATE Br;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            Br = (PSERIAL_BAUD_RATE)buffer;

            Br->BaudRate = Extension->CurrentBaud;

            reqContext->Information = sizeof(SERIAL_BAUD_RATE);

            break;

        }

        case IOCTL_SERIAL_GET_MODEM_CONTROL: {
            SERIAL_IOCTL_SYNC S;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(ULONG);

            S.Extension = Extension;
            S.Data = buffer;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialGetMCRContents,
                &S
                );

            break;
        }
        case IOCTL_SERIAL_SET_MODEM_CONTROL: {
            SERIAL_IOCTL_SYNC S;

            Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            S.Extension = Extension;
            S.Data = buffer;


            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetMCRContents,
                &S
                );

            break;
        }
        case IOCTL_SERIAL_SET_FIFO_CONTROL: {
            SERIAL_IOCTL_SYNC S;

            Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            S.Extension = Extension;
            S.Data = buffer;


            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetFCRContents,
                &S
                );

            break;
        }
        case IOCTL_SERIAL_SET_LINE_CONTROL: {

            PSERIAL_LINE_CONTROL Lc;
            UCHAR LData;
            UCHAR LStop;
            UCHAR LParity;
            UCHAR Mask = 0xff;

            Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            //
            // Points to the line control record in the Request.
            //
            Lc =  (PSERIAL_LINE_CONTROL)buffer;

            switch (Lc->WordLength) {
                case 5: {

                    LData = SERIAL_5_DATA;
                    Mask = 0x1f;
                    break;

                }
                case 6: {

                    LData = SERIAL_6_DATA;
                    Mask = 0x3f;
                    break;

                }
                case 7: {

                    LData = SERIAL_7_DATA;
                    Mask = 0x7f;
                    break;

                }
                case 8: {

                    LData = SERIAL_8_DATA;
                    break;

                }
                default: {

                    Status = STATUS_INVALID_PARAMETER;
                    goto DoneWithIoctl;

                }

            }

            Extension->WmiCommData.BitsPerByte = Lc->WordLength;

            switch (Lc->Parity) {

                case NO_PARITY: {
                    Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
                    LParity = SERIAL_NONE_PARITY;
                    break;

                }
                case EVEN_PARITY: {
                    Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
                    LParity = SERIAL_EVEN_PARITY;
                    break;

                }
                case ODD_PARITY: {
                    Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
                    LParity = SERIAL_ODD_PARITY;
                    break;

                }
                case SPACE_PARITY: {
                    Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
                    LParity = SERIAL_SPACE_PARITY;
                    break;

                }
                case MARK_PARITY: {
                    Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
                    LParity = SERIAL_MARK_PARITY;
                    break;

                }
                default: {

                    Status = STATUS_INVALID_PARAMETER;
                    goto DoneWithIoctl;
                    break;
                }

            }

            switch (Lc->StopBits) {

                case STOP_BIT_1: {
                    Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1;
                    LStop = SERIAL_1_STOP;
                    break;
                }
                case STOP_BITS_1_5: {

                    if (LData != SERIAL_5_DATA) {

                        Status = STATUS_INVALID_PARAMETER;
                        goto DoneWithIoctl;
                    }
                    Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1_5;
                    LStop = SERIAL_1_5_STOP;
                    break;

                }
                case STOP_BITS_2: {

                    if (LData == SERIAL_5_DATA) {

                        Status = STATUS_INVALID_PARAMETER;
                        goto DoneWithIoctl;
                    }
                    Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_2;
                    LStop = SERIAL_2_STOP;
                    break;

                }
                default: {

                    Status = STATUS_INVALID_PARAMETER;
                    goto DoneWithIoctl;
                }

            }

            Extension->LineControl =
                (UCHAR)((Extension->LineControl & SERIAL_LCR_BREAK) |
                        (LData | LParity | LStop));
            Extension->ValidDataMask = Mask;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetLineControl,
                Extension
                );

            break;
        }
        case IOCTL_SERIAL_GET_LINE_CONTROL: {

            PSERIAL_LINE_CONTROL Lc;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            Lc = (PSERIAL_LINE_CONTROL)buffer;

            RtlZeroMemory(buffer, OutputBufferLength);

            if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
                Lc->WordLength = 5;
            } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                        == SERIAL_6_DATA) {
                Lc->WordLength = 6;
            } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                        == SERIAL_7_DATA) {
                Lc->WordLength = 7;
            } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                        == SERIAL_8_DATA) {
                Lc->WordLength = 8;
            }

            if ((Extension->LineControl & SERIAL_PARITY_MASK)
                    == SERIAL_NONE_PARITY) {
                Lc->Parity = NO_PARITY;
            } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
                    == SERIAL_ODD_PARITY) {
                Lc->Parity = ODD_PARITY;
            } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
                    == SERIAL_EVEN_PARITY) {
                Lc->Parity = EVEN_PARITY;
            } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
                    == SERIAL_MARK_PARITY) {
                Lc->Parity = MARK_PARITY;
            } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
                    == SERIAL_SPACE_PARITY) {
                Lc->Parity = SPACE_PARITY;
            }

            if (Extension->LineControl & SERIAL_2_STOP) {
                if (Lc->WordLength == 5) {
                    Lc->StopBits = STOP_BITS_1_5;
                } else {
                    Lc->StopBits = STOP_BITS_2;
                }
            } else {
                Lc->StopBits = STOP_BIT_1;
            }

            reqContext->Information = sizeof(SERIAL_LINE_CONTROL);

            break;
        }
        case IOCTL_SERIAL_SET_TIMEOUTS: {

            PSERIAL_TIMEOUTS NewTimeouts;

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            NewTimeouts =(PSERIAL_TIMEOUTS)buffer;

            if ((NewTimeouts->ReadIntervalTimeout == MAXULONG) &&
                (NewTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) &&
                (NewTimeouts->ReadTotalTimeoutConstant == MAXULONG)) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }


            Extension->Timeouts.ReadIntervalTimeout =
                NewTimeouts->ReadIntervalTimeout;

            Extension->Timeouts.ReadTotalTimeoutMultiplier =
                NewTimeouts->ReadTotalTimeoutMultiplier;

            Extension->Timeouts.ReadTotalTimeoutConstant =
                NewTimeouts->ReadTotalTimeoutConstant;

            Extension->Timeouts.WriteTotalTimeoutMultiplier =
                NewTimeouts->WriteTotalTimeoutMultiplier;

            Extension->Timeouts.WriteTotalTimeoutConstant =
                NewTimeouts->WriteTotalTimeoutConstant;

            break;
        }
        case IOCTL_SERIAL_GET_TIMEOUTS: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            *((PSERIAL_TIMEOUTS)buffer) = Extension->Timeouts;
            reqContext->Information = sizeof(SERIAL_TIMEOUTS);

            break;
        }
        case IOCTL_SERIAL_SET_CHARS: {

            SERIAL_IOCTL_SYNC S;
            PSERIAL_CHARS NewChars;

           Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            NewChars = (PSERIAL_CHARS)buffer;

            //
            // The only thing that can be wrong with the chars
            // is that the xon and xoff characters are the
            // same.
            //
#if 0
            if (NewChars->XonChar == NewChars->XoffChar) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }
#endif

            //
            // We acquire the control lock so that only
            // one request can GET or SET the characters
            // at a time.  The sets could be synchronized
            // by the interrupt spinlock, but that wouldn't
            // prevent multiple gets at the same time.
            //

            S.Extension = Extension;
            S.Data = NewChars;

            //
            // Under the protection of the lock, make sure that
            // the xon and xoff characters aren't the same as
            // the escape character.
            //

            if (Extension->EscapeChar) {

                if ((Extension->EscapeChar == NewChars->XonChar) ||
                    (Extension->EscapeChar == NewChars->XoffChar)) {

                    Status = STATUS_INVALID_PARAMETER;
                    break;

                }

            }

            Extension->WmiCommData.XonCharacter = NewChars->XonChar;
            Extension->WmiCommData.XoffCharacter = NewChars->XoffChar;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetChars,
                &S
                );


            break;

        }
        case IOCTL_SERIAL_GET_CHARS: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            *((PSERIAL_CHARS)buffer) = Extension->SpecialChars;
            reqContext->Information = sizeof(SERIAL_CHARS);


            break;
        }
        case IOCTL_SERIAL_SET_DTR:
        case IOCTL_SERIAL_CLR_DTR: {


            //
            // We acquire the lock so that we can check whether
            // automatic dtr flow control is enabled.  If it is
            // then we return an error since the app is not allowed
            // to touch this if it is automatic.
            //

            if ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK)
                == SERIAL_DTR_HANDSHAKE) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                WdfInterruptSynchronize(
                    Extension->WdfInterrupt,
                    ((IoControlCode ==
                     IOCTL_SERIAL_SET_DTR)?
                     (SerialSetDTR):(SerialClrDTR)),
                    Extension
                    );

            }

            break;
        }
        case IOCTL_SERIAL_RESET_DEVICE: {

            break;
        }
        case IOCTL_SERIAL_SET_RTS:
        case IOCTL_SERIAL_CLR_RTS: {

            //
            // We acquire the lock so that we can check whether
            // automatic rts flow control or transmit toggleing
            // is enabled.  If it is then we return an error since
            // the app is not allowed to touch this if it is automatic
            // or toggling.
            //

            if (((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
                 == SERIAL_RTS_HANDSHAKE) ||
                ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
                 == SERIAL_TRANSMIT_TOGGLE)) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                WdfInterruptSynchronize(
                    Extension->WdfInterrupt,
                    ((IoControlCode ==
                     IOCTL_SERIAL_SET_RTS)?
                     (SerialSetRTS):(SerialClrRTS)),
                    Extension
                    );

            }

            break;

        }
        case IOCTL_SERIAL_SET_XOFF: {

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialPretendXoff,
                Extension
                );

            break;

        }
        case IOCTL_SERIAL_SET_XON: {

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialPretendXon,
                Extension
                );

            break;

        }
        case IOCTL_SERIAL_SET_BREAK_ON: {

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialTurnOnBreak,
                Extension
                );

            break;
        }
        case IOCTL_SERIAL_SET_BREAK_OFF: {

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialTurnOffBreak,
                Extension
                );

            break;
        }
        case IOCTL_SERIAL_SET_QUEUE_SIZE: {

            //
            // Type ahead buffer is fixed, so we just validate
            // the the users request is not bigger that our
            // own internal buffer size.
            //

            PSERIAL_QUEUE_SIZE Rs;

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_QUEUE_SIZE), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            ASSERT(Extension->InterruptReadBuffer);

            Rs =   (PSERIAL_QUEUE_SIZE)buffer;

            reqContext->SystemBuffer = buffer;

            //
            // We have to allocate the memory for the new
            // buffer while we're still in the context of the
            // caller.  We don't even try to protect this
            // with a lock because the value could be stale
            // as soon as we release the lock - The only time
            // we will know for sure is when we actually try
            // to do the resize.
            //

            if (Rs->InSize <= Extension->BufferSize) {

                Status = STATUS_SUCCESS;
                break;

            }

            reqContext->Type3InputBuffer =
                    ExAllocatePool2(
                        POOL_FLAG_NON_PAGED | POOL_FLAG_USE_QUOTA,
                        Rs->InSize,
                        POOL_TAG
                        );

            if (!reqContext->Type3InputBuffer) {

                Status = STATUS_INSUFFICIENT_RESOURCES;
                break;

            }

            //
            // Well the data passed was big enough.  Do the request.
            //
            // There are two reason we place it in the read queue:
            //
            // 1) We want to serialize these resize requests so that
            //    they don't contend with each other.
            //
            // 2) We want to serialize these requests with reads since
            //    we don't want reads and resizes contending over the
            //    read buffer.
            //


            SerialStartOrQueue(
                       Extension,
                       Request,
                       Extension->ReadQueue,
                       &Extension->CurrentReadRequest,
                       SerialStartRead
                       );

            return;
        }
        case IOCTL_SERIAL_GET_WAIT_MASK: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            //
            // Simple scalar read.  No reason to acquire a lock.
            //

            reqContext->Information = sizeof(ULONG);

            *((ULONG *)buffer) = Extension->IsrWaitMask;

            break;

        }
        case IOCTL_SERIAL_SET_WAIT_MASK: {

            ULONG NewMask;

            SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for set mask\n");

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            NewMask = *((ULONG *)buffer);
            reqContext->SystemBuffer = buffer;

            //
            // Make sure that the mask only contains valid
            // waitable events.
            //

            if (NewMask & ~(SERIAL_EV_RXCHAR   |
                            SERIAL_EV_RXFLAG   |
                            SERIAL_EV_TXEMPTY  |
                            SERIAL_EV_CTS      |
                            SERIAL_EV_DSR      |
                            SERIAL_EV_RLSD     |
                            SERIAL_EV_BREAK    |
                            SERIAL_EV_ERR      |
                            SERIAL_EV_RING     |
                            SERIAL_EV_PERR     |
                            SERIAL_EV_RX80FULL |
                            SERIAL_EV_EVENT1   |
                            SERIAL_EV_EVENT2)) {

                SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Unknown mask %x\n", NewMask);

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            //
            // Either start this request or put it on the
            // queue.
            //

            SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing set mask request %p"
                             "\n", Request);

            SerialStartOrQueue(Extension, Request, Extension->MaskQueue,
                                      &Extension->CurrentMaskRequest,
                                      SerialStartMask);
            return;

        }
        case IOCTL_SERIAL_WAIT_ON_MASK: {

            SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for wait mask\n");

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->SystemBuffer = buffer;

            //
            // Either start this request or put it on the
            // queue.
            //

            SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing wait mask request"
                             "%p\n", Request);

            SerialStartOrQueue(
                       Extension,
                       Request,
                       Extension->MaskQueue,
                       &Extension->CurrentMaskRequest,
                       SerialStartMask
                       );
            return;
        }
        case IOCTL_SERIAL_IMMEDIATE_CHAR: {

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->SystemBuffer = buffer;

            if (Extension->CurrentImmediateRequest) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                //
                // We can queue the char.  We need to set
                // a cancel routine because flow control could
                // keep the char from transmitting.  Make sure
                // that the request hasn't already been canceled.
                //

                Extension->CurrentImmediateRequest = Request;
                Extension->TotalCharsQueued++;
                SerialStartImmediate(Extension);
                return;

            }

            break;

        }
        case IOCTL_SERIAL_PURGE: {

            ULONG Mask;

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            //
            // Check to make sure that the mask only has
            // 0 or the other appropriate values.
            //

            Mask = *((ULONG *)(buffer));

            if ((!Mask) || (Mask & (~(SERIAL_PURGE_TXABORT |
                                      SERIAL_PURGE_RXABORT |
                                      SERIAL_PURGE_TXCLEAR |
                                      SERIAL_PURGE_RXCLEAR
                                     )
                                   )
                           )) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            reqContext->SystemBuffer = buffer;

            //
            // Either start this request or put it on the
            // queue.
            //

            SerialStartOrQueue(
                       Extension,
                       Request,
                       Extension->PurgeQueue,
                       &Extension->CurrentPurgeRequest,
                       SerialStartPurge
                       );
            return;
        }
        case IOCTL_SERIAL_GET_HANDFLOW: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(SERIAL_HANDFLOW);

            *((PSERIAL_HANDFLOW)buffer) = Extension->HandFlow;

            break;

        }
        case IOCTL_SERIAL_SET_HANDFLOW: {

            SERIAL_IOCTL_SYNC S;
            PSERIAL_HANDFLOW HandFlow;

            //
            // Make sure that the hand shake and control is the
            // right size.
            //

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            HandFlow = (PSERIAL_HANDFLOW)buffer;

            //
            // Make sure that there are no invalid bits set in
            // the control and handshake.
            //

            if (HandFlow->ControlHandShake & SERIAL_CONTROL_INVALID) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            if (HandFlow->FlowReplace & SERIAL_FLOW_INVALID) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            //
            // Make sure that the app hasn't set an invlid DTR mode.
            //

            if ((HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
                SERIAL_DTR_MASK) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            //
            // Make sure that haven't set totally invalid xon/xoff
            // limits.
            //

            if ((HandFlow->XonLimit < 0) ||
                ((ULONG)HandFlow->XonLimit > Extension->BufferSize)) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            if ((HandFlow->XoffLimit < 0) ||
                ((ULONG)HandFlow->XoffLimit > Extension->BufferSize)) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }

            S.Extension = Extension;
            S.Data = HandFlow;

            //
            // Under the protection of the lock, make sure that
            // we aren't turning on error replacement when we
            // are doing line status/modem status insertion.
            //

            if (Extension->EscapeChar) {

                if (HandFlow->FlowReplace & SERIAL_ERROR_CHAR) {

                    Status = STATUS_INVALID_PARAMETER;
                    break;

                }

            }

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetHandFlow,
                &S
                );

            break;

        }
        case IOCTL_SERIAL_GET_MODEMSTATUS: {

            SERIAL_IOCTL_SYNC S;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(ULONG);

            S.Extension = Extension;
            S.Data = buffer;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialGetModemUpdate,
                &S
                );

            break;

        }
        case IOCTL_SERIAL_GET_DTRRTS: {

            ULONG ModemControl;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(ULONG);
            reqContext->Status = STATUS_SUCCESS;

            //
            // Reading this hardware has no effect on the device.
            //

            ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);

            ModemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE;

            *(PULONG)buffer = ModemControl;

            break;

        }
        case IOCTL_SERIAL_GET_COMMSTATUS: {

            SERIAL_IOCTL_SYNC S;

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_STATUS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(SERIAL_STATUS);

            S.Extension = Extension;
            S.Data =  buffer;

            //
            // Acquire the cancel spin lock so nothing much
            // changes while were getting the state.
            //

            //IoAcquireCancelSpinLock(&OldIrql);

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialGetCommStatus,
                &S
                );

            //IoReleaseCancelSpinLock(OldIrql);

            break;

        }
        case IOCTL_SERIAL_GET_PROPERTIES: {


            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_COMMPROP), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            //
            // No synchronization is required since this information
            // is "static".
            //

            SerialGetProperties(
                Extension,
                buffer
                );

            reqContext->Information = sizeof(SERIAL_COMMPROP);
            reqContext->Status = STATUS_SUCCESS;

            break;
        }
        case IOCTL_SERIAL_XOFF_COUNTER: {

            PSERIAL_XOFF_COUNTER Xc;

            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_XOFF_COUNTER), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            Xc = (PSERIAL_XOFF_COUNTER)buffer;

            if (Xc->Counter <= 0) {

                Status = STATUS_INVALID_PARAMETER;
                break;

            }
            reqContext->SystemBuffer = buffer;

            //
            // There is no output, so make that clear now
            //

            reqContext->Information = 0;

            //
            // So far so good.  Put the request onto the write queue.
            //

            SerialStartOrQueue(
                       Extension,
                       Request,
                       Extension->WriteQueue,
                       &Extension->CurrentWriteRequest,
                       SerialStartWrite
                       );
            return;

        }
        case IOCTL_SERIAL_LSRMST_INSERT: {

            PUCHAR escapeChar;
            SERIAL_IOCTL_SYNC S;

            //
            // Make sure we get a byte.
            //
            Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->SystemBuffer = buffer;

            escapeChar = (PUCHAR)buffer;

            if (*escapeChar) {

                //
                // We've got some escape work to do.  We will make sure that
                // the character is not the same as the Xon or Xoff character,
                // or that we are already doing error replacement.
                //

                if ((*escapeChar == Extension->SpecialChars.XoffChar) ||
                    (*escapeChar == Extension->SpecialChars.XonChar) ||
                    (Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)) {

                    Status = STATUS_INVALID_PARAMETER;

                    break;

                }

            }

            S.Extension = Extension;
            S.Data = buffer;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialSetEscapeChar,
                reqContext
                );

            break;

        }
        case IOCTL_SERIAL_CONFIG_SIZE: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->Information = sizeof(ULONG);
            reqContext->Status = STATUS_SUCCESS;

            *(PULONG)buffer = 0;

            break;
        }
        case IOCTL_SERIAL_GET_STATS: {

            Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIALPERF_STATS), &buffer, &bufSize );
            if( !NT_SUCCESS(Status) ) {
                SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
                break;
            }

            reqContext->SystemBuffer = buffer;

            reqContext->Information = sizeof(SERIALPERF_STATS);
            reqContext->Status = STATUS_SUCCESS;

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialGetStats,
                reqContext
                );

            break;
        }
        case IOCTL_SERIAL_CLEAR_STATS: {

            WdfInterruptSynchronize(
                Extension->WdfInterrupt,
                SerialClearStats,
                Extension
                );
            break;
        }
        default: {

            Status = STATUS_INVALID_PARAMETER;
            break;
        }
    }

DoneWithIoctl:;

    reqContext->Status = Status;

    SerialCompleteRequest(Request, Status, reqContext->Information);

    return;

}


VOID
SerialGetProperties(
    IN PSERIAL_DEVICE_EXTENSION Extension,
    IN PSERIAL_COMMPROP Properties
    )

/*++

Routine Description:

    This function returns the capabilities of this particular
    serial device.

Arguments:

    Extension - The serial device extension.

    Properties - The structure used to return the properties

Return Value:

    None.

--*/

{


    RtlZeroMemory(
        Properties,
        sizeof(SERIAL_COMMPROP)
        );

    Properties->PacketLength = sizeof(SERIAL_COMMPROP);
    Properties->PacketVersion = 2;
    Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
    Properties->MaxTxQueue = 0;
    Properties->MaxRxQueue = 0;

    Properties->MaxBaud = SERIAL_BAUD_USER;
    Properties->SettableBaud = Extension->SupportedBauds;

    Properties->ProvSubType = SERIAL_SP_RS232;
    Properties->ProvCapabilities = SERIAL_PCF_DTRDSR |
                                   SERIAL_PCF_RTSCTS |
                                   SERIAL_PCF_CD     |
                                   SERIAL_PCF_PARITY_CHECK |
                                   SERIAL_PCF_XONXOFF |
                                   SERIAL_PCF_SETXCHAR |
                                   SERIAL_PCF_TOTALTIMEOUTS |
                                   SERIAL_PCF_INTTIMEOUTS;
    Properties->SettableParams = SERIAL_SP_PARITY |
                                 SERIAL_SP_BAUD |
                                 SERIAL_SP_DATABITS |
                                 SERIAL_SP_STOPBITS |
                                 SERIAL_SP_HANDSHAKING |
                                 SERIAL_SP_PARITY_CHECK |
                                 SERIAL_SP_CARRIER_DETECT;


    Properties->SettableData = SERIAL_DATABITS_5 |
                               SERIAL_DATABITS_6 |
                               SERIAL_DATABITS_7 |
                               SERIAL_DATABITS_8;
    Properties->SettableStopParity = SERIAL_STOPBITS_10 |
                                     SERIAL_STOPBITS_15 |
                                     SERIAL_STOPBITS_20 |
                                     SERIAL_PARITY_NONE |
                                     SERIAL_PARITY_ODD  |
                                     SERIAL_PARITY_EVEN |
                                     SERIAL_PARITY_MARK |
                                     SERIAL_PARITY_SPACE;
    Properties->CurrentTxQueue = 0;
    Properties->CurrentRxQueue = Extension->BufferSize;

}

VOID
SerialEvtIoInternalDeviceControl(
    IN WDFQUEUE     Queue,
    IN WDFREQUEST Request,
    IN size_t      OutputBufferLength,
    IN size_t      InputBufferLength,
    IN ULONG      IoControlCode
)
/*++

Routine Description:

    This routine provides the initial processing for all of the
    internal Ioctrls for the serial device.

Arguments:

    PDevObj - Pointer to the device object for this device

    PIrp - Pointer to the WDFREQUEST for the current request

Return Value:

    The function value is the final status of the call

--*/

{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION pDevExt = NULL;
    PVOID buffer;
    PREQUEST_CONTEXT reqContext;
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
    size_t  bufSize;

    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "SerialEvtIoInternalDeviceControl for: %p\n", Request);

    pDevExt = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));

    if (SerialCompleteIfError(pDevExt, Request) != STATUS_SUCCESS) {

       SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
                    "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
       return;

    }

    reqContext = SerialGetRequestContext(Request);
    reqContext->Information = 0;
    reqContext->Status = STATUS_SUCCESS;
    reqContext->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    switch (IoControlCode) {

    case IOCTL_SERIAL_INTERNAL_DO_WAIT_WAKE:
        //
        // Init wait-wake policy structure.
        //
        WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
        //
        // Override the default settings from allow user control to do not allow.
        //
        wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
        status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
        if (!NT_SUCCESS(status)) {
            SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
            break;
        }

       pDevExt->IsWakeEnabled = TRUE;
       status = STATUS_SUCCESS;
       break;

    case IOCTL_SERIAL_INTERNAL_CANCEL_WAIT_WAKE:

       WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
       //
       // Override the default settings.
       //
       wakeSettings.Enabled = WdfFalse; // Disables wait-wake
       wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
       status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
       if (!NT_SUCCESS(status)) {
           SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
           break;
       }

       pDevExt->IsWakeEnabled = FALSE;
       status = STATUS_SUCCESS;
       break;


    //
    // Put the serial port in a "filter-driver" appropriate state
    //
    // WARNING: This code assumes it is being called by a trusted kernel
    // entity and no checking is done on the validity of the settings
    // passed to IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS
    //
    // If validity checking is desired, the regular ioctl's should be used
    //

    case IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS:
    case IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS: {

       SERIAL_BASIC_SETTINGS   basic;
       PSERIAL_BASIC_SETTINGS  pBasic;
       SERIAL_IOCTL_SYNC       S;

       if (IoControlCode == IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS) {


         //
         // Check the buffer size
         //
         status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
         if( !NT_SUCCESS(status) ) {
            SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
            break;
         }

         reqContext->SystemBuffer = buffer;

          //
          // Everything is 0 -- timeouts and flow control and fifos.  If
          // We add additional features, this zero memory method
          // may not work.
          //

          RtlZeroMemory(&basic, sizeof(SERIAL_BASIC_SETTINGS));

          basic.TxFifo = 1;
          basic.RxFifo = SERIAL_1_BYTE_HIGH_WATER;

          reqContext->Information = sizeof(SERIAL_BASIC_SETTINGS);
          pBasic = (PSERIAL_BASIC_SETTINGS)buffer;

          //
          // Save off the old settings
          //

          RtlCopyMemory(&pBasic->Timeouts, &pDevExt->Timeouts,
                        sizeof(SERIAL_TIMEOUTS));

          RtlCopyMemory(&pBasic->HandFlow, &pDevExt->HandFlow,
                        sizeof(SERIAL_HANDFLOW));

          pBasic->RxFifo = pDevExt->RxFifoTrigger;
          pBasic->TxFifo = pDevExt->TxFifoAmount;

          //
          // Point to our new settings
          //

          pBasic = &basic;
       } else { // restoring settings

          status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
          if( !NT_SUCCESS(status) ) {
              SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
              break;
          }

          pBasic = (PSERIAL_BASIC_SETTINGS)buffer;
       }

       //
       // Set the timeouts
       //

       RtlCopyMemory(&pDevExt->Timeouts, &pBasic->Timeouts,
                     sizeof(SERIAL_TIMEOUTS));

       //
       // Set flowcontrol
       //

       S.Extension = pDevExt;
       S.Data = &pBasic->HandFlow;
       WdfInterruptSynchronize(pDevExt->WdfInterrupt, SerialSetHandFlow, &S);

       if (pDevExt->FifoPresent) {
          pDevExt->TxFifoAmount = pBasic->TxFifo;
          pDevExt->RxFifoTrigger = (UCHAR)pBasic->RxFifo;

          WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
          READ_RECEIVE_BUFFER(pDevExt, pDevExt->Controller);
          WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller,
                             (UCHAR)(SERIAL_FCR_ENABLE | pDevExt->RxFifoTrigger
                                     | SERIAL_FCR_RCVR_RESET
                                     | SERIAL_FCR_TXMT_RESET));
       } else {
          pDevExt->TxFifoAmount = pDevExt->RxFifoTrigger = 0;
          WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
       }


       break;
    }

    default:
       status = STATUS_INVALID_PARAMETER;
       break;

    }

    reqContext->Status = status;

    SerialCompleteRequest(Request, reqContext->Status, reqContext->Information);

    return;
}





<|start_filename|>audio/simpleaudiosample/Source/Filters/speakertoptable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    speakertoptable.h

Abstract:

    Declaration of topology tables.
--*/

#ifndef _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_
#define _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_

//=============================================================================
static
KSDATARANGE SpeakerTopoPinDataRangesBridge[] =
{
 {
   sizeof(KSDATARANGE),
   0,
   0,
   0,
   STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
   STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
   STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
 }
};

//=============================================================================
static
PKSDATARANGE SpeakerTopoPinDataRangePointersBridge[] =
{
  &SpeakerTopoPinDataRangesBridge[0]
};

//=============================================================================
static
PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] =
{
  // KSPIN_TOPO_WAVEOUT_SOURCE
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      SpeakerTopoPinDataRangePointersBridge,            // DataRanges
      KSPIN_DATAFLOW_IN,                                // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSCATEGORY_AUDIO,                                // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  },
  // KSPIN_TOPO_LINEOUT_DEST
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      SpeakerTopoPinDataRangePointersBridge,            // DataRanges
      KSPIN_DATAFLOW_OUT,                               // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSNODETYPE_SPEAKER,                              // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  }
};

//=============================================================================
static
KSJACK_DESCRIPTION SpeakerJackDescBridge =
{
    KSAUDIO_SPEAKER_STEREO,
    0xB3C98C,               // Color spec for green
    eConnTypeUnknown,
    eGeoLocFront,
    eGenLocPrimaryBox,
    ePortConnIntegratedDevice,
    TRUE
};

// Only return a KSJACK_DESCRIPTION for the physical bridge pin.
static 
PKSJACK_DESCRIPTION SpeakerJackDescriptions[] =
{
    NULL,
    &SpeakerJackDescBridge
};

//=============================================================================
static
PCPROPERTY_ITEM SpeakerPropertiesVolume[] =
{
    {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_VOLUMELEVEL,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_SpeakerTopology
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerVolume, SpeakerPropertiesVolume);

//=============================================================================
static
PCPROPERTY_ITEM SpeakerPropertiesMute[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_MUTE,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_SpeakerTopology
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerMute, SpeakerPropertiesMute);

//=============================================================================
static
PCNODE_DESCRIPTOR SpeakerTopologyNodes[] =
{
    // KSNODE_TOPO_VOLUME
    {
      0,                              // Flags
      &AutomationSpeakerVolume,     // AutomationTable
      &KSNODETYPE_VOLUME,             // Type
      &KSAUDFNAME_MASTER_VOLUME         // Name
    },
    // KSNODE_TOPO_MUTE
    {
      0,                              // Flags
      &AutomationSpeakerMute,       // AutomationTable
      &KSNODETYPE_MUTE,               // Type
      &KSAUDFNAME_MASTER_MUTE            // Name
    }
};

C_ASSERT(KSNODE_TOPO_VOLUME == 0);
C_ASSERT(KSNODE_TOPO_MUTE == 1);

static
PCCONNECTION_DESCRIPTOR SpeakerTopoMiniportConnections[] =
{
    //  FromNode,                 FromPin,                    ToNode,                 ToPin
    {   PCFILTER_NODE,            KSPIN_TOPO_WAVEOUT_SOURCE,    KSNODE_TOPO_VOLUME,     1 },
    {   KSNODE_TOPO_VOLUME,       0,                          KSNODE_TOPO_MUTE,       1 },
    {   KSNODE_TOPO_MUTE,         0,                          PCFILTER_NODE,          KSPIN_TOPO_LINEOUT_DEST }
};

//=============================================================================
static
PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] =
{
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION,
        KSPROPERTY_TYPE_GET |
        KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_SpeakerTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION2,
        KSPROPERTY_TYPE_GET |
        KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_SpeakerTopoFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerTopoFilter, PropertiesSpeakerTopoFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor =
{
  0,                                            // Version
  &AutomationSpeakerTopoFilter,                 // AutomationTable
  sizeof(PCPIN_DESCRIPTOR),                     // PinSize
  SIZEOF_ARRAY(SpeakerTopoMiniportPins),        // PinCount
  SpeakerTopoMiniportPins,                      // Pins
  sizeof(PCNODE_DESCRIPTOR),                    // NodeSize
  SIZEOF_ARRAY(SpeakerTopologyNodes),           // NodeCount
  SpeakerTopologyNodes,                         // Nodes
  SIZEOF_ARRAY(SpeakerTopoMiniportConnections), // ConnectionCount
  SpeakerTopoMiniportConnections,               // Connections
  0,                                            // CategoryCount
  NULL                                          // Categories
};

#endif // _SIMPLEAUDIOSAMPLE_SPEAKERTOPTABLE_H_


<|start_filename|>audio/sysvad/EndpointsCommon/usbhsspeakertoptable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsspeakertoptable.h

Abstract:

    Declaration of topology tables for the USB Headset speaker (external).

--*/

#ifndef _SYSVAD_USBHSSPEAKERTOPTABLE_H_
#define _SYSVAD_USBHSSPEAKERTOPTABLE_H_

#include "usbhsspeakertopo.h"

//=============================================================================
static
KSDATARANGE UsbHsSpeakerTopoPinDataRangesBridge[] =
{
 {
   sizeof(KSDATARANGE),
   0,
   0,
   0,
   STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
   STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
   STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
 }
};

//=============================================================================
static
PKSDATARANGE UsbHsSpeakerTopoPinDataRangePointersBridge[] =
{
  &UsbHsSpeakerTopoPinDataRangesBridge[0]
};

//=============================================================================
static
PCPIN_DESCRIPTOR UsbHsSpeakerTopoMiniportPins[] =
{
  // KSPIN_TOPO_WAVEOUT_SOURCE
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(UsbHsSpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      UsbHsSpeakerTopoPinDataRangePointersBridge,      // DataRanges
      KSPIN_DATAFLOW_IN,                                // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSCATEGORY_AUDIO,                                // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  },
  // KSPIN_TOPO_LINEOUT_DEST
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(UsbHsSpeakerTopoPinDataRangePointersBridge),// DataRangesCount
      UsbHsSpeakerTopoPinDataRangePointersBridge,      // DataRanges
      KSPIN_DATAFLOW_OUT,                               // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSNODETYPE_HEADSET_SPEAKERS,                     // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  }
};

//=============================================================================
static
KSJACK_DESCRIPTION UsbHsSpeakerJackDesc =
{
    KSAUDIO_SPEAKER_STEREO,
    0,                      // color
    eConnTypeOtherDigital,
    eGeoLocBottom,
    eGenLocPrimaryBox,
    ePortConnJack,
    FALSE                   // run-time code sets this value.
};

// Only return a KSJACK_DESCRIPTION for the physical bridge pin.
static 
PKSJACK_DESCRIPTION UsbHsSpeakerJackDescriptions[] =
{
    NULL,
    &UsbHsSpeakerJackDesc
};

//=============================================================================
static
PCCONNECTION_DESCRIPTOR UsbHsSpeakerTopoMiniportConnections[] =
{
  //  FromNode,             FromPin,                    ToNode,                 ToPin
  {   PCFILTER_NODE,        KSPIN_TOPO_WAVEOUT_SOURCE,  PCFILTER_NODE,          KSPIN_TOPO_LINEOUT_DEST }
};

//=============================================================================
static
PCPROPERTY_ITEM UsbHsSpeakerPropertiesTopoFilter[] =
{
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsSpeakerTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION2,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsSpeakerTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_CONTAINERID,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsSpeakerTopoFilter
    }
};

static 
PCEVENT_ITEM UsbHsSpeakerPropertiesTopoFilterEvent[] =
{
  {
    &KSEVENTSETID_PinCapsChange,                        // Something changed!
    KSEVENT_PINCAPS_JACKINFOCHANGE,                     // The only event-property defined.
    KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsSpeakerTopoFilterEvent
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationUsbHsSpeakerTopoFilterWithEvent, 
                               UsbHsSpeakerPropertiesTopoFilter,
                               UsbHsSpeakerPropertiesTopoFilterEvent);

//=============================================================================
static
PCFILTER_DESCRIPTOR UsbHsSpeakerTopoMiniportFilterDescriptor =
{
  0,                                                    // Version
  &AutomationUsbHsSpeakerTopoFilterWithEvent,           // AutomationTable
  sizeof(PCPIN_DESCRIPTOR),                             // PinSize
  SIZEOF_ARRAY(UsbHsSpeakerTopoMiniportPins),           // PinCount
  UsbHsSpeakerTopoMiniportPins,                         // Pins
  sizeof(PCNODE_DESCRIPTOR),                            // NodeSize
  0,                                                    // NodeCount
  NULL,                                                 // Nodes
  SIZEOF_ARRAY(UsbHsSpeakerTopoMiniportConnections),    // ConnectionCount
  UsbHsSpeakerTopoMiniportConnections,                  // Connections
  0,                                                    // CategoryCount
  NULL                                                  // Categories
};

#endif // _SYSVAD_USBHSSPEAKERTOPTABLE_H_
    



<|start_filename|>avstream/sampledevicemft/stdafx.h<|end_filename|>
//
//    Copyright (C) Microsoft.  All rights reserved.
//
#pragma once


#include <SDKDDKVer.h>
#include <windows.h>
#include <winnt.h>
#include <tchar.h>
#include <comdef.h>
#include <initguid.h>
#include <ks.h>
#include <ksmedia.h>
#include <Strsafe.h>
#include <wchar.h>
#include <stdio.h>
#include <assert.h>
#include <mfidl.h>
#include <wincodec.h>
#include <mfapi.h>
#include <mftransform.h>
#include <mfidl.h>
#include <mferror.h>
#include <mftransform.h>
#include <time.h>
#include <initguid.h>
#include <d3d9.h>
#include <dxva2api.h>
#include <d3d11.h>
#include <mfcaptureengine.h>
#include <algorithm>
#include <new>
#include <d3d11_4.h>
#include <vector>
#include <map>
#include <stdexcept>
using namespace std;
#include <Windows.Foundation.h>
#include <wrl\client.h>
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;

#include <wil\result.h>
#include <wil\resource.h>
#include <wil\com.h>
#include <wil\result_macros.h>

// @@@@ README Please check / uncheck various hash defines to enable/ disable features
// MF_DEVICEMFT_ASYNCPIN_NEEDED will show how to use Asynchronous queues
// MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED will show how to decode a compressed mediatype.. it supports H264 and MJPG
// MF_DEVICEMFT_SET_SPHERICAL_ATTRIBUTES will set the spherical attributes
// MF_DEVICEMFT_ENUM_HW_DECODERS will make the device MFT enumerate hardware decoders
// 
//#define MF_DEVICEMFT_ADD_GRAYSCALER_ 1  
#define MF_DEVICEMFT_ASYNCPIN_NEEDED           1
//#define MF_DEVICEMFT_DECODING_MEDIATYPE_NEEDED 0
#define MF_DEVICEMFT_SET_SPHERICAL_ATTRIBUTES  1
//#define MF_DEVICEMFT_ENUM_HW_DECODERS        1


/*Some important notes regarding Device MFT
Device MFT is an asynchronous transform that is loaded in the device source
There are a few key operations that need a special mention
1) InitializeTransform: This is where the device MFT should be initialized
2) ProcessInput is where the samples are supplied to the Device MFT
3) METransformHaveOutput is how deviceMFT notifies the Device Transform Manager (DTM) in the OS to pick up samples in its queues
4) SetOutPutStreamState is where the state transition on a Device MFT will come in
*/


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APOStressTest.h<|end_filename|>
#include <stdafx.h>

#define FIVE_MIN_IN_SEC 300

class CAPOStressTest
{
    CAPOStressTest(){};
    ~CAPOStressTest(){};

    BEGIN_TEST_CLASS(CAPOStressTest)
        START_APPVERIFIFER
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioenginebaseapop.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"System")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
        END_APPVERIFIER
    END_TEST_CLASS()

public:
    TEST_METHOD_SETUP(setUpMethod);
    TEST_METHOD_CLEANUP(tearDownMethod);

protected:
    BEGIN_TEST_METHOD(AudioAPOStressTest)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"5C35BC8F-FAE1-434B-B842-6B58353696AB")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"1EB5F7F0-CA62-411E-9006-53A23F488332")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Stress the APO - AudioAPOStressTest")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"30")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"60")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: AudioAPOStressTest")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()
};

HRESULT BasicAudioStreaming(IMMDevice *pEndpoint);
HRESULT DisableEnableEndpoint(IMMDevice *pEndpoint);
HRESULT BasicAudioCapture(IMMDevice *pEndpoint);
HRESULT BasicAudioLoopback(IMMDevice *pEndpoint);
HRESULT BasicSpatialAudio(IMMDevice *pEndpoint);
HRESULT BasicOffloadStreaming(IMMDevice *pEndpoint);
void RunStressTest(int timeInSeconds, std::vector<wil::com_ptr<IMMDevice>>& pEndpoints);

bool SetupSkipRTHeap();
bool CleanupSkipRTHeap();

<|start_filename|>storage/miniports/storahci/src/util.c<|end_filename|>
﻿/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    util.c

Abstract:

    This file contains utility functions of AHCI miniport.


Notes:

Revision History:

--*/

#if _MSC_VER >= 1200
#pragma warning(push)
#endif

#pragma warning(disable:4054)
#pragma warning(disable:4055)
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union

#include "generic.h"

PAHCI_MEMORY_REGISTERS
GetABARAddress(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension,
    _In_ PPORT_CONFIGURATION_INFORMATION ConfigInfo
    )
/*++
    Search a set of resources (I/O and memory) for an entry corresponding to an AHCI ABAR address.

It assumes:
    AdapterExtension->AhciBaseAddress has been set
Called by:
    AhciHwFindAdapter
It performs:
    1. If any resources are defined, loop over all resource entries, looking for the AHCI ABAR address. If found, return it.
    2. If this point is reached, ABAR was not found. Return NULL.
Affected Variables/Registers:
    none
ReturnValue:
    Address of AHCI controller memory registers if found, NULL otherwise

--*/
{
    PAHCI_MEMORY_REGISTERS  abar;
    ULONG i;

    abar = NULL;

    // loop over resource entries
    if (ConfigInfo->NumberOfAccessRanges > 0) {
        for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) {
            BOOLEAN bAccessRangeFound = FALSE;

            // Test to see if this is the needed resource entry, depending on the interface type
            if (ConfigInfo->AdapterInterfaceType == PCIBus) {
                if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart == AdapterExtension->AhciBaseAddress) {
                    bAccessRangeFound = TRUE;
                }
            } else {
                if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart != 0) {
                    bAccessRangeFound = TRUE;
                }
            }

            if (bAccessRangeFound) {
                abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(AdapterExtension,
                                                                     ConfigInfo->AdapterInterfaceType,
                                                                     ConfigInfo->SystemIoBusNumber,
                                                                     (*(ConfigInfo->AccessRanges))[i].RangeStart,
                                                                     (*(ConfigInfo->AccessRanges))[i].RangeLength,
                                                                     (BOOLEAN)!(*(ConfigInfo->AccessRanges))[i].RangeInMemory);
                break;
            }
        }
    }

    return abar;
}


VOID
RecordExecutionHistory(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    ULONG Function
  )
/*++
    Takes a snapshot of the AHCI registers for debugging
It assumes:
    nothing
Called by:
    Everything

It performs:
    (overview)
    1 Select the next available index
    2 Take the snapshot
    (details)
    1.1 Select the next available index
    2.1 Copy over the Channel Extension header
    2.2 Copy over the AHCI registers
Affected Variables/Registers:
    none
Return Value:
    none
--*/
{
    if (ChannelExtension->ExecutionHistory == NULL) {
        return;
    }

    //1.1 Select the next available index
    // Actually, ExecutionHistoryNextAvailableIndex is used for current index.
    if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) {
        ChannelExtension->ExecutionHistoryNextAvailableIndex = 0;
    } else {
        ChannelExtension->ExecutionHistoryNextAvailableIndex++;
    }

    StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp));

    //2.1 Copy over the Channel Extension header
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function;

    if (ChannelExtension->AdapterExtension->IS) {
        // Keep using the old field "IS" to save StateFlags information.
        StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS, &ChannelExtension->StateFlags, sizeof(ULONG));
    } else {
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS = (ULONG)~0;
    }

    StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].SlotManager, &ChannelExtension->SlotManager, sizeof(SLOT_MANAGER));

    //2.2 Copy over the AHCI registers
    if (ChannelExtension->Px) {
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[0] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[1] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[2] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[3] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU);

        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[4] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[5] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[6] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[7] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->DW7_Reserved);

        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[8] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[9] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[10] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[11] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong);

        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[12] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[13] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[14] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI);
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[15] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SNTF.AsUlong);

    } else {
        for(int i = 0; i < 0x10; i++) {
            ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[i] = (ULONG)~0;
        }
    }
}

VOID
RecordInterruptHistory(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    ULONG PxIS,
    ULONG PxSSTS,
    ULONG PxSERR,
    ULONG PxCI,
    ULONG PxSACT,
    ULONG Function
  )
/*++
    Takes a snapshot of the Interrupt for issue analysis.
It assumes:
    nothing
Called by:
    Everything

Affected Variables/Registers:
    none
Return Value:
    none
--*/
{
    if (ChannelExtension->ExecutionHistory == NULL) {
        return;
    }

    //1. Select the next available index
    // Actually, ExecutionHistoryNextAvailableIndex is used for current index.
    if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) {
        ChannelExtension->ExecutionHistoryNextAvailableIndex = 0;
    }else{
        ChannelExtension->ExecutionHistoryNextAvailableIndex++;
    }

    StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp));

    //2. Copy data
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function;

    if (ChannelExtension->AdapterExtension->IS) {
        // Keep using the old field "IS" to save StateFlags information.
        StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS, &ChannelExtension->StateFlags, sizeof(ULONG));
    } else {
        ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS = (ULONG)~0;
    }

    StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].SlotManager, &ChannelExtension->SlotManager, sizeof(SLOT_MANAGER));

    AhciZeroMemory((PCHAR)ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px, sizeof(ULONG)*16);
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[4] = PxIS;
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[10] = PxSSTS;
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[12] = PxSERR;
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[13] = PxSACT;
    ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[14] = PxCI;
}

VOID
Set_PxIE(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PAHCI_INTERRUPT_ENABLE IE
    )
/*++
    This is just a space saver function that makes the interrupt configuration more readable

Called by:

--*/
{
    AHCI_INTERRUPT_ENABLE ie;
    AHCI_COMMAND cmd;
    PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension;

    ie.AsUlong = StorPortReadRegisterUlong(adapterExtension, &IE->AsUlong);
    ie.DHRE = 1; //Device to Host Register FIS Interrupt (DHRS):  A D2H Register FIS has been received with the �I� bit set, and has been copied into system memory.
    ie.PSE  = 1; //PIO Setup FIS Interrupt (PSS):  A PIO Setup FIS has been received with the �I� bit set, it has been copied into system memory, and the data related to that FIS has been transferred.  This bit shall be set even if the data transfer resulted in an error.
    ie.DSE  = 1; //DMA Setup FIS Interrupt (DSS):  A DMA Setup FIS has been received with the �I� bit set and has been copied into system memory.
    ie.SDBE = 1; //Set Device Bits Interrupt (SDBS):  A Set Device Bits FIS has been received with the �I� bit set and has been copied into system memory.

    ie.UFE  = 0; //Unknown FIS Interrupt (UFS): When set to �1�, indicates that an unknown FIS was received and has been copied into system memory.  This bit is cleared to �0� by software clearing the PxSERR.DIAG.F bit to �0�.  Note that this bit does not directly reflect the PxSERR.DIAG.F bit.  PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory.  Software should wait to act on an unknown FIS until this bit is set to �1� or the two bits may become out of sync.
    ie.DPE  = 0; //Descriptor Processed (DPS):  A PRD with the �I� bit set has transferred all of its data.  Refer to section 5.4.2.
    ie.PCE  = 1; //Port Connect Change Status (PCS): 1=Change in Current Connect Status. 0=No change in Current Connect Status.  This bit reflects the state of PxSERR.DIAG.X.  This bit is only cleared when PxSERR.DIAG.X is cleared.
    if(adapterExtension->CAP.SMPS) {
        ie.DMPE  = 1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device.  This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to �1�.
    } else {
        ie.DMPE  = 0;
    }

    //Reserved :14;
    ie.PRCE = 1; //PhyRdy Change Status (PRCS): When set to �1� indicates the internal PhyRdy signal changed state.  This bit reflects the state of P0SERR.DIAG.N.  To clear this bit, software must clear P0SERR.DIAG.N to �0�.
    ie.IPME = 0; //Incorrect Port Multiplier Status (IPMS):  Indicates that the HBA received a FIS from a device whose Port Multiplier field did not match what was expected.  The IPMS bit may be set during enumeration of devices on a Port Multiplier due to the normal Port Multiplier enumeration process.  It is recommended that IPMS only be used after enumeration is complete on the Port Multiplier.

    ie.OFE  = 1; //Overflow Status (OFS):  Indicates that the HBA received more bytes from a device than was specified in the PRD table for the command.
    //Reserved2 :1;
    ie.INFE = 1; //Interface Non-fatal Error Status (INFS):  Indicates that the HBA encountered an error on the Serial ATA interface but was able to continue operation.  Refer to section 6.1.2.
    ie.IFE  = 1; //Interface Fatal Error Status (IFS):  Indicates that the HBA encountered an error on the Serial ATA interface which caused the transfer to stop.  Refer to section 6.1.2.

    ie.HBDE = 1; //Host Bus Data Error Status (HBDS):  Indicates that the HBA encountered a data error (uncorrectable ECC / parity) when reading from or writing to system memory.
    ie.HBFE = 1; //Host Bus Fatal Error Status (HBFS):  Indicates that the HBA encountered a host bus error that it cannot recover from, such as a bad software pointer.  In PCI, such an indication would be a target or master abort.
    ie.TFEE = 1; //Task File Error Status (TFES):  This bit is set whenever the status register is updated by the device and the error bit (bit 0) is set.
    cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong);
    if(cmd.CPD) {    //check for PxCMD.CPD set to �1� before setting CPDE
        ie.CPDE = 1; //Cold Port Detect Status (CPDS): When set, a device status has changed as detected by the cold presence detect logic.  This bit can either be set due to a non-connected port receiving a device, or a connected port having its device removed.  This bit is only valid if the port supports cold presence detect as indicated by PxCMD.CPD set to �1�.
    } else {
        ie.CPDE = 0;
    }
    StorPortWriteRegisterUlong(adapterExtension, &IE->AsUlong, ie.AsUlong);
}


ULONG
GetStringLength (
    _In_ PSTR   String,
    _In_ ULONG  MaxLength
    )
/*
    This function returns the length of string. (not include the "NULL" terminator)
*/
{
    ULONG i;

    for (i = 0; i < MaxLength; i++) {
        if (String[i] == '\0') {
            break;
        }
    }

    return min(i, MaxLength);
}

VOID
WMultiStringToAscii(
    _Inout_ PZZSTR Strings,
    _In_  ULONG StringBufferLength
    )
/*+++

    This routine is used to convert the Source buffer in Wide Multi_SZ into ASCII.
    Note that StringBufferLength should contain the double '0' as the ending of Multi_SZ
         This routine only works when the input string is WCHARs.

--*/
{
    PWCHAR  source = (PWCHAR)Strings;
    PUCHAR  destination = (PUCHAR)Strings;
    ULONG   charCount;

    // the Strings buffer should at least with length of 2 wchars.
    if ( (Strings != NULL) && (StringBufferLength >= 4) ) {

        // Run through the Source buffer and convert the WCHAR to ASCII, placing
        // the converted value in Destination. Ensure that the destination buffer isn't overflowed.
        for (charCount = 0; (charCount < (StringBufferLength / 2 - 1)) && ((*source != '\0') || (*(source + 1) != '\0')); charCount++) {
            *destination = (UCHAR)*source;
            destination++;
            source++;
        }

        // zero the reset of buffer. 
        // "StringBufferLehgth" is length in bytes; "charCount" is count of chars from converted ACSII string.
        if (StringBufferLength > charCount) {
            AhciZeroMemory((PCHAR)destination, (StringBufferLength - charCount));
        }
    }

    return;
}

_Success_(return != FALSE)
BOOLEAN
StringToULONG (
    _In_ PSTR InputString,
    _Out_ PULONG Value
    )
/*
    This function converts string into an ULONG value
    The string should consist of characters of '0' ~ '9' and a '\0' as ending.

Return Value:
    TRUE - when the whole string is converted and the value is in range 0 ~ 0xFFFFFFFF
    FALSE - otherwise
*/
{
    BOOLEAN result = TRUE;
    PSTR    tempString = InputString;
    ULONG   tempValue = 0;

    if ( (InputString == NULL) || (Value == NULL) ) {
        return FALSE;
    }

    while (*tempString != '\0') {
        if ( (*tempString >= '0') && (*tempString <= '9') ) {
            tempValue = tempValue * 10 + (*tempString++ - '0');
        } else {
            result = FALSE;
            break;
        }

        if ( (*tempString != '\0') && (MAXULONG / 10 < tempValue) ) {
            // the value will overflow, bail out
            result = FALSE;
            break;
        }
    }

    if (result == TRUE) {
        *Value = tempValue;
    }

    return result;
}

_Success_(return != FALSE)
BOOLEAN
CompareId (
    _In_opt_ PSTR DeviceId,
    _In_ ULONG  DeviceIdLength,
    _In_opt_ PZZSTR TargetId,
    _In_ ULONG  TargetIdLength,
    _Inout_opt_ PULONG Value
)
/*++
Character comparison between DeviceId and TargetId to the min(DeviceIdLength, TargetIdLength)

Note that TargetId is a multi_sz string. DeviceId will be used to compare with each of strings in TargetId.

When string matches and 'Value' is asked, put value of remaining TargetId into "Value" field

DeviceId -- source string for comparison, in format of ASCII string.

TargetId -- target string for comparison, in format of multi_sz string.
    TargetId format: xxxxxxxx nnn (first part is the ID to compare, second part is the value)

    Special character in TargetId:
        '?' - matches any character in DeviceId
        '*' - indicating end of TargetId, matches any characters left in DeviceId. It should be used only at the end of string.

Return Value:
    TRUE - On a match or contain
    FALSE - On a mismatch
--*/
{
    ULONG   compareLength;
    ULONG   i;
    PUCHAR  localDeviceId;
    PSTR    localStrings = TargetId;
    PUCHAR  localTargetId = (PUCHAR)localStrings;
    BOOLEAN matches = FALSE;

    if ((DeviceId == NULL) || (TargetId == NULL)) {
        // nothing to compare, return TRUE
        return TRUE;
    }

    while ( (*localStrings != '\0') || (*(localStrings + 1) != '\0') ) {
        localDeviceId = (PUCHAR)DeviceId;
        compareLength = min(DeviceIdLength, GetStringLength(localStrings, TargetIdLength));
        if (compareLength == 0) {
            return FALSE;
        }

        matches = TRUE;
        for(i = 0; i < compareLength; i++) {
            if (*localTargetId == '*') {
                localTargetId++;
                break;  // whole string match
            } else if ( (*localDeviceId == '\0') || (*localTargetId == '\0') ) {
                matches = FALSE;
                break;
            } else if ( (*localDeviceId == *localTargetId) ||
                        (*localTargetId == '?') ) {
                localDeviceId++;
                localTargetId++;
            } else {
                matches = FALSE;
                break;
            }
        }

        if (matches) {
            break;
        }

        localStrings+= GetStringLength(localStrings, TargetIdLength) + 1;
        localTargetId = (PUCHAR)localStrings;
    }

    if ( (Value != NULL) &&
         (matches == TRUE) &&
         (*localTargetId == ' ') ) {

         //convert the left part of localTargetId into a value
         localTargetId++;
         if (!StringToULONG((PSTR)localTargetId, Value)) {
             matches = FALSE;
         }
    }

    return matches;
}

VOID
AhciBusChangeCallback(
    _In_ PVOID AdapterExtension,
    _In_opt_ PVOID Context,
    _In_ SHORT AddressType,
    _In_ PVOID Address,
    _In_ ULONG Status
    )
{
    PAHCI_ADAPTER_EXTENSION adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
    PSTOR_ADDR_BTL8         storAddrBtl8 = (PSTOR_ADDR_BTL8)Address;

    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(AddressType);

    NT_ASSERT(AddressType == STOR_ADDRESS_TYPE_BTL8);

    if (Status == STOR_STATUS_SUCCESS) {
        // targeted rescan completed successfully
        return;
    }

    // Previous targeted rescan failed, retry it.
    if (IsPortValid(adapterExtension, storAddrBtl8->Path)) {

        StorPortStateChangeDetected(AdapterExtension,
                                    STATE_CHANGE_TARGET,
                                    (PSTOR_ADDRESS)&adapterExtension->PortExtension[storAddrBtl8->Path]->DeviceExtension[0].DeviceAddress,
                                    0,
                                    AhciBusChangeCallback,
                                    NULL);
    }

    return;
}

VOID
PortBusChangeProcess (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    )
{
    AHCI_SERIAL_ATA_CONTROL sctl;
    STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 };
    ULONG status = STOR_STATUS_UNSUCCESSFUL;

    // 1 if link speed was limited, restore the supported value.
    sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong);
    if ((sctl.SPD != 0) &&
        (sctl.SPD != ChannelExtension->AdapterExtension->CAP.ISS)) {

        sctl.SPD = 0;   //AHCI 3.3.11 --- 0h  No speed negotiation restrictions
        StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong);
    }

    // 2 Kicks off the Start Channel state machine
    AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
    AhciPortReset(ChannelExtension, TRUE);    // all requests should be completed
    AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);

    // Mark that cached Log Page information need to be updated.
    ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = TRUE;

    // 3 Requests rescan on this port. Using STATE_CHANGE_TARGET to make sure a REPORT LUNS command coming from Storport.
    status = StorPortStateChangeDetected(ChannelExtension->AdapterExtension,
                                         STATE_CHANGE_TARGET,
                                         (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress,
                                         0,
                                         AhciBusChangeCallback,
                                         NULL);

    // If the state change notification fails, set a timer to retry the notification after a delay
    if (status == STOR_STATUS_UNSUCCESSFUL) {

        if (ChannelExtension->BusChangeTimer == NULL) {
            StorPortInitializeTimer(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeTimer);
        }

        status = StorPortRequestTimer(ChannelExtension->AdapterExtension, 
                                     ChannelExtension->BusChangeTimer, 
                                     AhciBusChangeTimerCallback,
                                     ChannelExtension, 
                                     100000, // 100 milliseconds
                                     0);

        if (status != STOR_STATUS_SUCCESS) {
            PortReleaseActiveReference(ChannelExtension, NULL);
        }
    } else {

        if (ChannelExtension->BusChangeTimer != NULL) {

            StorPortFreeTimer(ChannelExtension->AdapterExtension, ChannelExtension->BusChangeTimer);
            ChannelExtension->BusChangeTimer = NULL;
        }

        PortReleaseActiveReference(ChannelExtension, NULL);
    }
}

VOID
AhciPortBusChangeDpcRoutine(
    _In_ PSTOR_DPC  Dpc,
    _In_ PVOID      AdapterExtension,
    _In_opt_ PVOID  SystemArgument1,
    _In_opt_ PVOID  SystemArgument2
    )
/*++
    Asks port driver to request a QDR on behalf of the miniport
It assumes:
    nothing
Called by:
    Indirectly by AhciHwInterrupt

It performs:
    1 Kicks off the Start Channel state machine
    2 Requests QDR
Affected Variables/Registers:
    none
--*/
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)SystemArgument1;
    ULONG                   busChangeInProcess;
    BOOLEAN                 portIdle = FALSE;

    UNREFERENCED_PARAMETER(Dpc);
    UNREFERENCED_PARAMETER(AdapterExtension);
    UNREFERENCED_PARAMETER(SystemArgument2);

    if (channelExtension == NULL) {
        NT_ASSERT(channelExtension != NULL);
        return;
    }

    busChangeInProcess = InterlockedBitTestAndSet((LONG*)&channelExtension->PoFxPendingWork, 1);  //BusChange is at bit 1

    if (busChangeInProcess == 1) {
        // bus change is pending in another process.
        return;
    }

    RecordExecutionHistory(channelExtension, 0x00000030);//AhciPortBusChangeDpcRoutine

    PortAcquireActiveReference(channelExtension, NULL, &portIdle);

    // if port is in Active state, continue to process bus change notification.
    // otherwise, it will be processed when port gets into Active state
    if (!portIdle) {
        ULONG   busChangePending;
        busChangePending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 1);  //BusChange is at bit 1

        if (busChangePending == 1) {
            PortBusChangeProcess(channelExtension);
        }
    }

    return;
}

VOID
AhciBusChangeTimerCallback(
    _In_ PVOID AdapterExtension,
    _In_opt_ PVOID ChannelExtension
    )
{
    PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)ChannelExtension;

    if ( (channelExtension == NULL) || (channelExtension->Px == NULL) ) {

        // The port has been stopped. 
        //
        // Note: 
        // Px is set to NULL in AhciPortStop function. StartIo spin lock is utilized to
        // prevent race condition with AhciPortStop function. StartIo spin lock is acquired 
        // before AhciPortStop is called. When we are here in AhciBusChangeTimerCallback, because
        // it is a timer callback function, StartIo spin lock is already held - Storport holds
        // StartIo spin lock before invoking miniport timer callback function.
        //       

        return;
    }

    AhciPortBusChangeDpcRoutine(&channelExtension->BusChangeDpc, AdapterExtension, channelExtension, NULL);
}

VOID
ReleaseSlottedCommand(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR SlotNumber,
    _In_ BOOLEAN AtDIRQL
    )
/*++
    Performs Completion Data marshaling back into the SRB
It assumes:
    SRB STATUS has already been filled out
    ATA status from the AHCI registers is valid for this command
    SlotNumber points to a fully filled out Slot entry
Called by:
    AhciHwStartIo
    AhciNonQueuedErrorRecovery
    AhciCompleteIssuedSRBs

It performs:
    (overview)
    1 Initialize
    2 Marshal completion data
    3 Complete the Command
    (details)
    1.1 Keep track of the completion for our debugging records
    2.1 Handle Request Sense marshaling
    2.2.1 Handle everything else's marshaling
    2.2.2 If this was marked as needing return data, fill in the return Task File
    3.1 Make the slot available again
    3.2 Perform the Slot's last request, if there was one
    3.3.1 IO is completing, that IO may have paused the queue so unpause the queue
    3.3.2  Complete the command
Affected Variables/Registers:
    SRB
--*/
{
    PSLOT_CONTENT           slotContent;
    PAHCI_SRB_EXTENSION     srbExtension;
    PSTORAGE_REQUEST_BLOCK  srbToRelease;
    BOOLEAN                 isSenseSrb;
    BOOLEAN                 retrySrb = FALSE;
    BOOLEAN                 isReadLogExtSrbIssuedInNCQErrorRecovery = FALSE;

    slotContent = &ChannelExtension->Slot[SlotNumber];
    if (slotContent->Srb == NULL) {
        return;
    }

    srbExtension = GetSrbExtension(slotContent->Srb);
    isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction);

    if ((slotContent->Srb == (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb) && 
        (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 1)) {

        isReadLogExtSrbIssuedInNCQErrorRecovery = TRUE;
    }

    if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
        RecordExecutionHistory(ChannelExtension, 0x10000053);//ReleaseSlottedCommand
    }

  //2/2 Log command completion part for our debugging records
    if (LogCommand(ChannelExtension->AdapterExtension->LogFlags)) {
        PCOMMAND_HISTORY         cmdHistory;
        slotContent->CommandHistoryIndex %= 64; // should not be 64 or bigger. do it anyway to make sure we are safe

        cmdHistory = &ChannelExtension->CommandHistory[slotContent->CommandHistoryIndex];
        StorPortCopyMemory((PVOID)&cmdHistory->CompletionFIS, (PVOID)&ChannelExtension->ReceivedFIS->D2hRegisterFis, sizeof(AHCI_D2H_REGISTER_FIS));

        cmdHistory->CompletionPx[0] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong);
        cmdHistory->CompletionPx[1] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU);
        cmdHistory->CompletionPx[2] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong);
        cmdHistory->CompletionPx[3] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU);

        cmdHistory->CompletionPx[4] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong);
        cmdHistory->CompletionPx[5] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong);
        cmdHistory->CompletionPx[6] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong);
        cmdHistory->CompletionPx[7] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->DW7_Reserved);

        cmdHistory->CompletionPx[8] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong);
        cmdHistory->CompletionPx[9] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong);
        cmdHistory->CompletionPx[10] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong);
        cmdHistory->CompletionPx[11] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong);

        cmdHistory->CompletionPx[12] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong);
        cmdHistory->CompletionPx[13] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT);
        cmdHistory->CompletionPx[14] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI);
        cmdHistory->CompletionPx[15] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SNTF.AsUlong);

        cmdHistory->SrbStatus = slotContent->Srb->SrbStatus;

    }

  //2. Then complete the command
    if (isSenseSrb) {
      //2.1 Handle Request Sense marshaling
        srbToRelease = (PSTORAGE_REQUEST_BLOCK)SrbGetOriginalRequest(slotContent->Srb);

        //that original SRB must have SRB_STATUS_AUTOSENSE_VALID set appropriately
        if (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) {
            srbToRelease->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            SrbSetScsiStatus(srbToRelease, SCSISTAT_CHECK_CONDITION);
        } else {
            srbToRelease->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID;
        }

        //finish use of Sense.Srb
        srbExtension->AtaFunction = 0;
    } else {
      //2.2.1 Handle everything else's marshaling
        srbToRelease = slotContent->Srb;

        if ((srbExtension->Flags & ATA_FLAGS_SENSEDATA_SET) == 0) {
           //Record error and status
            srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar;
            srbExtension->AtaError = ChannelExtension->TaskFileData.ERR;

          //2.2.2 If this was marked as needing return data, fill in the return Task File.
            if( IsReturnResults(srbExtension->Flags) ) {
                SetReturnRegisterValues(ChannelExtension, srbToRelease, NULL);
            }

            // interpret ATA error to be SCSI error and fill SenseBuffer
            // also log IO Record
            if (IsAtaCommand(srbExtension->AtaFunction) ||
                IsAtaCfisPayload(srbExtension->AtaFunction)) {
                // IO Record for ATA device is logged in AtaMapError()
                AtaMapError(ChannelExtension, srbToRelease, slotContent->StateFlags.FUA);
            } else {
                if (SRB_STATUS(srbToRelease->SrbStatus) == SRB_STATUS_SUCCESS) {
                    ChannelExtension->DeviceExtension[0].IoRecord.SuccessCount++;
                } else {
                    ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++;
                }
            }
        }
    }

  //3.1 Make the slot available again
    slotContent->CmdHeader = NULL;
    slotContent->CommandHistoryIndex = 0;
    slotContent->Srb = NULL;
    slotContent->StateFlags.FUA = FALSE;

    //Clear the CommandsToComplete bit
    ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << SlotNumber);
    ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << SlotNumber);

  //3.3.1 IO is completing, that IO may have paused the queue so unpause the queue
  //      During NCQ Error Recovery process, this flag will be reset in the error recovery completion process, either through reset or through slot release again.
    if (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 0) {
        ChannelExtension->StateFlags.QueuePaused = FALSE;
    }

  //3.3.2 Complete the command
    if ( (srbToRelease->SrbStatus != SRB_STATUS_SUCCESS) && !isSenseSrb && (srbExtension->RetryCount == 0) ) {
        if ( (ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile == 1) && 
             IsNCQWriteCommand(srbExtension) ) {
            // NCQ WRITE with Hybrid Information failed, retry once without Hybrid Information.
            ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 0;
            retrySrb = TRUE;
        } else if (srbToRelease->SrbStatus == SRB_STATUS_PARITY_ERROR) {
            // if a command encounters CRC error, retry once
            retrySrb = TRUE;
        }
    }

    //
    // There is a bad timing will break the error recovery and cause IO hang eventually, typically happen in SSD but not limited to.
    // 1. NCQ error happen first, which will issue the ReadLogExtCommand;
    // 2. Then Nonqueued error happen before the ReadLogExtCommand complete.
    //    In ReleaseSlottedCommand, ReadLogExtCommand may got chance to be retried, which will result in SingleIoSlice set to 1, 
    //    and SingleIoSlice will skip the PreservedSettingCommand(the ReservedSlotInUse is already set).
    // 3. Then ReservedSlotInUse never got chance to be cleared because it relies on PreservedSettingCommand execution.
    //    ReservedSlotInUse will block the queue activation, finally the IO will hang and AhciPortReset will be repeatedly issued by upper layer.
    //   
    // Do not retry ReadLogExtCommand when NCQ error recovery in process, which makes sense since ReadLogExtCommand is sense-like command as well.
    //
    if (retrySrb && isReadLogExtSrbIssuedInNCQErrorRecovery) {
        retrySrb = FALSE;
    }

    if (retrySrb) {
        srbToRelease->SrbStatus = SRB_STATUS_PENDING;
        srbExtension->RetryCount++;
        AhciProcessIo(ChannelExtension, srbToRelease, AtDIRQL);
    } else {
        // otherwise, complete it.
        AhciCompleteRequest(ChannelExtension, srbToRelease, AtDIRQL);
    }
}

VOID
AhciCompleteJustSlottedRequest(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    )
/*
    This routine is to complete a request that occupies a slot but has not been issued to adapter yet.
    Srb status should be set before calling this routine.
*/
{
    PSLOT_CONTENT           slotContent;
    PAHCI_SRB_EXTENSION     srbExtension;
    BOOLEAN                 isSenseSrb;
    PSTORAGE_REQUEST_BLOCK  srbToComplete;
    STOR_LOCK_HANDLE        lockHandle = {InterruptLock, 0};

    srbExtension = GetSrbExtension(Srb);
    slotContent = &ChannelExtension->Slot[srbExtension->QueueTag];

    NT_ASSERT(slotContent->Srb == Srb);

    isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction);

    if (isSenseSrb) {
      // Handle Request Sense marshaling
        srbToComplete = (PSTORAGE_REQUEST_BLOCK)SrbGetOriginalRequest(Srb);

        //Sense Srb doesn't have chance to run yet, clear Sense Valid flag from original SRB.
        srbToComplete->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID;

        //finish use of Sense.Srb
        srbExtension->AtaFunction = 0;
    } else {
        srbToComplete = Srb;
    }

    if (!AtDIRQL) {
        AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockHandle);
    }

  //1. Make the slot available again
    slotContent->CmdHeader = NULL;
    slotContent->CommandHistoryIndex = 0;
    slotContent->Srb = NULL;
    slotContent->StateFlags.FUA = FALSE;

  //2. this function can be called from places that don't call ActivateQueue yet. Clear the bit in IO Slices
    if ((ChannelExtension->SlotManager.HighPriorityAttribute & (1 << srbExtension->QueueTag)) != 0) {
        ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << srbExtension->QueueTag);
    }
    if ((ChannelExtension->SlotManager.NCQueueSlice & (1 << srbExtension->QueueTag)) != 0) {
        ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << srbExtension->QueueTag);
    }
    if ((ChannelExtension->SlotManager.NormalQueueSlice & (1 << srbExtension->QueueTag)) != 0) {
        ChannelExtension->SlotManager.NormalQueueSlice &= ~(1 << srbExtension->QueueTag);
    }
    if ((ChannelExtension->SlotManager.SingleIoSlice & (1 << srbExtension->QueueTag)) != 0) {
        ChannelExtension->SlotManager.SingleIoSlice &= ~(1 << srbExtension->QueueTag);
    }
    if ((ChannelExtension->SlotManager.CommandsToComplete & (1 << srbExtension->QueueTag)) != 0) {
        ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << srbExtension->QueueTag);
    }

    if (!AtDIRQL) {
        AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockHandle);
    }

  //3. Complete the command
    AhciCompleteRequest(ChannelExtension, srbToComplete, AtDIRQL);

    return;
}

VOID
AhciCompleteRequest(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    )
/*++
    Wrapper for ComleteRequest to protect against completing the local SRB back to port driver who doesn't know anything about local SRB
It assumes:
    SRB is completely ready to be completed back to the SRB generator
It performs:
    1. If Srb has completion routine, put it in completion queue.
    2. Complete the command back to the owner. Do not complete the Local Srb.
Called by:
    AhciHwStartIo
    RequiresControllerResources
    ReleaseSlottedCommand

NOTE:
    The caller of this routine should call ActiveQueue to program the command (from Srb completion routine) to adapter

--*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);

  //1. If Srb acquired active reference or has completion routine, put it in completion queue. Otherwise, complete it.
    if ( ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) ||
         (srbExtension->CompletionRoutine != NULL) ) {

        if (AtDIRQL == FALSE) {
            STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0};

            AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
            AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90);
            AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
        } else {
            AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90);
        }

        if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
            RecordExecutionHistory(ChannelExtension, 0x30000045); //Exit AhciCompleteRequest, SRB is put in CompletionQueue
        }

        if (!IsDumpMode(ChannelExtension->AdapterExtension)) {
            StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->CompletionDpc, ChannelExtension, Srb);
        } else {
            AhciPortSrbCompletionDpcRoutine(&ChannelExtension->CompletionDpc, ChannelExtension->AdapterExtension, ChannelExtension, Srb);
        }

    } else {
      //2. Complete the command back to the owner
        if (IsMiniportInternalSrb(ChannelExtension, Srb)) {
            NT_ASSERT(((PSCSI_REQUEST_BLOCK)Srb == &ChannelExtension->Sense.Srb) || (ChannelExtension->StateFlags.ReservedSlotInUse == 0));
            RecordExecutionHistory(ChannelExtension, 0x20000045); //Exit AhciCompleteRequest,  Local SRB
            return;
        } else {
            NT_ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING);
            StorPortNotification(RequestComplete, ChannelExtension->AdapterExtension, Srb);

            if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
                RecordExecutionHistory(ChannelExtension, 0x10000045); //Exit AhciCompleteRequest, SRB from port driver
            }
        }
    }

    return;
}

VOID
GetAvailableSlot(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PSTORAGE_REQUEST_BLOCK  Srb
    )
/*++
    Returns the next available slot that can be used for an ATA command in the QueueTag field of the SRB
It assumes:

Called by:

It performs:
    1.1 Initialize variables
    2.1 Special case the slot for the local SRB
    2.2 Chose the slot circularly starting with CurrentCommandSlot
    3.1 Update CurrentCommandSlot

Affected Variables/Registers:

Return Value:
    Next available slot number.  If SRB is localSRB then slot is 0.
    If no slot is available tag returned is 0xFF.
--*/
{
    ULONG               allocated;
    UCHAR               limit;
    UCHAR               i;
    PAHCI_SRB_EXTENSION srbExtension;

    srbExtension = GetSrbExtension(Srb);

  //1.1 Initialize variables
    srbExtension->QueueTag = 0xFF;
    limit = ChannelExtension->CurrentCommandSlot;

    allocated = GetOccupiedSlots(ChannelExtension);

  //2.1 Use slot 0 for internal commands, don't increment CCS
    if (Srb == (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb ) {
        if ((allocated & (1 << 0)) > 0) {
            srbExtension->QueueTag = 0xFF;
        } else {
            srbExtension->QueueTag = 0;
        }
        return;
    }

    // case of Sense.Srb used for NCQ error recovery.
    if ((Srb == (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb) &&
        IsAtaCommand(srbExtension->AtaFunction)) {

        if ((allocated & (1 << 0)) == 0) {
            srbExtension->QueueTag = 0;
            return;
        }
    }

  //2.2 Chose the slot circularly starting with CCS
    for (i = limit; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) {
        if ( (allocated & (1 << i)) == 0 ) {
            srbExtension->QueueTag = i;
            goto getout;
        }
    }

    for (i = 1; i < limit; i++) {
        if ( (allocated & (1 << i)) == 0 ) {
            srbExtension->QueueTag = i;
            goto getout;
        }
    }

getout:
  //3.1 Update CurrentCommandSlot
    if (IsRequestSenseSrb(srbExtension->AtaFunction)) {
      //If this SRB is for Request Sense, make sure it is given the next chance to run during ActivateQueue by not incrementing CCS.
        return;
    }

    ChannelExtension->CurrentCommandSlot++;
    if (ChannelExtension->CurrentCommandSlot == (ChannelExtension->AdapterExtension->CAP.NCS + 1)) {
        ChannelExtension->CurrentCommandSlot = 1;
    }

    return;
}


BOOLEAN
UpdateSetFeatureCommands(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR OldFeatures,
    _In_ UCHAR NewFeatures,
    _In_ UCHAR OldSectorCount,
    _In_ UCHAR NewSectorCount
  )
/*++
    Edit a PersistentSetting command
It assumes:
    0xFF means invalid feature
Called by:
    RequiresControllerResources

It performs:
    1.1 Find the location of the given command
    2.1 Replace it with the new command
Affected Variables/Registers:
    PersistentSettings
Return Value:
    TRUE if the edit succeeded
    FALSE if the command did not already exist
--*/
{
    BOOLEAN done = FALSE;
    UCHAR   i = 0;

    for (i = 0; i < MAX_SETTINGS_PRESERVED; i++) {
      //
        if ( (ChannelExtension->PersistentSettings.CommandParams[i].Features == OldFeatures) &&
             (ChannelExtension->PersistentSettings.CommandParams[i].SectorCount == OldSectorCount) ) {
            //Replace old one it with the new command
            ChannelExtension->PersistentSettings.CommandParams[i].Features = NewFeatures;
            ChannelExtension->PersistentSettings.CommandParams[i].SectorCount = NewSectorCount;
            done = TRUE;
            break;
        } else if ( (ChannelExtension->PersistentSettings.CommandParams[i].Features == NewFeatures) &&
                    (ChannelExtension->PersistentSettings.CommandParams[i].SectorCount == NewSectorCount) ) {
            //the new command is already in array
            done = TRUE;
            break;
        }
    }

    if (!done) {
        //try to find an empty slot
        ULONG slotsAvailable = ChannelExtension->PersistentSettings.Slots;
        i = 0;
        while (slotsAvailable) {
            slotsAvailable >>= 1;
            i++;
        }
        //Add the new command into the empty slot
        if (i < MAX_SETTINGS_PRESERVED) {
            ChannelExtension->PersistentSettings.CommandParams[i].Features = NewFeatures;
            ChannelExtension->PersistentSettings.CommandParams[i].SectorCount = NewSectorCount;
            //mark the Persistent settings slot as programmed
            ChannelExtension->PersistentSettings.Slots |= (1 << i);

            done = TRUE;
        } else {
            NT_ASSERT(FALSE);  //we don't expect so much commands for persistent settings.
        }
    }

    return done;
}

VOID
RestorePreservedSettings(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
  )
/*++
    Send each of the commands in the PersistentSettings structure one at a time using the local SRB
It assumes:
    The local SRB is not currently in use
Called by:
    AhciPortIssueInitCommands
    AhciPortReset
    AhciNonQueuedErrorRecovery

It performs:
    1 Reinitialize all the commands to send
    2 Start the first command

Affected Variables/Registers:
--*/
{
    BOOLEAN reservedSlotInUse = 0;

    RecordExecutionHistory(ChannelExtension, 0x00000041); // RestorePreservedSettings

    //1 Reinitialize all the commands to send
    ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots;
    reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3);    //ReservedSlotInUse field is at bit 3

    if (reservedSlotInUse == 1) {

        // the process has been started.
        RecordExecutionHistory(ChannelExtension, 0x10010041); // RestorePreservedSettings process already started
        return;
    }

    // acquire active reference for process of restore preserved settings
    if (!AtDIRQL) {
        // this is only needed if function is called at lower level than DIRQL.
        // the places calling this function at DIRQL already made sure the unit is active.
        ULONG   restorePreservedSettingsInProcess;
        BOOLEAN portIdle = FALSE;

        restorePreservedSettingsInProcess = InterlockedBitTestAndSet((LONG*)&ChannelExtension->PoFxPendingWork, 0);  //RestorePreservedSettings is at bit 0

        if (restorePreservedSettingsInProcess == 1) {

            // it's already in process, return from here
            RecordExecutionHistory(ChannelExtension, 0x10020041); // RestorePreservedSettings already in process
            return;
        }

        if (PortAcquireActiveReference(ChannelExtension, NULL, &portIdle)) {
            ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 1;
        }

        if (portIdle) {

            // Unit Active process will continue on RestorePreservedSettings process
            RecordExecutionHistory(ChannelExtension, 0x10030041); // RestorePreservedSettings will continue in Unit Active
            return;
        } else {
            ULONG   restorePreservedSettingsPending;
            restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&ChannelExtension->PoFxPendingWork, 0);  //RestorePreservedSettings is at bit 0

            if (restorePreservedSettingsPending == 0) {

                // Unit Active process already started on RestorePreservedSettings process
                RecordExecutionHistory(ChannelExtension, 0x10040041); // RestorePreservedSettings already started in Unit Active
                return;
            }

        }
    }

    //2 Start the first command
    IssuePreservedSettingCommands(ChannelExtension, NULL);    

    //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine.
    if (ChannelExtension->Local.Srb.SrbExtension != NULL) {
        PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb);
        if (srbExtension->AtaFunction != 0) {
            AhciProcessIo(ChannelExtension, (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb, AtDIRQL);
        }
    }

    return;
}

VOID
AhciPortIssueInitCommands(
    PAHCI_CHANNEL_EXTENSION ChannelExtension
  )
/*++
    Send each of the commands in the Init Command list and PersistentSettings structure, one at a time using the local SRB
It assumes:
    The local SRB is not currently in use
Called by:
    AhciDeviceInitialize

It performs:
    1 Reinitialize all the commands to send
    2 Start the first command

Affected Variables/Registers:
--*/
{
    BOOLEAN reservedSlotInUse = 0;

    RecordExecutionHistory(ChannelExtension, 0x00000042); // AhciPortIssueInitCommands

    //1 Reinitialize all the commands to send
    ChannelExtension->DeviceInitCommands.CommandToSend = 0;
    reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3);    //ReservedSlotInUse field is at bit 3

    if (reservedSlotInUse == 1) {

        // the process has been started.
        RecordExecutionHistory(ChannelExtension, 0x10010042); // AhciPortIssueInitCommands process already started
        return;
    }

    // acquire active reference for process of issuing init commands
    // note: Port should be in Active state during device start.
    if (PortAcquireActiveReference(ChannelExtension, NULL, NULL)) {
        ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 1;
    }

    //2 Start the first command
    IssueInitCommands(ChannelExtension, NULL);

    //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine.
    if (ChannelExtension->Local.Srb.SrbExtension != NULL) {
        PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb);
        if (srbExtension->AtaFunction != 0) {
            AhciProcessIo(ChannelExtension, (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb, FALSE);
        }
    }

    return;
}

VOID
AhciPortStartTelemetry(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    )
/*++

Routine Description:

    This function logs telemetry for AHCI port start status.

Arguments:

    AdapterExtension - Pointer to the device extension for adapter.

Return Value:

    None.

--*/
{
    NT_ASSERT(AdapterExtension->InRunningPortsProcess == FALSE);

    for (ULONG i = 0; i <= AdapterExtension->HighestPort; ++i) {
        PAHCI_CHANNEL_EXTENSION channelExtension = AdapterExtension->PortExtension[i];
        if (channelExtension != NULL) {
            BOOLEAN portStartSuccess = (channelExtension->StartState.ChannelNextStartState == StartComplete) ? (TRUE) : (FALSE);
            AhciTelemetryLogPortStart(channelExtension,
                                      (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress),
                                      ((portStartSuccess) ? (AhciTelemetryEventIdPortStartSuccess) : (AhciTelemetryEventIdPortRunningStartFail)),
                                      ((portStartSuccess) ? ("Port Start Success") : ("Port Start Failed")),
                                      ((portStartSuccess) ? (0) : (AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING)),
                                      NULL,
                                      0
                                      );
        }
    }

    return;
}

VOID
AhciPortMarkDeviceFailed(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ AHCI_DEVICE_FAILURE_REASON FailureReason,
    _In_ ULONG Flags,
    _In_ BOOLEAN SuppressFailure
    )
/*++

Routine Description:

    This function will call storport API to mark failed device.

    When caller passes TRUE for SuppressFailure, this function will
    only call storport API to mark failed device once; otherwise,
    this function will always call storport API to mark failed device.

Arguments:

    ChannelExtension - Pointer to the device extension for channel.

    FailureReason - Indicates device failure reason.

    Flags - Indicates whether remove failed device.

    SuppressFailure - Indicates whether suppress failures.

Return Value:

    None.

--*/
{
    NT_ASSERT(ChannelExtension != NULL);

    //
    // Ignore Non-ATA device failures.
    //
    if (!IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) {
        return;
    }

    switch (FailureReason) {

        case AhciDeviceFailureTooManyBusChange: {
            if ((!SuppressFailure) ||
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) {
                StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension,
                                         (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                         Flags,
                                         L"Too many bus change");
            }

            if (SuppressFailure &&
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) {
                ChannelExtension->DeviceFailureThrottleFlag |= AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK;
            }

            break;
        }

        case AhciDeviceFailureTooManyNCQError: {
            if ((!SuppressFailure) ||
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) {
                StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension,
                                         (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                         Flags,
                                         L"Too many NCQ error");
            }

            if (SuppressFailure &&
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) {
                ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK;
            }

            break;
        }

        case AhciDeviceFailureTooManyNonQueuedError: {
            if ((!SuppressFailure) ||
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) {
                StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension,
                                         (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                         Flags,
                                         L"Too many NonQueue error");
            }

            if (SuppressFailure &&
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) {
                ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK;
            }

            break;
        }

        case AhciDeviceFailureDeviceStuck: {
            if ((!SuppressFailure) ||
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) {
                StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension,
                                         (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress),
                                         0,
                                         L"Device may stuck");
            }

            if (SuppressFailure &&
                ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) {
                ChannelExtension->DeviceFailureThrottleFlag |= AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK;
            }

            break;
        }

        default: {
            NT_ASSERT(FALSE);
            break;
        }
    }
}

#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4054)
#pragma warning(default:4055)
#pragma warning(default:4214)
#pragma warning(default:4201)
#endif


<|start_filename|>audio/sysvad/EndpointsCommon/bthhfpminipairs.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    bthhfpminipairs.h

Abstract:

    Bluetooth HFP audio endpoint filter definitions. 

--*/

#ifndef _SYSVAD_BTHHFPMINIPAIRS_H_
#define _SYSVAD_BTHHFPMINIPAIRS_H_

#include "bthhfpspeakertopo.h"
#include "bthhfpspeakertoptable.h"
#include "bthhfpspeakerwavtable.h"
#include "bthhfpspeakerwbwavtable.h"

#include "bthhfpmictopo.h"
#include "bthhfpmictoptable.h"
#include "bthhfpmicwavtable.h"
#include "bthhfpmicwbwavtable.h"

NTSTATUS
CreateMiniportWaveRTSYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

NTSTATUS
CreateMiniportTopologySYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

//
// Render miniports.
//

/*********************************************************************
* Topology/Wave bridge connection for BTH-HFP speaker                *
*                                                                    *
*              +------+                +------+                      *
*              | Wave |                | Topo |                      *
*              |      |                |      |                      *
* System   --->|0    2|---> Loopback   |      |                      *
*              |      |                |      |                      *
* Offload  --->|1    3|--------------->|0    1|---> Line Out         *
*              |      |                |      |                      *
*              +------+                +------+                      *
*********************************************************************/
static
PHYSICALCONNECTIONTABLE BthHfpSpeakerTopologyPhysicalConnections[] =
{
    {
        KSPIN_TOPO_WAVEOUT_SOURCE,      // TopologyIn
        KSPIN_WAVE_RENDER3_SOURCE,      // WaveOut
        CONNECTIONTYPE_WAVE_OUTPUT
    }
};

// A subdevice is created for each HFP device using the reference string in the form of:
// <Base_Reference_String>-<Hashcode>
// where Base_Reference_String is one of the following template names
#define BTHHFP_SPEAKER_TOPO_NAME    L"TopologyBthHfpSpeaker"
#define BTHHFP_SPEAKER_WAVE_NAME    L"WaveBthHfpSpeaker"
#define BTHHFP_MIC_TOPO_NAME        L"TopologyBthHfpMic"
#define BTHHFP_MIC_WAVE_NAME        L"WaveBthHfpMic"
// And Hashcode is 8-character long hexadecimal value

// A reference string max length is being set to a value to accommodate them both
#define BTHHFP_INTERFACE_REFSTRING_MAX_LENGTH 100

static
ENDPOINT_MINIPAIR BthHfpSpeakerMiniports =
{
    eBthHfpSpeakerDevice,
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_TOPO_NAME and made unique for each side band interface
    BTHHFP_SPEAKER_TOPO_NAME,           // template name matches with KSNAME_TopologyBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &BthHfpSpeakerTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    BTHHFP_SPEAKER_WAVE_NAME,           // template name matches with KSNAME_WaveBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &BthHfpSpeakerWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    BthHfpSpeakerPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(BthHfpSpeakerPinDeviceFormatsAndModes),
    BthHfpSpeakerTopologyPhysicalConnections,
    SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections),
    ENDPOINT_NO_FLAGS
};

static
ENDPOINT_MINIPAIR BthHfpSpeakerWBMiniports =
{
    eBthHfpSpeakerDevice,
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_TOPO_NAME and made unique for each side band interface
    BTHHFP_SPEAKER_TOPO_NAME,           // Template name that matches with KSNAME_TopologyBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &BthHfpSpeakerTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    BTHHFP_SPEAKER_WAVE_NAME,           // Template name matches with KSNAME_WaveBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &BthHfpSpeakerWBWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    BthHfpSpeakerWBPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(BthHfpSpeakerWBPinDeviceFormatsAndModes),
    BthHfpSpeakerTopologyPhysicalConnections,
    SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections),
    ENDPOINT_NO_FLAGS
};

//
// Capture miniports.
//

/*********************************************************************
* Topology/Wave bridge connection for BTH-HFP mic                    *
*                                                                    *
*              +------+    +------+                                  *
*              | Topo |    | Wave |                                  *
*              |      |    |      |                                  *
*  Mic in  --->|0    1|===>|0    1|---> Capture Host Pin             *
*              |      |    |      |                                  *
*              +------+    +------+                                  *
*********************************************************************/
static
PHYSICALCONNECTIONTABLE BthHfpMicTopologyPhysicalConnections[] =
{
    {
        KSPIN_TOPO_BRIDGE,              // TopologyOut
        KSPIN_WAVE_BRIDGE,              // WaveIn
        CONNECTIONTYPE_TOPOLOGY_OUTPUT
    }
};

static
ENDPOINT_MINIPAIR BthHfpMicMiniports =
{
    eBthHfpMicDevice,
    NULL,                               // Will be generated based on BTHHFP_MIC_TOPO_NAME and made unique for each side band interface
    BTHHFP_MIC_TOPO_NAME,               // template name matches with KSNAME_TopologyBthHfpMic in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &BthHfpMicTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    BTHHFP_MIC_WAVE_NAME,               // template name matches with KSNAME_WaveBthHfpMic in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &BthHfpMicWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    BthHfpMicPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(BthHfpMicPinDeviceFormatsAndModes),
    BthHfpMicTopologyPhysicalConnections,
    SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections),
    ENDPOINT_NO_FLAGS
};

static
ENDPOINT_MINIPAIR BthHfpMicWBMiniports =
{
    eBthHfpMicDevice,
    NULL,                               // Will be generated based on BTHHFP_MIC_TOPO_NAME and made unique for each side band interface
    BTHHFP_MIC_TOPO_NAME,               // template name matches with KSNAME_TopologyBthHfpMic in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &BthHfpMicTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    BTHHFP_MIC_WAVE_NAME,               // template name matches with KSNAME_WaveBthHfpMic in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &BthHfpMicWBWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    BthHfpMicWBPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(BthHfpMicWBPinDeviceFormatsAndModes),
    BthHfpMicTopologyPhysicalConnections,
    SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections),
    ENDPOINT_NO_FLAGS
};

//=============================================================================
//
// Render miniport pairs.
//
static
PENDPOINT_MINIPAIR  g_BthHfpRenderEndpoints[] = 
{
    &BthHfpSpeakerMiniports,
    &BthHfpSpeakerWBMiniports
};

#define g_cBthHfpRenderEndpoints  (SIZEOF_ARRAY(g_BthHfpRenderEndpoints))

// If the count of Render endpoints changes, update formula below
C_ASSERT(g_cBthHfpRenderEndpoints == 2);

//=============================================================================
//
// Capture miniport pairs.
//
static
PENDPOINT_MINIPAIR  g_BthHfpCaptureEndpoints[] = 
{
    &BthHfpMicMiniports,
    &BthHfpMicWBMiniports
};

#define g_cBthHfpCaptureEndpoints (SIZEOF_ARRAY(g_BthHfpCaptureEndpoints))

// If the count of Capture endpoints changes, update formula below
C_ASSERT(g_cBthHfpCaptureEndpoints == 2);

//=============================================================================
//
// Total miniports = # endpoints * 2 (topology + wave) / 2 (since we have one each for narrowband-only and wideband)
//
// 
// Allow 10 Bluetooth hands-free profile devices.
//
#define MAX_BTHHFP_DEVICES      (10)
#define g_MaxBthHfpMiniports    ((g_cBthHfpRenderEndpoints + g_cBthHfpCaptureEndpoints) * MAX_BTHHFP_DEVICES)

// g_MaxBthHfpMiniports is used when calculating the MaxObjects inside AddDevice.
C_ASSERT(g_MaxBthHfpMiniports == 40);

#endif // _SYSVAD_BTHHFPMINIPAIRS_H_



<|start_filename|>audio/sysvad/EndpointsCommon/usbhsmictopo.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsmictopo.cpp

Abstract:

    Implementation of topology miniport for the mic (external: headphone).

--*/
#ifdef SYSVAD_USB_SIDEBAND

#pragma warning (disable : 4127)

#include <sysvad.h>
#include "simple.h"
#include "mintopo.h"
#include "usbhstopo.h"
#include "usbhsmictopo.h"
#include "usbhsmictoptable.h"

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_UsbHsMicTopoFilter
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Redirects property request to miniport object

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicTopoFilter]"));

    // PropertryRequest structure is filled by portcls. 
    // MajorTarget is a pointer to miniport object for miniports.
    //
    NTSTATUS            ntStatus = STATUS_INVALID_DEVICE_REQUEST;

    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack))
    {
        switch(PropertyRequest->PropertyItem->Id)
        {
            case KSPROPERTY_JACK_DESCRIPTION:
                ntStatus = PropertyHandler_UsbHsJackDescription(
                    PropertyRequest,
                    ARRAYSIZE(UsbHsMicJackDescriptions),
                    UsbHsMicJackDescriptions);
                break;
            
            case KSPROPERTY_JACK_DESCRIPTION2:
                ntStatus = PropertyHandler_UsbHsJackDescription2(
                    PropertyRequest,
                    ARRAYSIZE(UsbHsMicJackDescriptions),
                    UsbHsMicJackDescriptions);
                break;
            
            case KSPROPERTY_JACK_CONTAINERID:
                ntStatus = PropertyHandler_UsbHsJackContainerId(
                    PropertyRequest,
                    ARRAYSIZE(UsbHsMicJackDescriptions),
                    UsbHsMicJackDescriptions);
                break;            
        }
    }
    
    return ntStatus;
} // PropertyHandler_UsbHsMicTopoFilter

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_UsbHsMicVolumeLevel
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL )

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicVolumeLevel]"));

    NTSTATUS                ntStatus        = STATUS_INVALID_DEVICE_REQUEST;
    PCMiniportTopology      miniport        = (PCMiniportTopology)PropertyRequest->MajorTarget;
    PSIDEBANDDEVICECOMMON     usbHsDevice    = NULL;
    ULONG                   channel         = (ULONG)-1;
    
    usbHsDevice = miniport->GetSidebandDevice(); // weak ref.
    ASSERT(usbHsDevice != NULL);
    
    if (usbHsDevice->IsVolumeSupported(miniport->m_DeviceType) == FALSE)
    {
       ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); 
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ntStatus = PropertyHandler_UsbHsVolumeLevel_BasicSupport(PropertyRequest);
    }
    else if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        // Instance is the channel #.
        channel = *(PULONG(PropertyRequest->Instance));

        ULONG cbNeeded = sizeof(LONG);

        if (PropertyRequest->ValueSize == 0)
        {
            PropertyRequest->ValueSize = cbNeeded;
            ntStatus = STATUS_BUFFER_OVERFLOW;
        }
        else if (PropertyRequest->ValueSize < cbNeeded)
        {
            ntStatus = STATUS_BUFFER_TOO_SMALL;
        }
        else
        {
            LONG* volume = (LONG*)PropertyRequest->Value;

            if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
            {
                ntStatus = usbHsDevice->GetVolume(eUsbHsMicDevice, channel, volume);
            }
            else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
            {
                ntStatus = usbHsDevice->SetVolume(eUsbHsMicDevice, channel, *volume);
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_UsbHsMicMute
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MUTE )

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicMute]"));

    NTSTATUS                ntStatus        = STATUS_INVALID_DEVICE_REQUEST;
    PCMiniportTopology      miniport        = (PCMiniportTopology)PropertyRequest->MajorTarget;
    PSIDEBANDDEVICECOMMON   usbHsDevice    = NULL;
    ULONG                   channel         = (ULONG)-1;
    
    usbHsDevice = miniport->GetSidebandDevice(); // weak ref.
    ASSERT(usbHsDevice != NULL);
    
    if (usbHsDevice->IsMuteSupported(miniport->m_DeviceType) == FALSE)
    {
       ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); 
    }
    else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
    {
        ntStatus = PropertyHandler_UsbHsMute_BasicSupport(PropertyRequest);
    }
    else if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        // Instance is the channel #.
        channel = *(PULONG(PropertyRequest->Instance));

        ULONG cbNeeded = sizeof(LONG);

        if (PropertyRequest->ValueSize == 0)
        {
            PropertyRequest->ValueSize = cbNeeded;
            ntStatus = STATUS_BUFFER_OVERFLOW;
        }
        else if (PropertyRequest->ValueSize < cbNeeded)
        {
            ntStatus = STATUS_BUFFER_TOO_SMALL;
        }
        else
        {
            LONG* mute = (LONG*)PropertyRequest->Value;

            if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
            {
                *mute = usbHsDevice->GetMute(eUsbHsMicDevice, channel);

                ntStatus = STATUS_SUCCESS;

            }
            else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
            {
                ntStatus = usbHsDevice->SetMute(eUsbHsMicDevice, channel, *mute);
            }
        }
    }

    return ntStatus;
}

//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
PropertyHandler_UsbHsMicTopoNode
( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
)
/*++

Routine Description:

  Redirects property request to miniport object

Arguments:

  PropertyRequest - 

Return Value:

  NT status code.

--*/
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicTopoNode]"));

    // PropertryRequest structure is filled by portcls. 
    // MajorTarget is a pointer to miniport object for miniports.
    //
    NTSTATUS    ntStatus = STATUS_INVALID_DEVICE_REQUEST;

    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio))
    {
        if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
        {
            ntStatus = PropertyHandler_UsbHsMicVolumeLevel(PropertyRequest);
        }
        else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MUTE)
        {
            ntStatus = PropertyHandler_UsbHsMicMute(PropertyRequest);
        }
    }

    return ntStatus;
} // PropertyHandler_UsbHsMicTopoFilter

//=============================================================================
#pragma code_seg()
NTSTATUS 
PropertyHandler_UsbHsMicTopoNodeEvent
(
    _In_    PPCEVENT_REQUEST    EventRequest
)
{
    ASSERT(EventRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicTopoNodeEvent]"));

    // Validate the node.
    if (EventRequest->Node != KSNODE_TOPO_VOLUME &&
        EventRequest->Node != KSNODE_TOPO_MUTE)
        return STATUS_INVALID_PARAMETER;

    return PropertyHandler_UsbHsTopoNodeEvent(EventRequest);
}

//=============================================================================
#pragma code_seg()
NTSTATUS
PropertyHandler_UsbHsMicTopoFilterEvent
(
    _In_    PPCEVENT_REQUEST    EventRequest
)
{
    ASSERT(EventRequest);

    DPF_ENTER(("[PropertyHandler_UsbHsMicTopoFilterEvent]"));

    return PropertyHandler_UsbHsTopoNodeEvent(EventRequest);
}

#pragma code_seg()
#endif  // SYSVAD_USB_SIDEBAND




<|start_filename|>audio/sysvad/APO/KWSApo/KWSApoDll.cpp<|end_filename|>
//
// KWSApoDll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Author:
//
// Description:
//
// KWSApoDll.cpp : Implementation of DLL Exports.

#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsync.h>
#include <mmreg.h>

#include "resource.h"
#include "KWSApoDll.h"
#include <KWSApo.h>

#include <KWSApoDll_i.c>


//-------------------------------------------------------------------------
// Array of APO_REG_PROPERTIES structures implemented in this module.
// Each new APO implementation will be added to this array.
//
APO_REG_PROPERTIES const *gCoreAPOs[] =
{
    &CKWSApoEFX::sm_RegProperties.m_Properties
};

// {secret}
class CKWSApoDllModule : public CAtlDllModuleT< CKWSApoDllModule >
{
public :
    DECLARE_LIBID(LIBID_KWSApoDlllib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_KWSAPODLL, "{0A21D954-674A-4C09-806E-DB4FBE8F199C}")

};

// {secret}
CKWSApoDllModule _AtlModule;


// {secret}
extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
    {
    }
    // do necessary cleanup only if the DLL is being unloaded dynamically
    else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved))
    {
    }

    return _AtlModule.DllMain(dwReason, lpReserved);
}


// {secret}
STDAPI DllCanUnloadNow(void)
{
    return _AtlModule.DllCanUnloadNow();
}


// {secret}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
{
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/TestMediaType.cpp<|end_filename|>
// AudioMediaType.cpp -- Copyright (c) 2003 Microsoft Corporation
//
//
// Description:
//
//      Implementation of the TestMediaType
//

#include "TestMediaType.h"
#include <memory>
#include <wil\resultmacros.h>

//-----------------------------------------------------------------------------
// Description:
//
//  Creates an CTestAudioMediaType object with the given format.
//
// Parameters:
//
//      pFormat - [in] Format for the new AudioMediaType.
//      ppIAudioMediaType - [out] upon success returns the pointer to the 
//                                new interface.
//
// Return values:
//  
//      S_OK            Success
//      E_OUTOFMEMORY   On memory failure.
//      E_INVALIDARG    Null parameter
//
// Remarks:
//
HRESULT CTestAudioMediaType::Create
(  
    const UNCOMPRESSEDAUDIOFORMAT* pFormat,
    IAudioMediaType** ppIAudioMediaType
)
{
    if (!pFormat || !ppIAudioMediaType)
    {
        return E_INVALIDARG;
    }

    HRESULT hResult = S_OK;

    *ppIAudioMediaType = NULL;

    std::unique_ptr<CTestAudioMediaType> pObj(new(std::nothrow) CTestAudioMediaType());

    pObj->m_Format = *pFormat;

    // TODO: iter8 fix this to use WAVEFORMATEXTENSIBLE (commented lines) 
    // after KSCommon lib is fixed
    if (KSDATAFORMAT_SUBTYPE_PCM ==  pFormat->guidFormatType)
    {
        pObj->m_Wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
    }
    else
    {
        pObj->m_Wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    }
    pObj->m_Wfx.Format.nChannels = (WORD)pObj->m_Format.dwSamplesPerFrame;
    pObj->m_Wfx.Format.nSamplesPerSec = (DWORD)pObj->m_Format.fFramesPerSecond;
    pObj->m_Wfx.Format.nAvgBytesPerSec = (DWORD)(pObj->m_Format.dwBytesPerSampleContainer * 
        pObj->m_Format.fFramesPerSecond * pObj->m_Format.dwSamplesPerFrame);
    pObj->m_Wfx.Format.nBlockAlign = (WORD)(pObj->m_Format.dwBytesPerSampleContainer * pObj->m_Format.dwSamplesPerFrame);
    pObj->m_Wfx.Format.wBitsPerSample = (WORD)pObj->m_Format.dwValidBitsPerSample;
    pObj->m_Wfx.Format.cbSize = 0;

    hResult = pObj->QueryInterface( 
        __uuidof(IAudioMediaType), 
        (LPVOID *) ppIAudioMediaType );

    return( hResult );
} // Create

//-----------------------------------------------------------------------------
// Description:
//
//  Used to addref an interface
//
// Results:
//
//     New reference count
//
ULONG STDMETHODCALLTYPE CTestAudioMediaType::AddRef()
{
    return (ULONG) InterlockedIncrement( (LONG *) &m_cRef );
} // AddRef

//-----------------------------------------------------------------------------
// Description:
//
//  Used to release an interface
//
// Results:
//
//     New reference count
//
ULONG STDMETHODCALLTYPE CTestAudioMediaType::Release()
{
    ULONG ulNewRefCount = (ULONG) InterlockedDecrement( (LONG *) &m_cRef );

    //
    //  If our new refcount is zero, then we need to delete ourselves
    //
    if( 0 == ulNewRefCount )
    {
        delete this;
    }
    
    return( ulNewRefCount );
} // Release

//-----------------------------------------------------------------------------
// Description:
//
//  Used to query an interface
//
// Parameters:
//
//     riid - [in] id to look for
//     ppvObject - [out] pointer to object
//
// Return Values:
//
//    S_OK             On success.
//    E_POINTER        Invalid destination pointer
//    E_NOINTERFACE    No interface of that type
//    error            Other
//
STDMETHODIMP CTestAudioMediaType::QueryInterface( 
    REFIID riid, LPVOID *ppvObject )
{
    if (!ppvObject)
    {
        return E_POINTER;
    }

    HRESULT     hResult;

    hResult = S_OK;
    *ppvObject = NULL;

    if( ( riid == __uuidof(IAudioMediaType) ) ||
        ( riid == __uuidof(IUnknown) ) )
    {
        *ppvObject = ( IAudioMediaType *)this;
    }
    else
    {
        hResult = E_NOINTERFACE;
    }

    if ( SUCCEEDED( hResult ) )
    {
        ((LPUNKNOWN) *ppvObject)->AddRef( );
    }

    return( hResult );
} // QueryInterface
      
//-----------------------------------------------------------------------------
// Description:
//
//  Not implemented
//
// Parameters:
//
//     pAudioMediaType - [in] media type
//     pdwFlags - [out] flags
//
// Return Values:
//
//    E_NOTIMPL       Not implemented.
//
HRESULT STDMETHODCALLTYPE CTestAudioMediaType::IsEqual( 
    IAudioMediaType * /* pAudioMediaType */,
    DWORD * /* pdwFlags */
)
{
    return E_NOTIMPL;
} // IsEqual

//-----------------------------------------------------------------------------
// Description:
//
//  Returns pointer to internal member variable m_Wfx.
//
// Results:
//
//     Returns pointer to WaveFormatEx representing the format.
//
const WAVEFORMATEX *STDMETHODCALLTYPE CTestAudioMediaType::GetAudioFormat( void)
{
    return reinterpret_cast<WAVEFORMATEX*>(&m_Wfx);
} // GetAudioFormat
 
//-----------------------------------------------------------------------------
// Description:
//
//  Copies the uncompressed data to the parameter.
//
// Parameters:
//
//     pUncompressedAudioFormat - [in] format pointer
//
// Return Values:
//
//    S_OK         On success
//    E_POINTER    Invalid pointer
//
HRESULT STDMETHODCALLTYPE CTestAudioMediaType::GetUncompressedAudioFormat( 
    UNCOMPRESSEDAUDIOFORMAT *pUncompressedAudioFormat 
)
{
    RETURN_HR_IF(E_FAIL, (KSDATAFORMAT_SUBTYPE_PCM != m_Format.guidFormatType) && 
        (KSDATAFORMAT_SUBTYPE_IEEE_FLOAT != m_Format.guidFormatType));

    if (!pUncompressedAudioFormat)
    {
        return E_POINTER;
    }

    CopyMemory(pUncompressedAudioFormat, &m_Format, sizeof(UNCOMPRESSEDAUDIOFORMAT));

    return S_OK;
} // GetUncompressedAudioFormat


<|start_filename|>audio/sysvad/APO/KWSApo/KWSApo.cpp<|end_filename|>
//
// KWSApo.cpp -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description:
//
//  Implementation of ProcessBuffer
//
#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsync.h>
#include <mmreg.h>

#include <audioenginebaseapo.h>
#include <baseaudioprocessingobject.h>
#include <resource.h>

#include <float.h>

#include "KWSApo.h"

#pragma AVRT_CODE_BEGIN
void WriteSilence(
    _Out_writes_(u32FrameCount * u32SamplesPerFrame)
        FLOAT32 *pf32Frames,
    UINT32 u32FrameCount,
    UINT32 u32SamplesPerFrame )
{
    ZeroMemory(pf32Frames, sizeof(FLOAT32) * u32FrameCount * u32SamplesPerFrame);
}
#pragma AVRT_CODE_END

#pragma AVRT_CODE_BEGIN
void ProcessBuffer(
    FLOAT32 *pf32OutputFrames,
    const FLOAT32 *pf32InputFrames,
    UINT32   u32ValidFrameCount,
    INTERLEAVED_AUDIO_FORMAT_INFORMATION *formatInfo)
{
    UINT32 totalChannelCount = (formatInfo->PrimaryChannelCount + formatInfo->InterleavedChannelCount);

    ASSERT_REALTIME();
    ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) );
    ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) );

    // loop through samples
    while (u32ValidFrameCount--)
    {
        // copy over the Primary channel data
        for (UINT32 i = formatInfo->PrimaryChannelStartPosition; i < (formatInfo->PrimaryChannelStartPosition + formatInfo->PrimaryChannelCount); i++)
        {
            *pf32OutputFrames = *(pf32InputFrames + i);
            pf32OutputFrames++;
        }

        // step forward to the next frame, ignoring interleaved data
        pf32InputFrames += (totalChannelCount);
    }
}

#pragma AVRT_CODE_END



<|start_filename|>audio/sysvad/EndpointsCommon/usbhsmictoptable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsmictopotable.h

Abstract:

    Declaration of topology table for the USB Headset mic (external)

--*/

#ifndef _SYSVAD_USBHSMICTOPTABLE_H_
#define _SYSVAD_USBHSMICTOPTABLE_H_

#include "usbhsmictopo.h"

// Function declarations.
NTSTATUS
PropertyHandler_UsbHsMicTopoFilter( 
    _In_ PPCPROPERTY_REQUEST      PropertyRequest 
    );

//=============================================================================
static
KSDATARANGE UsbHsMicTopoPinDataRangesBridge[] =
{
 {
   sizeof(KSDATARANGE),
   0,
   0,
   0,
   STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
   STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
   STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
 }
};

//=============================================================================
static
PKSDATARANGE UsbHsMicTopoPinDataRangePointersBridge[] =
{
  &UsbHsMicTopoPinDataRangesBridge[0]
};

//=============================================================================
static
PCPIN_DESCRIPTOR UsbHsMicTopoMiniportPins[] =
{
  // KSPIN_TOPO_MIC_ELEMENTS
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(UsbHsMicTopoPinDataRangePointersBridge),// DataRangesCount
      UsbHsMicTopoPinDataRangePointersBridge,              // DataRanges
      KSPIN_DATAFLOW_IN,                                // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSNODETYPE_HEADSET_MICROPHONE,                   // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  },

  // KSPIN_TOPO_BRIDGE
  {
    0,
    0,
    0,                                                  // InstanceCount
    NULL,                                               // AutomationTable
    {                                                   // KsPinDescriptor
      0,                                                // InterfacesCount
      NULL,                                             // Interfaces
      0,                                                // MediumsCount
      NULL,                                             // Mediums
      SIZEOF_ARRAY(UsbHsMicTopoPinDataRangePointersBridge),// DataRangesCount
      UsbHsMicTopoPinDataRangePointersBridge,              // DataRanges
      KSPIN_DATAFLOW_OUT,                               // DataFlow
      KSPIN_COMMUNICATION_NONE,                         // Communication
      &KSCATEGORY_AUDIO,                                // Category
      NULL,                                             // Name
      0                                                 // Reserved
    }
  }
};

//=============================================================================
static
KSJACK_DESCRIPTION UsbHsMicJackDesc =
{
    KSAUDIO_SPEAKER_MONO,
    JACKDESC_RGB(0, 0, 0),
    eConnTypeOtherDigital,
    eGeoLocBottom,
    eGenLocPrimaryBox,
    ePortConnJack,
    FALSE               // NOTE: For convenience, wired headset jacks will be "unplugged" at boot.
};

//=============================================================================
// Only return a KSJACK_DESCRIPTION for the physical bridge pin.
static 
PKSJACK_DESCRIPTION UsbHsMicJackDescriptions[] =
{
    &UsbHsMicJackDesc,
    NULL
};

//=============================================================================
static
PCPROPERTY_ITEM UsbHsMicPropertiesVolume[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_VOLUMELEVEL,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsMicTopoNode
  }
};

static
PCEVENT_ITEM UsbHsMicPropertiesVolumeEvent[] =
{
  // This is a generic event for nearly every node property.
  {
    &KSEVENTSETID_AudioControlChange,               // Something changed!
    KSEVENT_CONTROL_CHANGE,                         // The only event-property defined.
    KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsMicTopoNodeEvent
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationUsbHsMicVolumeWithEvent, 
                                     UsbHsMicPropertiesVolume, 
                                     UsbHsMicPropertiesVolumeEvent);


//=============================================================================
static
PCPROPERTY_ITEM UsbHsMicPropertiesMute[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_MUTE,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsMicTopoNode
  }
};

static
PCEVENT_ITEM UsbHsMicPropertiesMuteEvent[] =
{
  // This is a generic event for nearly every node property.
  {
    &KSEVENTSETID_AudioControlChange,               // Something changed!
    KSEVENT_CONTROL_CHANGE,                         // The only event-property defined.
    KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsMicTopoNodeEvent
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationUsbHsMicMuteWithEvent, 
                                     UsbHsMicPropertiesMute, 
                                     UsbHsMicPropertiesMuteEvent);

//=============================================================================
static
PCPROPERTY_ITEM UsbHsMicPropertiesPeakMeter[] =
{
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_PEAKMETER2,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_Topology
  },
  {
    &KSPROPSETID_Audio,
    KSPROPERTY_AUDIO_CPU_RESOURCES,
    KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
    PropertyHandler_Topology
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationUsbHsMicPeakMeter, UsbHsMicPropertiesPeakMeter);

//=============================================================================
static
PCNODE_DESCRIPTOR UsbHsMicTopologyNodes[] =
{
  // KSNODE_TOPO_VOLUME
  {
    0,                                      // Flags
    &AutomationUsbHsMicVolumeWithEvent,     // AutomationTable
    &KSNODETYPE_VOLUME,                     // Type
    &KSAUDFNAME_MIC_VOLUME                  // Name
  },
  // KSNODE_TOPO_MUTE
  {
    0,                                      // Flags
    &AutomationUsbHsMicMuteWithEvent,       // AutomationTable
    &KSNODETYPE_MUTE,                       // Type
    &KSAUDFNAME_MIC_MUTE                    // Name
  },
  // KSNODE_TOPO_PEAKMETER
  {
    0,                          // Flags
    &AutomationUsbHsMicPeakMeter,  // AutomationTable
    &KSNODETYPE_PEAKMETER,      // Type
    &KSAUDFNAME_PEAKMETER       // Name
  }
};

C_ASSERT( KSNODE_TOPO_VOLUME  == 0 );
C_ASSERT( KSNODE_TOPO_MUTE    == 1 );
C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 );

//=============================================================================
static
PCCONNECTION_DESCRIPTOR UsbHsMicMiniportConnections[] =
{
    //  FromNode,                 FromPin,                    ToNode,                 ToPin
    {   PCFILTER_NODE,            KSPIN_TOPO_MIC_ELEMENTS,    KSNODE_TOPO_VOLUME,     1 },
    {   KSNODE_TOPO_VOLUME,       0,                          KSNODE_TOPO_MUTE,       1 },
    {   KSNODE_TOPO_MUTE,         0,                          KSNODE_TOPO_PEAKMETER,  1 },
    {   KSNODE_TOPO_PEAKMETER,    0,                          PCFILTER_NODE,          KSPIN_TOPO_BRIDGE }
};


//=============================================================================
static
PCPROPERTY_ITEM UsbHsMicPropertiesTopoFilter[] =
{
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsMicTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION2,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsMicTopoFilter
    },
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_CONTAINERID,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_UsbHsMicTopoFilter
    }

};

static 
PCEVENT_ITEM UsbHsMicTopoPropertiesFilterEvent[] =
{
  {
    &KSEVENTSETID_PinCapsChange,                        // Something changed!
    KSEVENT_PINCAPS_JACKINFOCHANGE,                     // The only event-property defined.
    KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT,
    PropertyHandler_UsbHsMicTopoFilterEvent
  }
};

DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationUsbHsMicTopoFilterWithEvent, 
                               UsbHsMicPropertiesTopoFilter,
                               UsbHsMicTopoPropertiesFilterEvent);


//=============================================================================
static
PCFILTER_DESCRIPTOR UsbHsMicTopoMiniportFilterDescriptor =
{
  0,                                        // Version
  &AutomationUsbHsMicTopoFilterWithEvent,  // AutomationTable
  sizeof(PCPIN_DESCRIPTOR),                 // PinSize
  SIZEOF_ARRAY(UsbHsMicTopoMiniportPins),  // PinCount
  UsbHsMicTopoMiniportPins,                // Pins
  sizeof(PCNODE_DESCRIPTOR),                // NodeSize
  SIZEOF_ARRAY(UsbHsMicTopologyNodes),     // NodeCount
  UsbHsMicTopologyNodes,                   // Nodes
  SIZEOF_ARRAY(UsbHsMicMiniportConnections),// ConnectionCount
  UsbHsMicMiniportConnections,             // Connections
  0,                                        // CategoryCount
  NULL                                      // Categories
};

#endif // _SYSVAD_USBHSMICTOPTABLE_H_



<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APODeviceTest.h<|end_filename|>
#include <stdafx.h>

class CApoDeviceTests;

class CAPODevice
{
    public:
        CAPODevice
        (
            IMMDevice          *pIEndpoint,
            LPCWSTR             pszClassId,
            LPCWSTR             pszEndpoint,
            IPropertyStore     *pIStoreDevice,
            IPropertyStore     *pIStoreFx,
            LPARAM              apoType,
            LPCWSTR             apoName,
            LPCWSTR             pszAttachedDevice,
            BOOL                bProxyAPO
        );
        CAPODevice
        (
            IAudioProcessingObject     *pIAPO,
            LPCWSTR                     pszClassId
        );
        ~CAPODevice(void);
        HRESULT InitializeAPO(IAudioProcessingObject *pIAPO);
        DWORD   GetDeviceType() { return (DWORD)(m_lpType); }
        BOOL    IsValid() { return (m_fValid); }
        void    GetClsID(GUID *pgClsID) { *pgClsID = m_gClsID; }
        HRESULT GetAPOInterfaces
        (
            IAudioProcessingObject                **pIAPO,
            IAudioProcessingObjectRT              **pIAPORT,
            IAudioProcessingObjectConfiguration   **pIAPOConfig
        );
        IUnknown *GetObject() { return ((IUnknown*)m_pIUnknown.get()); }
        IMMDevice *GetEndpoint() { return m_pIEndpoint.get(); }
        IPropertyStore *GetEndpointStore() { return m_pIEPStore.get(); }
        IPropertyStore *GetFxStore() { return m_pIFXStore.get(); }
        IMMDeviceCollection *GetDeviceCollection() { return m_pIDevCollection.get(); }
        UINT GetSoftwareIoDeviceInCollection() { return m_nSoftwareIoDeviceInCollection; }
        UINT GetSoftwareIoConnectorIndex() { return m_nSoftwareIoConnectorIndex; }
        PAPO_REG_PROPERTIES GetProperties() { return (m_pRegProps); }
        LPSTR GetAttachedDevice() { return (LPSTR)(m_szAttachedDevice.get()); }
        BOOL IsProxyApo() { return m_bProxyAPO; }

        BOOL    m_fSelected;      // if TRUE, this device will be selected
        wil::unique_cotaskmem_string                    m_szEndpoint;
        wil::unique_cotaskmem_string                    m_sApoName;
        wil::unique_cotaskmem_string                    m_sApoTypeName;

    private:
        BOOL                                            m_fValid;
        GUID                                            m_gClsID;
        wil::com_ptr_nothrow<IUnknown>                  m_pIUnknown;
        wil::com_ptr_nothrow<IAudioProcessingObject>    m_pIAPO;
        PAPO_REG_PROPERTIES                             m_pRegProps;
        
        //  These are only used for SysFx
        wil::com_ptr_nothrow<IMMDevice>                 m_pIEndpoint;
        wil::unique_cotaskmem_string                    m_szAttachedDevice;
        wil::com_ptr_nothrow<IPropertyStore>            m_pIEPStore;
        wil::com_ptr_nothrow<IPropertyStore>            m_pIFXStore;
        wil::com_ptr_nothrow<IMMDeviceCollection>       m_pIDevCollection;
        UINT                                            m_nSoftwareIoDeviceInCollection;
        UINT                                            m_nSoftwareIoConnectorIndex;
        BOOL                                            m_bProxyAPO;
        wil::unique_cotaskmem_string                    m_szPnPId;   // a string that represents the PnPId (or other description) of the device under test
        LPARAM                                          m_lpType;         // flags that will be intersected with test case flags
};


// ----------------------------------------------------------
// Stores a list of APO devices on an endpoint pDevice
// The list is populated using AddSysFxDevices and accessed
// using m_DeviceList
// ----------------------------------------------------------
class CAPODeviceList
{
public:
    CAPODeviceList();
    ~CAPODeviceList(VOID);

    HRESULT Initialize(wil::com_ptr_nothrow<IMMDevice> pDevice, LPWSTR deviceName);
    HRESULT AddSysFxDevices();

protected:
    friend class CApoDeviceTests;

    std::list<std::unique_ptr<CAPODevice>>  m_DeviceList;
    wil::com_ptr_nothrow<IMMDevice>         m_spDevice;
    WCHAR                                   m_deviceName[MAX_PATH] = { '\0' };
};

class CApoDeviceTests : public WEX::TestClass<CApoDeviceTests>
{
    CApoDeviceTests(){};
    ~CApoDeviceTests(){};

    BEGIN_TEST_CLASS(CApoDeviceTests)
        START_APPVERIFIFER
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioenginebaseapop.h")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"devicetopologyp.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"Elevated")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        END_APPVERIFIER
    END_TEST_CLASS()

  public:
    TEST_METHOD_SETUP(setUpMethod);
    TEST_METHOD_CLEANUP(tearDownMethod);

  protected:
    BEGIN_TEST_METHOD(TestAPOInitialize)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"ED37B03B-F091-4C8D-9EDD-FE7EC803BC8E")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"5B0F542E-719A-4949-A9DF-F8BBAC0E9912")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify APO Initializes - TestAPOInitialize")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"1")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"5")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestAPOInitialize")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

    BEGIN_TEST_METHOD(TestCustomFormatSupport)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"571BD021-64F5-4CD1-9BA4-8ABD0857025F")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"EB313E34-CD9D-410B-9C6F-E7032C74F4D1")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify All Formats on Device are Valid - TestCustomFormatSupport")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"1")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"5")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestCustomFormatSupport")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

    BEGIN_TEST_METHOD(TestValidFrameCount)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"B9E9F61A-2882-4256-ABA8-D57A46E54B76")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"ABE4D880-7F1A-434C-A7B9-248FFA96D2CF")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify Frame Count is Valid - TestValidFrameCount")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"1")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"5")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestValidFrameCount")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

    BEGIN_TEST_METHOD(TestAPODataStreaming)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"D327B019-99E4-41BD-BF66-AF1A92801DAC")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"9EBF7FC3-7ADB-49A1-B0E0-C7501841E05A")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify APO Streams Data - TestAPODataStreaming")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"1")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"5")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestAPODataStreaming")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

    BEGIN_TEST_METHOD(TestActivateDeactivate)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"D1C21041-9080-4990-B6A2-5F718649A3F2")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"F0556EDA-4757-409F-AE89-C5C1B0E31A84")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify Proper APO Activation and Deactivation - TestActivateDeactivate")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"1")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"5")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestActivateDeactivate")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()

  private:
    std::list<std::unique_ptr<CAPODeviceList>> m_testDeviceWrapperList;
};

BEGIN_MODULE()
    MODULE_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
    MODULE_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
END_MODULE()


<|start_filename|>general/SimpleMediaSource/MediaSource/SimpleFrameGenerator.h<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//

#pragma once
#ifndef SIMPLE_FRAME_GENERATOR_H
#define SIMPLE_FRAME_GENERATOR_H

class SimpleFrameGenerator
{
public:
    SimpleFrameGenerator() = default;
    ~SimpleFrameGenerator() {};

    HRESULT Initialize(_In_ IMFMediaType* pMediaType);

    HRESULT CreateFrame(
        _Inout_updates_bytes_(len) BYTE* pBuf,
        _In_ DWORD len,
        _In_ LONG pitch,
        _In_ ULONG rgbMask);

    // pixel format converter
    static void RGB24ToYUY2(int R, int G, int B, BYTE* pY, BYTE* pU, BYTE* pV);
    static void RGB24ToY(int R, int G, int B, BYTE* pY);
    static void RGB32ToNV12(BYTE RGB1[8], BYTE RGB2[8], BYTE* pY1, BYTE* pY2, BYTE* pUV);

    static HRESULT RGB32ToNV12Frame(_Inout_updates_bytes_(len) BYTE* pbBuff, ULONG cbBuff, long stride, UINT width, UINT height, BYTE* pbBuffOut, ULONG cbBuffOut, long strideOut);

private: 
    HRESULT _CreateRGB32Frame(
        _Inout_updates_bytes_(len) BYTE* pBuf,
        _In_ DWORD len,
        _In_ LONG pitch,
        _In_ DWORD width,
        _In_ DWORD height,
        _In_ ULONG rgbMask);

    UINT32 m_width = 0;
    UINT32 m_height = 0;
    GUID m_subType = GUID_NULL;

};

#endif



<|start_filename|>bluetooth/serialhcibus/pdo.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    Pdo.c

Abstract:

    This module create a PDO and handles plug & play calls for the child device (PDO).

Environment:

    kernel mode only

--*/

#include "driver.h"
#include "pdo.tmh"


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PdoCreate)
#pragma alloc_text(PAGE, PdoDevD0Exit)
#pragma alloc_text(PAGE, PdoDevD0Entry)
#pragma alloc_text(PAGE, PdoDevPrepareHardware)
#pragma alloc_text(PAGE, PdoDevReleaseHardware)
#pragma alloc_text(PAGE, PdoResetHandler)
#ifdef DYNAMIC_ENUM
#pragma alloc_text(PAGE, PdoCreateDynamic)
#pragma alloc_text(PAGE, PdoResetHandlerDynamic)
#endif // DYNAMIC_ENUM
#endif // ALLOC_PRAGMA

#define MAX_ID_LEN 80

#ifdef DYNAMIC_ENUM

_Use_decl_annotations_
NTSTATUS
PdoResetHandlerDynamic(
    PVOID               _InterfaceContext,
    DEVICE_RESET_TYPE   _ResetType,
    ULONG               _Flags,
    PVOID               _ResetParameters
    )
/*++

Routine Description:

    This is the reset handler invoked by a driver that queries our GUID_DEVICE_RESET_INTERFACE_STANDARD interface.
    This is the version used if PDOs are created using WDF dynamic enumeration.

Arguments:

    _InterfaceContext - This is expected to be a PPDO_EXTENSION.

    _ResetType - This is expected to be PlatformLevelDeviceReset because that is all we support.

    _Flags - Unused, because no flags are defined currently.

    _ResetParameters - Unused, because it is only used for FunctionLevelDeviceReset.

Return Value:

    NTSTATUS code.

--*/
{
    PPDO_EXTENSION PdoExtension = (PPDO_EXTENSION) _InterfaceContext;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(_Flags);
    UNREFERENCED_PARAMETER(_ResetParameters);

    if (_ResetType != PlatformLevelDeviceReset) {
        return STATUS_NOT_SUPPORTED;
    }

    DeviceDoPLDR(PdoExtension->FdoExtension->WdfDevice);

    //
    // The bus driver is expected to cause the device to be surprise removed as part of PLDR. For
    // many hardware buses, this happens naturally, but for a software bus like us, we need to do it
    // ourselves. WDF dynamic enumeration makes this convenient because it already supports
    // GUID_REENUMERATE_SELF_INTERFACE_STANDARD, so we can simply call WdfDeviceSetFailed to
    // re-enumerate the device.
    //

    WdfDeviceSetFailed((WDFDEVICE) WdfObjectContextGetObject(PdoExtension), WdfDeviceFailedAttemptRestart);

    return STATUS_SUCCESS;
}

_Use_decl_annotations_
NTSTATUS
PdoCreateDynamic(
    WDFDEVICE       _Device,
    PWDFDEVICE_INIT _DeviceInit,
    PWCHAR          _HardwareIds,
    ULONG           _SerialNo
    )
/*++

Routine Description:

    This routine creates and initialize a PDO.

Arguments:

Return Value:

    NT Status code.

--*/
{
    NTSTATUS                        Status;
    PPDO_EXTENSION                  PdoExtension = NULL;
    WDFDEVICE                       ChildDevice = NULL;
    WDF_OBJECT_ATTRIBUTES           PdoAttributes;
    WDF_DEVICE_PNP_CAPABILITIES     PnpCaps;
    WDF_DEVICE_POWER_CAPABILITIES   PowerCaps;
    DECLARE_CONST_UNICODE_STRING(   CompatId, BT_PDO_COMPATIBLE_IDS);
    DECLARE_CONST_UNICODE_STRING(   DeviceLocation, L"Serial HCI Bus - Bluetooth Function");
    DECLARE_UNICODE_STRING_SIZE(    Buffer, MAX_ID_LEN);
    DECLARE_UNICODE_STRING_SIZE(    DeviceId, MAX_ID_LEN);
    WDF_IO_QUEUE_CONFIG             QueueConfig;
    WDFQUEUE                        Queue;
    WDF_QUERY_INTERFACE_CONFIG      DeviceResetInterfaceConfig;
    DEVICE_RESET_INTERFACE_STANDARD ResetInterface;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(_Device);

    KdPrint(("Entered PdoCreateDynamic\n"));

    //
    // Set DeviceType
    //
    WdfDeviceInitSetDeviceType(_DeviceInit, FILE_DEVICE_BUS_EXTENDER);

    //
    // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
    //
    RtlInitUnicodeString(&DeviceId, _HardwareIds);

    Status = WdfPdoInitAssignDeviceID(_DeviceInit, &DeviceId);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // NOTE: same string  is used to initialize hardware id too
    //
    Status = WdfPdoInitAddHardwareID(_DeviceInit, &DeviceId);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    Status = WdfPdoInitAddCompatibleID(_DeviceInit, &CompatId );
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    Status =  RtlUnicodeStringPrintf(&Buffer, L"%02d", _SerialNo);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    Status = WdfPdoInitAssignInstanceID(_DeviceInit, &Buffer);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // Provide a description about the device. This text is usually read from
    // the device. In the case of USB device, this text comes from the string
    // descriptor. This text is displayed momentarily by the PnP manager while
    // it's looking for a matching INF. If it finds one, it uses the Device
    // Description from the INF file or the friendly name created by
    // coinstallers to display in the device manager. FriendlyName takes
    // precedence over the DeviceDesc from the INF file.
    //
    Status = RtlUnicodeStringPrintf( &Buffer,
                                     L"SerialHciBus_%02d",
                                     _SerialNo );
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // You can call WdfPdoInitAddDeviceText multiple times, adding device
    // text for multiple locales. When the system displays the text, it
    // chooses the text that matches the current locale, if available.
    // Otherwise it will use the string for the default locale.
    // The driver can specify the driver's default locale by calling
    // WdfPdoInitSetDefaultLocale.
    //
    Status = WdfPdoInitAddDeviceText(_DeviceInit,
                                    &Buffer,
                                    &DeviceLocation,
                                     0x409 );
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    WdfPdoInitSetDefaultLocale(_DeviceInit, 0x409);

    //
    // Initialize the attributes to specify the size of PDO device extension.
    // All the state information private to the PDO will be tracked here.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&PdoAttributes, PDO_EXTENSION);

    //
    // Allow to forward requests to its FDO of this bus driver by using 
    // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback.
    //    
    WdfPdoInitAllowForwardingRequestToParent(_DeviceInit);
    
    //
    // Create a framework device object to represent PDO of this bus driver. In response 
    // to this call, framework creates a WDM deviceobject.
    //
    Status = WdfDeviceCreate(&_DeviceInit, 
                             &PdoAttributes, 
                             &ChildDevice);
    
    if (!NT_SUCCESS(Status)) {
        return Status;
    }


    //
    // Note: Once the device is created successfully, framework frees the
    // _DeviceInit memory and sets the _DeviceInit to NULL. So don't
    // call any WdfDeviceInit functions after that.
    //

    //
    // Initalize the PDO extension
    //
    PdoExtension = PdoGetExtension(ChildDevice);

    RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION));

    PdoExtension->FdoExtension = FdoGetExtension(_Device);

    PdoExtension->SerialNo = _SerialNo;

    PdoExtension->ResetRecoveryType = SUPPORTED_RESET_RECOVERY_TYPE;

    //
    // Set some properties for the child device.
    //
    WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps);  // Zeros this structure (note: WdfFalse is 0)

    //
    // Bus driver sets this value to WdfFalse for this embedded device, which cannot 
    // be physically removed; its FDO must not set/override this.
    //
    PnpCaps.Removable         = WdfFalse;      

    //
    // Bus driver sets this value to WdfTrue.  FDO can override this value (when this irp is on its 
    // way up) if it determines that this device cannot be safely surprise (not orderly) removed 
    // without data loss.
    //
    PnpCaps.SurpriseRemovalOK = WdfTrue;  
    
    PnpCaps.Address  = _SerialNo;
    PnpCaps.UINumber = _SerialNo;

    WdfDeviceSetPnpCapabilities(ChildDevice, &PnpCaps);

    WDF_DEVICE_POWER_CAPABILITIES_INIT(&PowerCaps);

    PowerCaps.DeviceD1 = WdfFalse;
    PowerCaps.DeviceD2 = WdfTrue;

    PowerCaps.WakeFromD0 = WdfFalse;    
    PowerCaps.WakeFromD1 = WdfFalse;
    PowerCaps.WakeFromD2 = WdfTrue;    
    PowerCaps.WakeFromD3 = WdfTrue;    
    
    PowerCaps.DeviceWake = PowerDeviceD2;

    PowerCaps.DeviceState[PowerSystemWorking]   = PowerDeviceD0;
    PowerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
    PowerCaps.DeviceState[PowerSystemShutdown]  = PowerDeviceD3;

    WdfDeviceSetPowerCapabilities(ChildDevice, &PowerCaps);


    //
    // Configure a default queue so that requests that are not
    // configure-forwarded using WdfDeviceConfigureRequestDispatching to goto
    // other queues get dispatched here.
    //

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);

    //
    // Cannot be power managed queue (dispatch only at D0) as
    // BthMini issues BthX DDI to get version and capabilities 
    // before enter D0.  A deadlock occurs if this is power managed.
    //
    QueueConfig.PowerManaged = WdfFalse;
    
    QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl;

    Status = WdfIoQueueCreate(ChildDevice, 
                              &QueueConfig, 
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &Queue);
    DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status));    
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }       

    if (PdoExtension->ResetRecoveryType == ResetRecoveryTypeParentResetInterface) {

        //
        // Instruct WDF to simply forward a request for the reset interface to the parent stack, so
        // that it reaches the ACPI bus/filter driver. That way when the reset interface is invoked,
        // it is the parent stack that gets reset and re-enumerated.
        //

        WDF_QUERY_INTERFACE_CONFIG_INIT(&DeviceResetInterfaceConfig, NULL, &GUID_DEVICE_RESET_INTERFACE_STANDARD, NULL);
        DeviceResetInterfaceConfig.SendQueryToParentStack = TRUE;

        Status = WdfDeviceAddQueryInterface(ChildDevice, &DeviceResetInterfaceConfig);
        if (!NT_SUCCESS(Status)) {
            DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDeviceAddQueryInterface failed, %!STATUS!", Status));
            goto Cleanup;
        }

    } else if (PdoExtension->ResetRecoveryType == ResetRecoveryTypeDriverImplemented) {

        RtlZeroMemory(&ResetInterface, sizeof(ResetInterface));
        ResetInterface.Size = sizeof(ResetInterface);
        ResetInterface.Version = DEVICE_RESET_INTERFACE_VERSION;
        ResetInterface.Context = PdoExtension;

        //
        // Since this interface is expected to be used only by drivers in the same stack, reference
        // counting is not required. If there is an expectation that drivers may query for the
        // interface using a remote I/O target to this stack (unusual for this interface), reference
        // counting must be implemented.
        //
        ResetInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
        ResetInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;

        ResetInterface.SupportedResetTypes = (1 << PlatformLevelDeviceReset);
        ResetInterface.DeviceReset = PdoResetHandlerDynamic;

        WDF_QUERY_INTERFACE_CONFIG_INIT(&DeviceResetInterfaceConfig, (PINTERFACE) &ResetInterface, &GUID_DEVICE_RESET_INTERFACE_STANDARD, NULL);
        Status = WdfDeviceAddQueryInterface(ChildDevice, &DeviceResetInterfaceConfig);
        if (!NT_SUCCESS(Status))
        {
            DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDeviceAddQueryInterface failed, %!STATUS!", Status));
            goto Cleanup;
        }
    }

Cleanup:


    //
    // Call WdfDeviceInitFree if you encounter an error before the
    // device is created. Once the device is created, framework
    // NULLs the DeviceInit value.
    //
    if (!NT_SUCCESS(Status)) {
        
        DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status));        

        if(ChildDevice) {
            WdfObjectDelete(ChildDevice);
        }
    }
    else
    {
        DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status));          
    }


    return Status;
}

#endif

_Use_decl_annotations_
NTSTATUS
PdoResetHandler(
    PVOID               _InterfaceContext,
    DEVICE_RESET_TYPE   _ResetType,
    ULONG               _Flags,
    PVOID               _ResetParameters
)
/*++

Routine Description:

    This is the reset handler invoked by a driver that queries our GUID_DEVICE_RESET_INTERFACE_STANDARD interface.
    This is the version used if PDOs are created using WDF static enumeration.

Arguments:

    _InterfaceContext - This is expected to be a PPDO_EXTENSION.

    _ResetType - This is expected to be PlatformLevelDeviceReset because that is all we support.

    _Flags - Unused, because no flags are defined currently.

    _ResetParameters - Unused, because it is only used for FunctionLevelDeviceReset.

Return Value:

    NTSTATUS code.

--*/
{
    PPDO_EXTENSION  PdoExtension = (PPDO_EXTENSION) _InterfaceContext;
    WDFDEVICE       Fdo = PdoExtension->FdoExtension->WdfDevice;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(_Flags);
    UNREFERENCED_PARAMETER(_ResetParameters);

    if (_ResetType != PlatformLevelDeviceReset) {
        return STATUS_NOT_SUPPORTED;
    }

    DeviceDoPLDR(Fdo);

    //
    // The bus driver is expected to cause the device to be surprise removed as part of PLDR. For
    // many hardware buses, this happens naturally, but for a software bus like us, we need to do it
    // ourselves.
    //

    FdoRemoveOneChildDevice(Fdo, BLUETOOTH_FUNC_IDS);
    FdoCreateOneChildDevice(Fdo, BT_PDO_HARDWARE_IDS, BLUETOOTH_FUNC_IDS);

    return STATUS_SUCCESS;
}

NTSTATUS
PdoCreate(
    _In_ WDFDEVICE  _Device,
    _In_ PWSTR      _HardwareIds,
    _In_ ULONG      _SerialNo
)
/*++

Routine Description:

    This routine creates and initialize a PDO to service a Bluetooth function.

Arguments:

    _Device - A framework device object
    
    _HardwareIds - a hardware ID for this device

    -SerialNo - serial number of the child DO
    
Return Value:

    NT Status code.

--*/
{
    NTSTATUS                            Status;
    PWDFDEVICE_INIT                     DeviceInit = NULL;
    WDF_PNPPOWER_EVENT_CALLBACKS        PnpPowerCallbacks;
    PPDO_EXTENSION                      PdoExtension = NULL;
    WDFDEVICE                           ChildDevice = NULL;
    WDF_OBJECT_ATTRIBUTES               Attributes;
    WDF_DEVICE_PNP_CAPABILITIES         PnpCaps;
    WDF_DEVICE_POWER_CAPABILITIES       PowerCaps;
    UNICODE_STRING                      StaticString = {0};
    UNICODE_STRING                      DeviceId;
    DECLARE_UNICODE_STRING_SIZE(        Buffer, MAX_ID_LEN);
    UNICODE_STRING ContainerID =        {0};
    WDF_PDO_EVENT_CALLBACKS             Callbacks;
    WDF_IO_QUEUE_CONFIG                 QueueConfig;
    WDFQUEUE                            Queue;
    WDF_QUERY_INTERFACE_CONFIG          DeviceResetInterfaceConfig;
    DEVICE_RESET_INTERFACE_STANDARD     ResetInterface;

    DoTrace(LEVEL_INFO, TFLAG_PNP, (" +PdoCreate: HWID(%S), compatID(%S)", _HardwareIds, BT_PDO_COMPATIBLE_IDS));

    PAGED_CODE();

    //
    // Allocate a WDFDEVICE_INIT structure and set the properties
    // so that we can create a device object for the child.
    //
    DeviceInit = WdfPdoInitAllocate(_Device);
    if (DeviceInit == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    //
    // Set DeviceType
    //
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);

    //
    // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
    //
    RtlInitUnicodeString(&DeviceId, _HardwareIds);
    Status = WdfPdoInitAssignDeviceID(DeviceInit, &DeviceId);
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    //
    // Note: same string is used to initialize hardware id
    //
    Status = WdfPdoInitAddHardwareID(DeviceInit, &DeviceId);
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    RtlInitUnicodeString(&StaticString, BT_PDO_COMPATIBLE_IDS);
    Status = WdfPdoInitAddCompatibleID(DeviceInit, &StaticString);
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    Status =  RtlUnicodeStringPrintf(&Buffer, L"%02d", _SerialNo);
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }
    Status = WdfPdoInitAssignInstanceID(DeviceInit, &Buffer);
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    //
    // Assign the containerID for an internally connected device
    //
    Status = RtlStringFromGUID(&GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, &ContainerID);
    if (!NT_SUCCESS(Status)) {
        DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to generate the ContainerID, %!STATUS!", Status));;
        goto Cleanup;
    }

    Status = WdfPdoInitAssignContainerID(DeviceInit, &ContainerID);
    if (!NT_SUCCESS(Status)) {
        DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to assign the ContainerID, %!STATUS!", Status));
        goto Cleanup;
    }
    
    //
    // Provide a description about the device. This text is usually read from
    // the device. This text is displayed momentarily by the PnP manager while
    // it's looking for a matching INF. If it finds one, it uses the Device
    // Description from the INF file or the friendly name created by
    // coinstallers to display in the device manager. FriendlyName takes
    // precedence over the DeviceDesc from the INF file.
    //
    Status = RtlUnicodeStringPrintf(&Buffer, L"SerialHciBus_%02d", _SerialNo );
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    //
    // You can call WdfPdoInitAddDeviceText multiple times, adding device
    // text for multiple locales. When the system displays the text, it
    // chooses the text that matches the current locale, if available.
    // Otherwise it will use the string for the default locale.
    // The driver can specify the driver's default locale by calling
    // WdfPdoInitSetDefaultLocale.
    //
    RtlInitUnicodeString(&StaticString, BT_PDO_DEVICE_LOCATION);
    Status = WdfPdoInitAddDeviceText(DeviceInit,
                                     &Buffer,
                                     &StaticString,
                                     0x409);
    if (!NT_SUCCESS(Status)) {        
        goto Cleanup;
    }

    WdfPdoInitSetDefaultLocale(DeviceInit, 0x409);

    //
    // Initialize the attributes to specify the size of PDO device extension.
    // All the state information private to the PDO will be tracked here.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, PDO_EXTENSION);

    //
    // Set power callbacks to handle idle/active transition of the Bluetooth function
    //
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);  

    //
    // Register PnP callback
    //
    PnpPowerCallbacks.EvtDevicePrepareHardware = PdoDevPrepareHardware;
    PnpPowerCallbacks.EvtDeviceReleaseHardware = PdoDevReleaseHardware; 

    //
    // Register for Power callback
    //
    PnpPowerCallbacks.EvtDeviceD0Entry = PdoDevD0Entry;
    PnpPowerCallbacks.EvtDeviceD0Exit  = PdoDevD0Exit; 
    
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, 
                                           &PnpPowerCallbacks); 

    //
    // Allow to forward requests to its FDO of this bus driver by using 
    // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback.
    //    
    WdfPdoInitAllowForwardingRequestToParent(DeviceInit);

    //
    // Register to handle bus level power management (arm for wake?)
    //
    WDF_PDO_EVENT_CALLBACKS_INIT(&Callbacks);
        
    //
    // Arm the device for wake:    
    //
    // When the device is powered down, the framework calls the bus driver's 
    // EvtDeviceEnableWakeAtBus callback function at the beginning of the shutdown 
    // sequence, while the child device is still in the D0 state. In this callback 
    // function, the bus driver must do whatever is required at the bus level to 
    // enable the wake signal.
    //    
    Callbacks.EvtDeviceEnableWakeAtBus  = PdoDevEnableWakeAtBus; 

    //
    // Disarm the device for wake:        
    //
    // If the child device triggered a wake signal, the system and the framework 
    // return the device to D0. The framework calls the bus driver's 
    // EvtDeviceDisableWakeAtBus callback function during startup of the child 
    // device. In this callback function, the bus driver should do whatever is 
    // required at the bus level to disable the wake signal, so that the device 
    // can no longer trigger it. Thus, EvtDeviceDisableWakeAtBus reverses the 
    // actions of EvtDeviceEnableWakeAtBus.
    //
    Callbacks.EvtDeviceDisableWakeAtBus = PdoDevDisableWakeAtBus;

    WdfPdoInitSetEventCallbacks(DeviceInit, &Callbacks);
    
    //
    // Create a framework device object to represent PDO of this bus driver. In response 
    // to this call, framework creates a WDM deviceobject.
    //
    Status = WdfDeviceCreate(&DeviceInit, 
                             &Attributes, 
                             &ChildDevice);    
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    //
    // Note: Once the device is created successfully, framework frees the
    // DeviceInit memory and sets the DeviceInit to NULL. So don't
    // call any WdfDeviceInit functions after that.
    //

    //
    // Initalize the PDO extension
    //
    PdoExtension = PdoGetExtension(ChildDevice);

    RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION));

    PdoExtension->FdoExtension = FdoGetExtension(_Device);

    PdoExtension->SerialNo = _SerialNo;

    PdoExtension->ResetRecoveryType = SUPPORTED_RESET_RECOVERY_TYPE;

    //
    // Set PnP and Power capabilities for this child device.
    //    
    WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps);  // Zeros this structure (note: WdfFalse is 0)

    //
    // Bus driver sets this value to WdfFalse for this embedded device, which cannot 
    // be physically removed; its FDO must not set/override this.
    //
    PnpCaps.Removable         = WdfFalse;      

    //
    // Bus driver sets this value to WdfTrue.  FDO can override this value (when this irp is on its 
    // way up) if it determines that this device cannot be safely surprise (not orderly) removed 
    // without data loss.
    //
    PnpCaps.SurpriseRemovalOK = WdfTrue;    

    PnpCaps.Address  = _SerialNo;
    PnpCaps.UINumber = _SerialNo;

    WdfDeviceSetPnpCapabilities(ChildDevice, &PnpCaps);

    WDF_DEVICE_POWER_CAPABILITIES_INIT(&PowerCaps);

    PowerCaps.DeviceD1 = WdfFalse;
    PowerCaps.DeviceD2 = WdfTrue;    

    PowerCaps.WakeFromD0 = WdfFalse;    
    PowerCaps.WakeFromD1 = WdfFalse;    
    PowerCaps.WakeFromD2 = WdfTrue;
    PowerCaps.WakeFromD3 = WdfTrue;    
    
    PowerCaps.DeviceState[PowerSystemWorking]   = PowerDeviceD0;
    PowerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
    PowerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
    PowerCaps.DeviceState[PowerSystemShutdown]  = PowerDeviceD3;

    PowerCaps.DeviceWake = PowerDeviceD2;   // Lowest-powered Dx state to send wake signal to system

    WdfDeviceSetPowerCapabilities(ChildDevice, &PowerCaps);       

    //
    // Configure a default queue so that requests that are not
    // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
    // other queues get dispatched here.
    //

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);

    //
    // Cannot be power managed queue (dispatch only at D0) as
    // BthMini issues BthX DDI to get version and capabilities 
    // before enter D0.  A deadlock occurs if this is power managed.
    //
    QueueConfig.PowerManaged = WdfFalse;
    
    QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl;

    Status = WdfIoQueueCreate(ChildDevice, 
                              &QueueConfig, 
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &Queue);
    DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status));    
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

    if (PdoExtension->ResetRecoveryType == ResetRecoveryTypeParentResetInterface) {

        //
        // Instruct WDF to simply forward a request for the reset interface to the parent stack, so
        // that it reaches the ACPI bus/filter driver. That way when the reset interface is invoked,
        // it is the parent stack that gets reset and re-enumerated.
        //

        WDF_QUERY_INTERFACE_CONFIG_INIT(&DeviceResetInterfaceConfig, NULL, &GUID_DEVICE_RESET_INTERFACE_STANDARD, NULL);
        DeviceResetInterfaceConfig.SendQueryToParentStack = TRUE;

        Status = WdfDeviceAddQueryInterface(ChildDevice, &DeviceResetInterfaceConfig);
        if (!NT_SUCCESS(Status)) {
            DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDeviceAddQueryInterface failed, %!STATUS!", Status));
            goto Cleanup;
        }

    } else if (PdoExtension->ResetRecoveryType == ResetRecoveryTypeDriverImplemented) {

        RtlZeroMemory(&ResetInterface, sizeof(ResetInterface));
        ResetInterface.Size = sizeof(ResetInterface);
        ResetInterface.Version = DEVICE_RESET_INTERFACE_VERSION;
        ResetInterface.Context = PdoExtension;

        //
        // Since this interface is expected to be used only by drivers in the same stack, reference
        // counting is not required. If there is an expectation that drivers may query for the
        // interface using a remote I/O target to this stack (unusual for this interface), reference
        // counting must be implemented.
        //
        ResetInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
        ResetInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;

        ResetInterface.SupportedResetTypes = (1 << PlatformLevelDeviceReset);
        ResetInterface.DeviceReset = PdoResetHandler;

        WDF_QUERY_INTERFACE_CONFIG_INIT(&DeviceResetInterfaceConfig, (PINTERFACE) &ResetInterface, &GUID_DEVICE_RESET_INTERFACE_STANDARD, NULL);
        Status = WdfDeviceAddQueryInterface(ChildDevice, &DeviceResetInterfaceConfig);
        if (!NT_SUCCESS(Status))
        {
            DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDeviceAddQueryInterface failed, %!STATUS!", Status));
            goto Cleanup;
        }
    }

    //
    // Add this device to the FDO's collection of children.
    // After the child device is added to the static collection successfully,
    // driver must call WdfPdoMarkMissing to get the device deleted. It
    // shouldn't delete the child device directly by calling WdfObjectDelete.
    //
    Status = WdfFdoAddStaticChild(_Device, ChildDevice);
    DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfFdoAddStaticChild (%!STATUS!)", Status));
    if (!NT_SUCCESS(Status)) {
        goto Cleanup;
    }

Cleanup:
    
    //
    // Call WdfDeviceInitFree if you encounter an error before the
    // device is created. Once the device is created, framework
    // NULLs the DeviceInit value.
    //
    if (!NT_SUCCESS(Status)) {
        
        DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status));
        
        if (DeviceInit != NULL) {
            WdfDeviceInitFree(DeviceInit);
        }

        if(ChildDevice) {
            WdfObjectDelete(ChildDevice);
        }
    }
    else
    {
        DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status));          
    }

    if (NULL != ContainerID.Buffer) {
        RtlFreeUnicodeString(&ContainerID);
    }

    return Status;
}

NTSTATUS
PdoDevPrepareHardware(
    _In_  WDFDEVICE     _Device,
    _In_  WDFCMRESLIST  _ResourcesRaw,
    _In_  WDFCMRESLIST  _ResourcesTranslated     
    )
/*++
Routine Description:

    This PnP CB function take a refernce of its parent so it will not enter DxState while in S0Idle.

Arguments:

    _Device - WDF Device object

    _ResourcesRaw - (Not referenced)
    
    _ResourcesTranslated - (Not referenced)

Return Value:

    NTSTATUS

--*/     
{
    NTSTATUS Status = STATUS_SUCCESS;
    WDFDEVICE ParentDevice;    
    
    PAGED_CODE();

    UNREFERENCED_PARAMETER(_ResourcesRaw);   
    UNREFERENCED_PARAMETER(_ResourcesTranslated);     

    DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevPrepareHardware"));  

    ParentDevice = WdfPdoGetParent(_Device);

    //
    // Take a reference to avoid FDO to enter DxState in IdleS0
    //
    Status = WdfDeviceStopIdle(ParentDevice, FALSE);
    DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfDeviceStopIdle %!STATUS!", Status));  
    
    //
    // Any failure Status code should be invesigated to ensure that the reference count is balanced. 
    //
    NT_ASSERT(NT_SUCCESS(Status));

    DoTrace(LEVEL_INFO, TFLAG_PNP, ("-PdoDevPrepareHardware"));   

    return Status;
}


NTSTATUS
PdoDevReleaseHardware(
    _In_  WDFDEVICE     _Device,
    _In_  WDFCMRESLIST  _ResourcesTranslated
    )
/*++
Routine Description:

    This PnP CB function release a refcount of its parent so it can enter DxState in S0Idle.

Arguments:

    _Device - WDF Device object
    
    _ResourcesTranslated - (Not referenced)

Return Value:

    NTSTATUS

--*/    
{
    WDFDEVICE ParentDevice;       
    
    PAGED_CODE();
   
    UNREFERENCED_PARAMETER(_ResourcesTranslated);   

    DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevReleaseHardware")); 

    ParentDevice = WdfPdoGetParent(_Device);

    //
    // Release a reference to allow FDO to enter DxState in IdleS0
    //
    WdfDeviceResumeIdle(ParentDevice);    
        
    return STATUS_SUCCESS;
}



NTSTATUS
PdoDevD0Entry(
    _In_   WDFDEVICE              _Device,
    _In_   WDF_POWER_DEVICE_STATE _PreviousState
    )
/*++
Routine Description:

    This PnPPower CB function is invoked after device has entered D0 (working) state. 

Arguments:

    _Device - WDF Device object
    
    PreviousState - Previous device power state

Return Value:

    NTSTATUS

--*/    
{
    NTSTATUS       Status = STATUS_SUCCESS;    

    PAGED_CODE();   

    UNREFERENCED_PARAMETER(_Device);
    UNREFERENCED_PARAMETER(_PreviousState);    
    
    DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Entry")); 

    // 
    // Can bring the Bluetooth function back to active state
    //


    DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Entry %!STATUS!", Status));
    
    return Status;
}


NTSTATUS
PdoDevD0Exit(
    _In_   WDFDEVICE              _Device,
    _In_   WDF_POWER_DEVICE_STATE _TargetState
    )
/*++
Routine Description:

    This PnP CB function is invoked when device has exited D0 (working) state.

Arguments:

    _Device - WDF Device object
    
    _TargetState - Next device power state that it is about to enter

Return Value:

    NTSTATUS

--*/    
{
    PAGED_CODE();  

    UNREFERENCED_PARAMETER(_Device);
    UNREFERENCED_PARAMETER(_TargetState);
    
    DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Exit: D0 -> D%d", _TargetState-WdfPowerDeviceD0));

    //
    // Can prepare the Bluetooth function to enter lower power device state
    //

   
    DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Exit"));

    return STATUS_SUCCESS;
}


VOID
PdoDevDisableWakeAtBus(
    _In_  WDFDEVICE _Device
    )
/*++

Routine Description:

    This framework callback routine performs bus-level operations that disable 
    the ability of one of the bus's devices to trigger a wake-up signal.
    
Arguments:

    _Device - Framework device object

Return Value:

   VOID

--*/    
{   
    //
    // Do not mark this function pageable to potentially reduce power up time.
    //
    
    DoTrace(LEVEL_INFO, TFLAG_POWER,("<==(D)== PdoDevDisableWakeAtBus"));  

    //
    // Device specific implementation to disarm for wake
    //
    DeviceDisableWakeControl(_Device);    
}

NTSTATUS
PdoDevEnableWakeAtBus(
    _In_  WDFDEVICE          _Device,
    _In_  SYSTEM_POWER_STATE _PowerState
    ) 
/*++

Routine Description:

    This framework callback routine performs bus-level operations that enable 
    one of the bus's devices to trigger a wake-up signal.
    
Arguments:

    _Device - Framework device object

    _PowerState - identifies the system power state that the system or device will wake from. 

Return Value:

   NTSTATUS

--*/
{
    //
    // Do not mark this function pageable to potentially reduce power up time.
    //
    
    DoTrace(LEVEL_INFO, TFLAG_POWER,("==(E)==> PdoDevEnableWakeAtBus from %S", 
            _PowerState == PowerSystemWorking ? L"S0" : L"Sx"));

    //
    // Device specific implementation to arm for wake
    //
    return DeviceEnableWakeControl(_Device, _PowerState);  
}

VOID
PdoIoQuDeviceControl(
    _In_  WDFQUEUE     _Queue,
    _In_  WDFREQUEST   _Request,
    _In_  size_t       _OutputBufferLength,
    _In_  size_t       _InputBufferLength,
    _In_  ULONG        _IoControlCode
    )
/*++

Routine Description:

    This routine is the dispatch routine for device control requests.  This routine can be invoke
    at the DISPATCH level from BthPort/mini.
    
Arguments:

    _Queue - Handle to the framework queue object that is associated
            with the I/O request.
    _Request - Handle to a framework request object.

    _OutputBufferLength - length of the request's output buffer,
                        if an output buffer is available.
    _InputBufferLength - length of the request's input buffer,
                        if an input buffer is available.

    _IoControlCode - the driver-defined or system-defined I/O control code
                    (IOCTL) that is associated with the request.

Return Value:

   VOID

--*/
{
    WDFDEVICE Device = NULL;
    NTSTATUS Status = STATUS_INVALID_PARAMETER;
    WDF_REQUEST_FORWARD_OPTIONS ForwardOptions;
    WDFDEVICE ParentDevice;
    ULONG ControlCode = (_IoControlCode & 0x00003ffc) >> 2;    

    UNREFERENCED_PARAMETER(_OutputBufferLength);
    UNREFERENCED_PARAMETER(_InputBufferLength);

    DoTrace(LEVEL_INFO, TFLAG_IOCTL,("+IoDeviceControl - InBufLen:%d, OutBufLen:%d",
            (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength));
    
    switch (_IoControlCode) {  
    case IOCTL_BTHX_GET_VERSION:
    case IOCTL_BTHX_SET_VERSION: 
    case IOCTL_BTHX_QUERY_CAPABILITIES:        
    case IOCTL_BTHX_WRITE_HCI:  
    case IOCTL_BTHX_READ_HCI:           
        Device = WdfIoQueueGetDevice(_Queue);
        WDF_REQUEST_FORWARD_OPTIONS_INIT(&ForwardOptions);
        ForwardOptions.Flags = WDF_REQUEST_FORWARD_OPTION_SEND_AND_FORGET;        
        ParentDevice = WdfPdoGetParent(Device);

        //
        // Forward known IOCTLs to FDO to process
        //
        Status = WdfRequestForwardToParentDeviceIoQueue(_Request,
                                                        WdfDeviceGetDefaultQueue(ParentDevice),
                                                        &ForwardOptions);
        break;  
      
    default:
        //
        // Complete this unexptected IOCTL with default STATUS_INVALID_PARAMETER.
        //        
        DoTrace(LEVEL_ERROR, TFLAG_IOCTL,("Unexpected IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode));
        break;         
    }
    
    if (!NT_SUCCESS(Status)){
        DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d) failed %!STATUS!", _IoControlCode, ControlCode, Status));
        WdfRequestComplete(_Request, Status);
        return;
    }        

    return;
}




<|start_filename|>gnss/gnssUmdf/Device.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    device.c

Abstract:

   This file contains the device entry points and callbacks.
    
Environment:

    Windows User-Mode Driver Framework

--*/

#include "precomp.h"
#include "Trace.h"
#include "Defaults.h"
#include "Device.h"
#include "FixSession.h"
#include "FixHandler.h"
#include "Queue.h"

#include "Device.tmh"


CDevice::CDevice(
    _In_ WDFDEVICE Device
) :
    _Device(Device),
    _Queue(nullptr)
{
}

CDevice::~CDevice()
{
    if (_Queue != nullptr)
    {
        _Queue = nullptr;
    }
}

CQueue*
CDevice::GetQueue()
{
    return _Queue;
}

NTSTATUS
CDevice::OnDeviceAdd(
    _In_ WDFDRIVER /*Driver*/,
    _Inout_ PWDFDEVICE_INIT DeviceInit
)
{
    NTSTATUS status = STATUS_SUCCESS;

    WDF_PNPPOWER_EVENT_CALLBACKS powerCallbacks;
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&powerCallbacks);

    powerCallbacks.EvtDeviceD0Entry = CDevice::OnD0Entry;
    powerCallbacks.EvtDeviceD0Exit = CDevice::OnD0Exit;

    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &powerCallbacks);

    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes,
                                            CDevice);
        
    deviceAttributes.EvtCleanupCallback = CDevice::OnCleanup;
    deviceAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
    deviceAttributes.ExecutionLevel = WdfExecutionLevelPassive;

    WDFDEVICE device;
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "WdfDeviceCreate failed with Status %!STATUS!", status);
        goto Exit;
    }

    // Construct device object on the preallocated buffer using placement 'new'
    CDevice* pDevice = new (GetDeviceObject(device)) CDevice(device);

    status = pDevice->Initialize();

Exit:
    return status;
}

NTSTATUS
CDevice::Initialize()
{
    NTSTATUS status = STATUS_SUCCESS;

    status = CQueue::AddQueueToDevice(_Device, &_Queue);

    // Register GNSS Device interface. Note that this sample is not publishing a symbolic link.
    status = WdfDeviceCreateDeviceInterface(_Device,
                                            (LPGUID)&GUID_DEVINTERFACE_GNSS,
                                            nullptr);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "WdfDeviceCreateDeviceInterface failed with %!STATUS!", status);
        goto Exit;
    }

Exit:
    return status;
}

void
CDevice::OnCleanup(
    _In_ WDFOBJECT Object
)
{
    CDevice *pDevice = GetDeviceObject(Object);

    // Device object constructed using placement 'new' so explicitly invoke destructor
    pDevice->~CDevice();
}

NTSTATUS
CDevice::OnD0Entry(
    _In_ WDFDEVICE /*Device*/,
    _In_ WDF_POWER_DEVICE_STATE /*PreviousState*/
)
{
    NTSTATUS status;

    //
    // FIX ME:
    // Add further functionality.
    // Refer to power consideration from MSDN document: https://docs.microsoft.com/en-us/windows-hardware/drivers/gnss/gnss-driver-requirements
    //

    status = STATUS_SUCCESS;

    return status;
}

NTSTATUS
CDevice::OnD0Exit(
    _In_ WDFDEVICE /*Device*/,
    _In_ WDF_POWER_DEVICE_STATE /*TargetState*/
)
{
    NTSTATUS status;
    
    //
    // FIX ME:
    // Add further functionality.
    // Refer to power consideration from MSDN document: https://docs.microsoft.com/en-us/windows-hardware/drivers/gnss/gnss-driver-requirements
    //

    status = STATUS_SUCCESS;

    return status;
}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/TestMediaType.h<|end_filename|>
// Copyright (c) Microsoft Corporation
//
// Author:      <NAME>
//
// Description:
//
//      Declaration of the AudioMediaType
//      The original code is copied from MF tree. 
//
//      Note that this implementation is only useful for some unit tests that need
//      to pass bogus media types.
//
#pragma once

#include <AudioMediaType.h>

//-----------------------------------------------------------------------------
// Description:
//
//  Implements a stripped down version of IAudioMediaType. 

// Remarks:
//
//  Most methods are not implemented, because they are not needed by the 
//  processor. 
//
//  NOTE: [alper] If this is passed to third party APO's then we may be forced
//  to implement all the methods.
//
class CTestAudioMediaType : public IAudioMediaType
{
public:
    
    //--------------------------------------------------------------------------
    // Description:
    //
    //   Used during creation
    //
    // Parameters:
    //
    //     pFormat - [in] the format
    //     ppIAudioMediaType - [out] Pointer to the created IAudioMediaType.
    //
    // Return Values:
    //
    //     S_OK         On success
    //     error        Otherwise
    //
    static HRESULT Create(  const UNCOMPRESSEDAUDIOFORMAT* pFormat,
                            IAudioMediaType** ppIAudioMediaType);

    // IUnknown methods
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    STDMETHOD( QueryInterface )( REFIID riid, LPVOID *ppvObject );

    //--------------------------------------------------------------------------
    // Description:
    //
    //   Get Major Type
    //
    // Parameters:
    //
    //     pguidMajorType - [out] pointer
    //
    // Return Values:
    //
    //     E_NOTIMPL         Not supported
    //
    virtual HRESULT STDMETHODCALLTYPE GetMajorType(GUID * /* pguidMajorType */)
    {
        return E_NOTIMPL;
    }
       
    //--------------------------------------------------------------------------
    // Description:
    //
    //   Checks to see if the format is compressed.
    //
    // Parameters:
    //
    //     pfCompressed - [out] On S_OK, this is set TRUE.
    //
    // Return Values:
    //
    //     S_OK         On success
    //     E_POINTER    Bad pointer
    //
    virtual HRESULT STDMETHODCALLTYPE IsCompressedFormat(
        /* [out] */ BOOL* pfCompressed
        )
    {
        if (NULL == pfCompressed)
        {
            return( E_POINTER );
        }
        
        *pfCompressed = TRUE;

        return( S_OK );
    }

    //--------------------------------------------------------------------------
    // Description:
    //
    //   Checks to see if the media types are equal.
    //
    // Parameters:
    //
    //     pAudioMediaType - [in] Media type.
    //     pdwFlags - [out] flags
    //
    // Return Values:
    //
    //     S_OK         On success
    //     error        Otherwise
    //
    virtual HRESULT STDMETHODCALLTYPE IsEqual( IAudioMediaType *pAudioMediaType,DWORD *pdwFlags);
        
    //--------------------------------------------------------------------------
    // Description:
    //
    //   Gets the Audio Format.
    //
    // Results:
    //
    //     Returns a WAVEFORMATEX pointer.
    //
    virtual const WAVEFORMATEX *STDMETHODCALLTYPE GetAudioFormat( void);

    //--------------------------------------------------------------------------
    // Description:
    //
    //   Gets the Uncompressed Audio Format.
    //
    // Parameters:
    //
    //     pUncompressedAudioFormat - [out] gets the format.
    //
    // Return Values:
    //
    //     S_OK         On success
    //     error        Otherwise
    //
    virtual HRESULT STDMETHODCALLTYPE GetUncompressedAudioFormat( 
                        UNCOMPRESSEDAUDIOFORMAT* pUncompressedAudioFormat );
 
protected:
    CTestAudioMediaType();

protected:
    LONG                        m_cRef;
    UNCOMPRESSEDAUDIOFORMAT   m_Format;
    WAVEFORMATEXTENSIBLE        m_Wfx;
};

//-----------------------------------------------------------------------------
// Called on creation
inline CTestAudioMediaType::CTestAudioMediaType()
{
    m_cRef             = 0;
} // CTestAudioMediaType



<|start_filename|>gnss/gnssUmdf/Trace.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    Trace.h

Abstract:

    This module contains the local type definitions for the Umdf Gnss Driver.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

//
// Define the tracing flags.
//

#define WPP_CONTROL_GUIDS                                              \
    WPP_DEFINE_CONTROL_GUID(                                           \
        GnssUmdfTraceGuid, (ceb3bf01,80e4,4dc7,b3bf,f6b5279eb8c4),     \
                                                                       \
        WPP_DEFINE_BIT(MYDRIVER_ALL_INFO)                              \
        WPP_DEFINE_BIT(TRACE_DRIVER)                                   \
        WPP_DEFINE_BIT(TRACE_DEVICE)                                   \
        WPP_DEFINE_BIT(TRACE_QUEUE)                                    \
        WPP_DEFINE_BIT(TRACE_FIX)                                      \
        )

#define WPP_FLAG_LEVEL_LOGGER(flag, level)                             \
    WPP_LEVEL_LOGGER(flag)

#define WPP_FLAG_LEVEL_ENABLED(flag, level)                            \
    (WPP_LEVEL_ENABLED(flag) &&                                        \
     WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)

#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags)                              \
           WPP_LEVEL_LOGGER(flags)
               
#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags)                            \
           (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)

//
// This comment block is scanned by the trace preprocessor to define our
// Trace function.
//
// begin_wpp config
// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...);
// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...);
// end_wpp

//
//
// Driver specific #defines
//
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
    #define MYDRIVER_TRACING_ID      L"Microsoft\\UMDF2.0\\gnssUmdf V1.0"
#endif


<|start_filename|>audio/sysvad/APO/AecApo/AecApoDll.cpp<|end_filename|>
//
// AecApoDll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Author:
//
// Description:
//
// AecApoDll.cpp : Implementation of DLL Exports.

#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsync.h>
#include <mmreg.h>

#include "resource.h"
#include "AecApoDll.h"
#include <AecApo.h>

#include <AecApoDll_i.c>


//-------------------------------------------------------------------------
// Array of APO_REG_PROPERTIES structures implemented in this module.
// Each new APO implementation will be added to this array.
//
APO_REG_PROPERTIES const *gCoreAPOs[] =
{
    &CAecApoMFX::sm_RegProperties.m_Properties
};

// {secret}
class CAecApoDllModule : public CAtlDllModuleT< CAecApoDllModule >
{
public :
    DECLARE_LIBID(LIBID_AecApoDlllib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_AECAPODLL, "{1314DF8C-99BA-4E06-8ABD-CB38614EDFF7}")

};

// {secret}
CAecApoDllModule _AtlModule;


// {secret}
extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
    {
    }
    // do necessary cleanup only if the DLL is being unloaded dynamically
    else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved))
    {
    }

    return _AtlModule.DllMain(dwReason, lpReserved);
}


// {secret}
STDAPI DllCanUnloadNow(void)
{
    return _AtlModule.DllCanUnloadNow();
}


// {secret}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
{
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}


<|start_filename|>storage/miniports/storahci/src/hbastat.h<|end_filename|>
/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    hbastat.h

Abstract:

    

Notes:

Revision History:

--*/

#pragma once

BOOLEAN
AhciAdapterReset( 
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    );

VOID
AhciCOMRESET(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PAHCI_PORT Px
    );

BOOLEAN
P_NotRunning(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PAHCI_PORT Px
    );

VOID
AhciAdapterRunAllPorts(
    _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension
    );

VOID
RunNextPort(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    );

VOID
P_Running_StartAttempt(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    );

BOOLEAN
P_Running(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

HW_TIMER_EX P_Running_Callback;

VOID
P_Running_WaitOnDET(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

VOID
P_Running_WaitWhileDET1(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

VOID
P_Running_WaitOnDET3(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

VOID
P_Running_WaitOnFRE(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

VOID
P_Running_WaitOnBSYDRQ(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

VOID
P_Running_StartFailed(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN TimerCallbackProcess
    );

BOOLEAN
AhciPortReset (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN CompleteAllRequests
    );

VOID
AhciPortErrorRecovery(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension
    );



<|start_filename|>audio/sysvad/savedata.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    savedata.cpp

Abstract:

    Implementation of SYSVAD data saving class.

    To save the playback data to disk, this class maintains a circular data
    buffer, associated frame structures and worker items to save frames to
    disk.
    Each frame structure represents a portion of buffer. When that portion
    of frame is full, a workitem is scheduled to save it to disk.



--*/
#pragma warning (disable : 4127)
#pragma warning (disable : 26165)

#include <sysvad.h>
#include "savedata.h"
#include <ntstrsafe.h>   // This is for using RtlStringcbPrintf

#define SAVEDATA_POOLTAG  'TDVS'
#define SAVEDATA_POOLTAG1 '1DVS'
#define SAVEDATA_POOLTAG2 '2DVS'
#define SAVEDATA_POOLTAG3 '3DVS'
#define SAVEDATA_POOLTAG4 '4DVS'
#define SAVEDATA_POOLTAG5 '5DVS'
#define SAVEDATA_POOLTAG6 '6DVS'
#define SAVEDATA_POOLTAG7 '7DVS'

//=============================================================================
// Defines
//=============================================================================
#define RIFF_TAG                    0x46464952;
#define WAVE_TAG                    0x45564157;
#define FMT__TAG                    0x20746D66;
#define DATA_TAG                    0x61746164;

#define DEFAULT_FRAME_COUNT         4
#define DEFAULT_FRAME_SIZE          PAGE_SIZE * 4 
#define DEFAULT_BUFFER_SIZE         DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT

#define DEFAULT_FILE_NAME           L"\\DosDevices\\C:\\STREAM"
#define OSDATA_FILE_NAME            L"\\DosDevices\\O:\\STREAM"
#define OFFLOAD_FILE_NAME           L"OFFLOAD"
#define HOST_FILE_NAME              L"HOST"

#define MAX_WORKER_ITEM_COUNT       15

//=============================================================================
// Statics
//=============================================================================
ULONG CSaveData::m_ulStreamId = 0;
ULONG CSaveData::m_ulOffloadStreamId = 0;

#pragma code_seg("PAGE")
//=============================================================================
// CSaveData
//=============================================================================

//=============================================================================
CSaveData::CSaveData()
:   m_pDataBuffer(NULL),
    m_FileHandle(NULL),
    m_ulFrameCount(DEFAULT_FRAME_COUNT),
    m_ulBufferSize(DEFAULT_BUFFER_SIZE),
    m_ulFrameSize(DEFAULT_FRAME_SIZE),
    m_ulBufferOffset(0),
    m_ulFrameIndex(0),
    m_fFrameUsed(NULL),
    m_waveFormat(NULL),
    m_pFilePtr(NULL),
    m_fWriteDisabled(FALSE),
    m_bInitialized(FALSE)
{
    PAGED_CODE();

    m_FileHeader.dwRiff           = RIFF_TAG;
    m_FileHeader.dwFileSize       = 0;
    m_FileHeader.dwWave           = WAVE_TAG;
    m_FileHeader.dwFormat         = FMT__TAG;
    m_FileHeader.dwFormatLength   = sizeof(WAVEFORMATEX);

    m_DataHeader.dwData           = DATA_TAG;
    m_DataHeader.dwDataLength     = 0;

    RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes));
} // CSaveData

//=============================================================================
CSaveData::~CSaveData()
{
    PAGED_CODE();

    DPF_ENTER(("[CSaveData::~CSaveData]"));

    // Update the wave header in data file with real file size.
    //
    if(m_pFilePtr)
    {
        m_FileHeader.dwFileSize =
            (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD);
        m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart -
                                     sizeof(m_FileHeader)        -
                                     m_FileHeader.dwFormatLength -
                                     sizeof(m_DataHeader);

        if (STATUS_SUCCESS == KeWaitForSingleObject
            (
                &m_FileSync,
                Executive,
                KernelMode,
                FALSE,
                NULL
            ))
        {
            if (NT_SUCCESS(FileOpen(FALSE)))
            {
                FileWriteHeader();

                FileClose();
            }

            KeReleaseMutex(&m_FileSync, FALSE);
        }
    }

    if (m_waveFormat)
    {
        ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1);
        m_waveFormat = NULL;
    }

    if (m_fFrameUsed)
    {
        ExFreePoolWithTag(m_fFrameUsed, SAVEDATA_POOLTAG2);
        m_fFrameUsed = NULL;
        // NOTE : Do not release m_pFilePtr.
    }

    if (m_FileName.Buffer)
    {
        ExFreePoolWithTag(m_FileName.Buffer, SAVEDATA_POOLTAG3);
        m_FileName.Buffer = NULL;
    }

    if (m_pDataBuffer)
    {
        ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4);
        m_pDataBuffer = NULL;
    }
} // CSaveData

//=============================================================================
void
CSaveData::DestroyWorkItems
(
    void
)
{
    PAGED_CODE();

    if (m_pWorkItems)
    {
        for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
        {
            if (m_pWorkItems[i].WorkItem!=NULL)
            {
                IoFreeWorkItem(m_pWorkItems[i].WorkItem);
                m_pWorkItems[i].WorkItem = NULL;
            }
        }
        ExFreePoolWithTag(m_pWorkItems, SAVEDATA_POOLTAG);
        m_pWorkItems = NULL;
    }

} // DestroyWorkItems

//=============================================================================
void
CSaveData::Disable
(
    _In_ BOOL                  fDisable
)
{
    PAGED_CODE();

    m_fWriteDisabled = fDisable;
} // Disable

//=============================================================================
NTSTATUS
CSaveData::FileClose(void)
{
    PAGED_CODE();

    NTSTATUS                    ntStatus = STATUS_SUCCESS;

    if (m_FileHandle)
    {
        ntStatus = ZwClose(m_FileHandle);
        m_FileHandle = NULL;
    }

    return ntStatus;
} // FileClose

//=============================================================================
NTSTATUS
CSaveData::FileOpen
(
    _In_  BOOL                  fOverWrite
)
{
    PAGED_CODE();

    NTSTATUS                    ntStatus = STATUS_SUCCESS;
    IO_STATUS_BLOCK             ioStatusBlock;

    if( FALSE == m_bInitialized )
    {
        return STATUS_UNSUCCESSFUL;
    }

    if(!m_FileHandle)
    {
        ntStatus =
            ZwCreateFile
            (
                &m_FileHandle,
                GENERIC_WRITE | SYNCHRONIZE,
                &m_objectAttributes,
                &ioStatusBlock,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                0,
                fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF,
                FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0
            );
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]"));
        }
    }

    return ntStatus;
} // FileOpen

//=============================================================================
NTSTATUS
CSaveData::FileWrite
(
    _In_reads_bytes_(ulDataSize)    PBYTE   pData,
    _In_                            ULONG   ulDataSize
)
{
    PAGED_CODE();

    ASSERT(pData);
    ASSERT(m_pFilePtr);

    NTSTATUS                    ntStatus;

    if (m_FileHandle)
    {
        IO_STATUS_BLOCK         ioStatusBlock;

        ntStatus = ZwWriteFile( m_FileHandle,
                                NULL,
                                NULL,
                                NULL,
                                &ioStatusBlock,
                                pData,
                                ulDataSize,
                                m_pFilePtr,
                                NULL);

        if (NT_SUCCESS(ntStatus))
        {
            ASSERT(ioStatusBlock.Information == ulDataSize);

            m_pFilePtr->QuadPart += ulDataSize;
        }
        else
        {
            DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]"));
        }
    }
    else
    {
        DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]"));
        ntStatus = STATUS_INVALID_HANDLE;
    }

    return ntStatus;
} // FileWrite

//=============================================================================
NTSTATUS
CSaveData::FileWriteHeader(void)
{
    PAGED_CODE();

    NTSTATUS                    ntStatus;

    if (m_FileHandle && m_waveFormat)
    {
        IO_STATUS_BLOCK         ioStatusBlock;

        m_pFilePtr->QuadPart = 0;

        m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ?
                                        sizeof( PCMWAVEFORMAT ) :
                                        sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize;

        ntStatus = ZwWriteFile( m_FileHandle,
                                NULL,
                                NULL,
                                NULL,
                                &ioStatusBlock,
                                &m_FileHeader,
                                sizeof(m_FileHeader),
                                m_pFilePtr,
                                NULL);
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]"));
        }

        m_pFilePtr->QuadPart += sizeof(m_FileHeader);

        ntStatus = ZwWriteFile( m_FileHandle,
                                NULL,
                                NULL,
                                NULL,
                                &ioStatusBlock,
                                m_waveFormat,
                                m_FileHeader.dwFormatLength,
                                m_pFilePtr,
                                NULL);
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]"));
        }

        m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength;

        ntStatus = ZwWriteFile( m_FileHandle,
                                NULL,
                                NULL,
                                NULL,
                                &ioStatusBlock,
                                &m_DataHeader,
                                sizeof(m_DataHeader),
                                m_pFilePtr,
                                NULL);
        if (!NT_SUCCESS(ntStatus))
        {
            DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]"));
        }

        m_pFilePtr->QuadPart += sizeof(m_DataHeader);
    }
    else
    {
        DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]"));
        ntStatus = STATUS_INVALID_HANDLE;
    }

    return ntStatus;
} // FileWriteHeader
NTSTATUS
CSaveData::SetDeviceObject
(
    _In_  PDEVICE_OBJECT        DeviceObject
)
{
    PAGED_CODE();

    ASSERT(DeviceObject);

    NTSTATUS                    ntStatus = STATUS_SUCCESS;
    
    m_pDeviceObject = DeviceObject;
    return ntStatus;
}

PDEVICE_OBJECT
CSaveData::GetDeviceObject
(
    void
)
{
    PAGED_CODE();

    return m_pDeviceObject;
}

#pragma code_seg()
//=============================================================================
PSAVEWORKER_PARAM
CSaveData::GetNewWorkItem
(
    void
)
{
    LARGE_INTEGER               timeOut = { 0 };
    NTSTATUS                    ntStatus;

    for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
    {
        ntStatus =
            KeWaitForSingleObject
            (
                &m_pWorkItems[i].EventDone,
                Executive,
                KernelMode,
                FALSE,
                &timeOut
            );
        if (STATUS_SUCCESS == ntStatus)
        {
            if (m_pWorkItems[i].WorkItem)
                return &(m_pWorkItems[i]);
            else
                return NULL;
        }
    }

    return NULL;
} // GetNewWorkItem
#pragma code_seg("PAGE")

//=============================================================================
NTSTATUS
CSaveData::Initialize
(
    _In_ BOOL       _bOffloaded
)
{
    PAGED_CODE();

    NTSTATUS    ntStatus = STATUS_SUCCESS;
    WCHAR       szTemp[MAX_PATH];
    size_t      cLen;
    OBJECT_ATTRIBUTES objectAttributes; 
    UNICODE_STRING    osDataVolumeString;
    HANDLE            osDataFileHandle = NULL;     
    IO_STATUS_BLOCK   ioStatusBlock;

    DPF_ENTER(("[CSaveData::Initialize]"));

    if (_bOffloaded)
    {
        m_ulOffloadStreamId++;
    }
    else
    {
        m_ulStreamId++;
    }

    // Probe if OSData volume exists.
    //
    RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_probe.txt", OSDATA_FILE_NAME);
    RtlInitUnicodeString(&osDataVolumeString, szTemp);
    InitializeObjectAttributes
    (
        &objectAttributes,
        &osDataVolumeString,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL
    );

    ntStatus =
        ZwCreateFile
        (
            &osDataFileHandle,
            GENERIC_WRITE | SYNCHRONIZE,
            &objectAttributes,
            &ioStatusBlock,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            0,
            FILE_OVERWRITE_IF,
            FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
            NULL,
            0
        );
    if (NT_SUCCESS(ntStatus))
    {
        ZwClose(osDataFileHandle);
    }

    // Allocate data file name.
    //
    RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_%s_%d.wav", NT_SUCCESS(ntStatus) ? OSDATA_FILE_NAME : DEFAULT_FILE_NAME, _bOffloaded ? OFFLOAD_FILE_NAME : HOST_FILE_NAME, _bOffloaded ? m_ulOffloadStreamId : m_ulStreamId);
    m_FileName.Length = 0;
    ntStatus = RtlStringCchLengthW (szTemp, sizeof(szTemp)/sizeof(szTemp[0]), &cLen);
    if (NT_SUCCESS(ntStatus))
    {
        m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) +  sizeof(WCHAR));//convert to wchar and add room for NULL
        m_FileName.Buffer = (PWSTR)
            ExAllocatePool2
            (
                POOL_FLAG_PAGED,
                m_FileName.MaximumLength,
                SAVEDATA_POOLTAG3
            );
        if (!m_FileName.Buffer)
        {
            DPF(D_TERSE, ("[Could not allocate memory for FileName]"));
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    // Allocate memory for data buffer.
    //
    if (NT_SUCCESS(ntStatus))
    {
        RtlStringCbCopyW(m_FileName.Buffer, m_FileName.MaximumLength, szTemp);
        m_FileName.Length = (USHORT)wcslen(m_FileName.Buffer) * sizeof(WCHAR);
        DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer));

        m_pDataBuffer = (PBYTE)
            ExAllocatePool2
            (
                POOL_FLAG_NON_PAGED,
                m_ulBufferSize,
                SAVEDATA_POOLTAG4
            );
        if (!m_pDataBuffer)
        {
            DPF(D_TERSE, ("[Could not allocate memory for Saving Data]"));
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    // Allocate memory for frame usage flags and m_pFilePtr.
    //
    if (NT_SUCCESS(ntStatus))
    {
        m_fFrameUsed = (PBOOL)
            ExAllocatePool2
            (
                POOL_FLAG_NON_PAGED,
                m_ulFrameCount * sizeof(BOOL) +
                sizeof(LARGE_INTEGER),
                SAVEDATA_POOLTAG2
            );
        if (!m_fFrameUsed)
        {
            DPF(D_TERSE, ("[Could not allocate memory for frame flags]"));
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    // Initialize the spinlock to synchronize access to the frames
    //
    KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ;

    // Initialize the file mutex
    //
    KeInitializeMutex( &m_FileSync, 1 ) ;

    // Open the data file.
    //
    if (NT_SUCCESS(ntStatus))
    {
        // m_fFrameUsed has additional memory to hold m_pFilePtr
        //
        m_pFilePtr = (PLARGE_INTEGER)
            (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL));

        // Create data file.
        InitializeObjectAttributes
        (
            &m_objectAttributes,
            &m_FileName,
            OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
            NULL,
            NULL
        );

        m_bInitialized = TRUE;

        // Write wave header information to data file.
        ntStatus = KeWaitForSingleObject
            (
                &m_FileSync,
                Executive,
                KernelMode,
                FALSE,
                NULL
            );

        if (STATUS_SUCCESS == ntStatus)
        {
            ntStatus = FileOpen(TRUE);
            if (NT_SUCCESS(ntStatus))
            {
                ntStatus = FileWriteHeader();

                FileClose();
            }

            KeReleaseMutex( &m_FileSync, FALSE );
        }
    }

    return ntStatus;
} // Initialize

//=============================================================================
NTSTATUS
CSaveData::InitializeWorkItems
(
    _In_  PDEVICE_OBJECT        DeviceObject
)
{
    PAGED_CODE();

    ASSERT(DeviceObject);

    NTSTATUS                    ntStatus = STATUS_SUCCESS;

    DPF_ENTER(("[CSaveData::InitializeWorkItems]"));

    if (m_pWorkItems != NULL)
    {
        return ntStatus;
    }

    m_pWorkItems = (PSAVEWORKER_PARAM)
        ExAllocatePool2
        (
            POOL_FLAG_NON_PAGED,
            sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT,
            SAVEDATA_POOLTAG
        );
    if (m_pWorkItems)
    {
        for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
        {

            m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject);
            if(m_pWorkItems[i].WorkItem == NULL)
            {
              return STATUS_INSUFFICIENT_RESOURCES;
            }
            KeInitializeEvent
            (
                &m_pWorkItems[i].EventDone,
                NotificationEvent,
                TRUE
            );
        }
    }
    else
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    return ntStatus;
} // InitializeWorkItems

//=============================================================================

IO_WORKITEM_ROUTINE SaveFrameWorkerCallback;

VOID
SaveFrameWorkerCallback
(
    _In_        PDEVICE_OBJECT         pDeviceObject, 
    _In_opt_    PVOID                  Context
)
{
    UNREFERENCED_PARAMETER(pDeviceObject);

    PAGED_CODE();

    ASSERT(Context);

    PSAVEWORKER_PARAM           pParam = (PSAVEWORKER_PARAM) Context;
    PCSaveData                  pSaveData;
    
    if (NULL == pParam)
    {
        // This is completely unexpected, assert here.
        //
        ASSERT(pParam);
        return;
    }

    DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo));

    ASSERT(pParam->pSaveData);
    ASSERT(pParam->pSaveData->m_fFrameUsed);

    if (pParam->WorkItem)
    {
        pSaveData = pParam->pSaveData;

        if (STATUS_SUCCESS == KeWaitForSingleObject
            (
                &pSaveData->m_FileSync,
                Executive,
                KernelMode,
                FALSE,
                NULL
            ))
        {
            if (NT_SUCCESS(pSaveData->FileOpen(FALSE)))
            { 
                pSaveData->FileWrite(pParam->pData, pParam->ulDataSize);
                pSaveData->FileClose();
            }
            InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE );

            KeReleaseMutex( &pSaveData->m_FileSync, FALSE );
        }
    }

    KeSetEvent(&pParam->EventDone, 0, FALSE);
} // SaveFrameWorkerCallback

//=============================================================================
NTSTATUS
CSaveData::SetDataFormat
(
    _In_ PKSDATAFORMAT          pDataFormat
)
{
    PAGED_CODE();
    NTSTATUS                    ntStatus = STATUS_SUCCESS;
 
    DPF_ENTER(("[CSaveData::SetDataFormat]"));

    ASSERT(pDataFormat);

    PWAVEFORMATEX pwfx = NULL;

    if (IsEqualGUIDAligned(pDataFormat->Specifier,
        KSDATAFORMAT_SPECIFIER_DSOUND))
    {
        pwfx =
            &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx);
    }
    else if (IsEqualGUIDAligned(pDataFormat->Specifier,
        KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
    {
        pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx;
    }

    if (pwfx)
    {
        // Free the previously allocated waveformat
        if (m_waveFormat)
        {
            ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1);
        }

        m_waveFormat = (PWAVEFORMATEX)
            ExAllocatePool2
            (
                POOL_FLAG_NON_PAGED,
                (pwfx->wFormatTag == WAVE_FORMAT_PCM) ?
                sizeof( PCMWAVEFORMAT ) :
                sizeof( WAVEFORMATEX ) + pwfx->cbSize,
                SAVEDATA_POOLTAG1
            );

        if(m_waveFormat)
        {
            RtlCopyMemory( m_waveFormat,
                           pwfx,
                           (pwfx->wFormatTag == WAVE_FORMAT_PCM) ?
                           sizeof( PCMWAVEFORMAT ) :
                           sizeof( WAVEFORMATEX ) + pwfx->cbSize);
        }
        else
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }
    }
    return ntStatus;
} // SetDataFormat

//=============================================================================
NTSTATUS
CSaveData::SetMaxWriteSize
(
    _In_ ULONG ulMaxWriteSize
)
{
    PAGED_CODE();
    
    NTSTATUS    ntStatus    = STATUS_SUCCESS;
    ULONG       bufferSize  = 0;
    PBYTE       buffer      = NULL;
 
    DPF_ENTER(("[CSaveData::SetMaxWriteSize]"));

    // 
    // Compute new buffer size.
    //
    ntStatus = RtlULongMult(ulMaxWriteSize, DEFAULT_FRAME_COUNT, &bufferSize);
    if (!NT_SUCCESS(ntStatus))
    {
        DPF(D_TERSE, ("[Could not allocate memory for Saving Data, MaxWriteSize %u is too big]", ulMaxWriteSize));
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Done;
    }

    //
    // Alloc memory for buffer.
    //
    buffer = (PBYTE)
        ExAllocatePool2
        (
            POOL_FLAG_NON_PAGED,
            bufferSize,
            SAVEDATA_POOLTAG4
        );
    if (!buffer)
    {
        DPF(D_TERSE, ("[Could not allocate memory for Saving Data]"));
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Done;
    }

    //
    // Free old one.
    //
    if (m_pDataBuffer)
    {
        ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4);
        m_pDataBuffer = NULL;
    }

    //
    // Init new buffer settings.
    //
    m_pDataBuffer  = buffer;
    m_ulBufferSize = bufferSize;
    m_ulFrameSize  = ulMaxWriteSize;
    
    ntStatus = STATUS_SUCCESS;

Done:
    return ntStatus;
} // SetDataFormat

//=============================================================================
void
CSaveData::ReadData
(
    _Inout_updates_bytes_all_(ulByteCount)  PBYTE   pBuffer,
    _In_                                    ULONG   ulByteCount
)
{
    UNREFERENCED_PARAMETER(pBuffer);
    UNREFERENCED_PARAMETER(ulByteCount);

    PAGED_CODE();

    // Not implemented yet.
} // ReadData

//=============================================================================
#pragma code_seg()
void
CSaveData::SaveFrame
(
    _In_ ULONG                  ulFrameNo,
    _In_ ULONG                  ulDataSize
)
{
    PSAVEWORKER_PARAM           pParam = NULL;

    DPF_ENTER(("[CSaveData::SaveFrame]"));

    pParam = GetNewWorkItem();
    if (pParam)
    {
        pParam->pSaveData = this;
        pParam->ulFrameNo = ulFrameNo;
        pParam->ulDataSize = ulDataSize;
        pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize;
        KeResetEvent(&pParam->EventDone);
        IoQueueWorkItem(pParam->WorkItem, SaveFrameWorkerCallback,
                        CriticalWorkQueue, (PVOID)pParam);
    }
} // SaveFrame
#pragma code_seg("PAGE")
//=============================================================================
void
CSaveData::WaitAllWorkItems
(
    void
)
{
    PAGED_CODE();

    DPF_ENTER(("[CSaveData::WaitAllWorkItems]"));

    // Save the last partially-filled frame
    if (m_ulBufferOffset > m_ulFrameIndex * m_ulFrameSize)
    {
        ULONG size;
        
        size = m_ulBufferOffset - m_ulFrameIndex * m_ulFrameSize;
        SaveFrame(m_ulFrameIndex, size);
    }
    
    for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
    {
        DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i));
        KeWaitForSingleObject
        (
            &(m_pWorkItems[i].EventDone),
            Executive,
            KernelMode,
            FALSE,
            NULL
        );
    }
} // WaitAllWorkItems

#pragma code_seg()
//=============================================================================
void
CSaveData::WriteData
(
    _In_reads_bytes_(ulByteCount)   PBYTE   pBuffer,
    _In_                            ULONG   ulByteCount
)
{
    ASSERT(pBuffer);

    BOOL                        fSaveFrame = FALSE;
    ULONG                       ulSaveFrameIndex = 0;
    KIRQL                       oldIrql;

    // If stream writing is disabled, then exit.
    //
    if (m_fWriteDisabled)
    {
        return;
    }

    DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount));

    if( 0 == ulByteCount )
    {
        return;
    }

    // The logic below assumes that write size is <= than frame size.
    if (ulByteCount > m_ulFrameSize)
    {
        ulByteCount = m_ulFrameSize;
    }
        
    // Check to see if this frame is available.
    KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql);
    if (!m_fFrameUsed[m_ulFrameIndex])
    {
        KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );

        ULONG ulWriteBytes = ulByteCount;

        if( (m_ulBufferSize - m_ulBufferOffset) < ulWriteBytes )
        {
            ulWriteBytes = m_ulBufferSize - m_ulBufferOffset;
        }

        RtlCopyMemory(m_pDataBuffer + m_ulBufferOffset, pBuffer, ulWriteBytes);
        m_ulBufferOffset += ulWriteBytes;

        // Check to see if we need to save this frame
        if (m_ulBufferOffset >= ((m_ulFrameIndex + 1) * m_ulFrameSize))
        {
            fSaveFrame = TRUE;
        }

        // Loop the buffer, if we reached the end.
        if (m_ulBufferOffset == m_ulBufferSize)
        {
            fSaveFrame = TRUE;
            m_ulBufferOffset = 0;
        }

        if (fSaveFrame)
        {
            InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFrameIndex]), TRUE );
            ulSaveFrameIndex = m_ulFrameIndex;
            m_ulFrameIndex = (m_ulFrameIndex + 1) % m_ulFrameCount;
        }

        // Write the left over if the next frame is available.
        if (ulWriteBytes != ulByteCount)
        {
            KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql );
            if (!m_fFrameUsed[m_ulFrameIndex])
            {
                KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );
                RtlCopyMemory
                (
                    m_pDataBuffer + m_ulBufferOffset,
                    pBuffer + ulWriteBytes,
                    ulByteCount - ulWriteBytes
                );
                
                m_ulBufferOffset += ulByteCount - ulWriteBytes;
            }
            else
            {
                KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql);
                DPF(D_BLAB, ("[Frame overflow, next frame is in use]"));
            }
        }

        if (fSaveFrame)
        {
            SaveFrame(ulSaveFrameIndex, m_ulFrameSize);
        }
    }
    else
    {
        KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );
        DPF(D_BLAB, ("[Frame %d is in use]", m_ulFrameIndex));
    }

} // WriteData




<|start_filename|>audio/sysvad/EndpointsCommon/a2dphpspeakerwavtable.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    s2dphppeakerwavtable.h

Abstract:

    Declaration of wave miniport tables for the A2DP Headphone speaker (external).

--*/

#ifndef _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_
#define _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_

//
// Function prototypes.
//
NTSTATUS PropertyHandler_A2dpHpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest);



#define A2DPHPSPEAKER_DEVICE_MAX_CHANNELS                 2       // Max Channels.
                                                         
#define A2DPHPSPEAKER_HOST_MAX_CHANNELS                   2       // Max Channels.
#define A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE            16      // Min Bits Per Sample
#define A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE            16      // Max Bits Per Sample
#define A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE                24000   // Min Sample Rate
#define A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE                96000   // Max Sample Rate
                                                         
#define A2DPHPSPEAKER_OFFLOAD_MAX_CHANNELS                2       // Max Channels.
#define A2DPHPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE         16      // Min Bits Per Sample
#define A2DPHPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE         16      // Max Bits Per Sample
#define A2DPHPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE             44100   // Min Sample Rate
#define A2DPHPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE             48000   // Max Sample Rate
                                                         
#define A2DPHPSPEAKER_LOOPBACK_MAX_CHANNELS               A2DPHPSPEAKER_HOST_MAX_CHANNELS          // Must be equal to host pin's Max Channels.
#define A2DPHPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE        A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE   // Must be equal to host pin's Min Bits Per Sample
#define A2DPHPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE        A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE   // Must be equal to host pin's Max Bits Per Sample
#define A2DPHPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE            A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE       // Must be equal to host pin's Min Sample Rate
#define A2DPHPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE            A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE       // Must be equal to host pin's Max Sample Rate

#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS          2       // Max Channels.
#define A2DPHPSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE   16      // Min Bits Per Sample
#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE   16      // Max Bits Per Sample
#define A2DPHPSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE       44100   // Min Sample Rate
#define A2DPHPSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE       44100   // Max Sample Rate

//
// Max # of pin instances.
//
#define A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS             6
#define A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS            MAX_INPUT_OFFLOAD_STREAMS
#define A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS          MAX_OUTPUT_LOOPBACK_STREAMS

//=============================================================================
static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerAudioEngineSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                24000,
                96000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                88200,
                352800,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                96000,
                384000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerHostPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                24000,
                96000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                32000,
                128000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                88200,
                352800,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 5
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                96000,
                384000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

static 
KSDATAFORMAT_WAVEFORMATEXTENSIBLE A2dpHpSpeakerOffloadPinSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

//
// Supported modes (only on streaming pins).
//
static
MODE_AND_DEFAULT_FORMAT A2dpHpSpeakerHostPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[0].DataFormat // 24KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION,
        &A2dpHpSpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz
    }
};

static
MODE_AND_DEFAULT_FORMAT A2dpHpSpeakerOffloadPinSupportedDeviceModes[] =
{
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,
        &A2dpHpSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz
    },
    {
        STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA,
        &A2dpHpSpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz
    }
};

//
// The entries here must follow the same order as the filter's pin
// descriptor array.
//
static 
PIN_DEVICE_FORMATS_AND_MODES A2dpHpSpeakerPinDeviceFormatsAndModes[] = 
{
    {
        SystemRenderPin,
        A2dpHpSpeakerHostPinSupportedDeviceFormats,
        SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceFormats),
        A2dpHpSpeakerHostPinSupportedDeviceModes,
        SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceModes)
    },
    {
        OffloadRenderPin,
        A2dpHpSpeakerOffloadPinSupportedDeviceFormats,
        SIZEOF_ARRAY(A2dpHpSpeakerOffloadPinSupportedDeviceFormats),
        A2dpHpSpeakerOffloadPinSupportedDeviceModes,
        SIZEOF_ARRAY(A2dpHpSpeakerOffloadPinSupportedDeviceModes),
    },
    {
        RenderLoopbackPin,
        A2dpHpSpeakerHostPinSupportedDeviceFormats,   // Must support all the formats supported by host pin
        SIZEOF_ARRAY(A2dpHpSpeakerHostPinSupportedDeviceFormats),
        NULL,   // loopback doesn't support modes.
        0
    },
    {
        BridgePin,
        NULL,
        0,
        NULL,
        0
    },
    {
        NoPin,      // For convenience, offload engine device formats appended here
        A2dpHpSpeakerAudioEngineSupportedDeviceFormats,
        SIZEOF_ARRAY(A2dpHpSpeakerAudioEngineSupportedDeviceFormats),
        NULL,       // no modes for this entry.
        0
    }
};

//=============================================================================
static
KSDATARANGE_AUDIO A2dpHpSpeakerPinDataRangesStream[] =
{
    { // 0
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        A2DPHPSPEAKER_HOST_MAX_CHANNELS,           
        A2DPHPSPEAKER_HOST_MIN_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_HOST_MAX_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_HOST_MIN_SAMPLE_RATE,            
        A2DPHPSPEAKER_HOST_MAX_SAMPLE_RATE             
    },
    { // 1
        {
            sizeof(KSDATARANGE_AUDIO),
            KSDATARANGE_ATTRIBUTES,         // An attributes list follows this data range
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        A2DPHPSPEAKER_OFFLOAD_MAX_CHANNELS,           
        A2DPHPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE,
        A2DPHPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE
    },
    { // 2
        {
            sizeof(KSDATARANGE_AUDIO),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        A2DPHPSPEAKER_LOOPBACK_MAX_CHANNELS,           
        A2DPHPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE,    
        A2DPHPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE,
        A2DPHPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE
    }
};

static
PKSDATARANGE A2dpHpSpeakerPinDataRangePointersStream[] =
{
    PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[0]),
    PKSDATARANGE(&PinDataRangeAttributeList)
};

static
PKSDATARANGE A2dpHpSpeakerPinDataRangePointersOffloadStream[] =
{
    PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[1]),
    PKSDATARANGE(&PinDataRangeAttributeList)
};

static
PKSDATARANGE A2dpHpSpeakerPinDataRangePointersLoopbackStream[] =
{
    PKSDATARANGE(&A2dpHpSpeakerPinDataRangesStream[2])
};

//=============================================================================
static
KSDATARANGE A2dpHpSpeakerPinDataRangesBridge[] =
{
    {
        sizeof(KSDATARANGE),
        0,
        0,
        0,
        STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
        STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
        STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
    }
};

static
PKSDATARANGE A2dpHpSpeakerPinDataRangePointersBridge[] =
{
    &A2dpHpSpeakerPinDataRangesBridge[0]
};

//=============================================================================

static
PCPROPERTY_ITEM PropertiesA2dpHpSpeakerOffloadPin[] =
{
    {
        &KSPROPSETID_OffloadPin,  // define new property set
        KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_OffloadPin
    },
    {
        &KSPROPSETID_OffloadPin,  // define new property set
        KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties
        KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_OffloadPin
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationA2dpHpSpeakerOffloadPin, PropertiesA2dpHpSpeakerOffloadPin);

//=============================================================================
static
PCPIN_DESCRIPTOR A2dpHpSpeakerWaveMiniportPins[] =
{
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM
    {
        A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS,
        A2DPHPSPEAKER_MAX_INPUT_SYSTEM_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersStream),
            A2dpHpSpeakerPinDataRangePointersStream,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD
    {
        A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS,
        A2DPHPSPEAKER_MAX_INPUT_OFFLOAD_STREAMS, 
        0,
        &AutomationA2dpHpSpeakerOffloadPin,     // AutomationTable
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersOffloadStream),
            A2dpHpSpeakerPinDataRangePointersOffloadStream,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_SINK,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
    // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK
    {
        A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS,
        A2DPHPSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, 
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersLoopbackStream),
            A2dpHpSpeakerPinDataRangePointersLoopbackStream,
            KSPIN_DATAFLOW_OUT,              
            KSPIN_COMMUNICATION_SINK,
            &KSNODETYPE_AUDIO_LOOPBACK,
            NULL,
            0
        }
    },
    // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE
    {
        0,
        0,
        0,
        NULL,
        {
            0,
            NULL,
            0,
            NULL,
            SIZEOF_ARRAY(A2dpHpSpeakerPinDataRangePointersBridge),
            A2dpHpSpeakerPinDataRangePointersBridge,
            KSPIN_DATAFLOW_OUT,
            KSPIN_COMMUNICATION_NONE,
            &KSCATEGORY_AUDIO,
            NULL,
            0
        }
    },
};

//=============================================================================
static
PCNODE_DESCRIPTOR A2dpHpSpeakerWaveMiniportNodes[] =
{
    // KSNODE_WAVE_AUDIO_ENGINE
    {
        0,                          // Flags
        NULL,                       // AutomationTable
        &KSNODETYPE_AUDIO_ENGINE,   // Type  KSNODETYPE_AUDIO_ENGINE
        NULL                        // Name
    }
};
//=============================================================================
//
//                   ----------------------------      
//                   |                          |      
//  System Pin   0-->|                          |--> 2 Loopback Pin
//                   |   HW Audio Engine node   |      
//  Offload Pin  1-->|                          |--> 3 KSPIN_WAVE_RENDER_SOURCE
//                   |                          |      
//                   ----------------------------      
static
PCCONNECTION_DESCRIPTOR A2dpHpSpeakerWaveMiniportConnections[] =
{
    { PCFILTER_NODE,            KSPIN_WAVE_RENDER_SINK_SYSTEM,     KSNODE_WAVE_AUDIO_ENGINE,   1 },
    { PCFILTER_NODE,            KSPIN_WAVE_RENDER_SINK_OFFLOAD,    KSNODE_WAVE_AUDIO_ENGINE,   2 },
    { KSNODE_WAVE_AUDIO_ENGINE, 3,                                 PCFILTER_NODE,              KSPIN_WAVE_RENDER_SINK_LOOPBACK },
    { KSNODE_WAVE_AUDIO_ENGINE, 0,                                 PCFILTER_NODE,              KSPIN_WAVE_RENDER_SOURCE },
};

//=============================================================================
static
PCPROPERTY_ITEM PropertiesA2dpHpSpeakerWaveFilter[] =
{
    {
        &KSPROPSETID_Pin,
        KSPROPERTY_PIN_PROPOSEDATAFORMAT,
        KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpWaveFilter
    },
    {
        &KSPROPSETID_AudioEffectsDiscovery,
        KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_A2dpHpWaveFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationA2dpHpSpeakerWaveFilter, PropertiesA2dpHpSpeakerWaveFilter);

//=============================================================================
static
PCFILTER_DESCRIPTOR A2dpHpSpeakerWaveMiniportFilterDescriptor =
{
    0,                                              // Version
    &AutomationA2dpHpSpeakerWaveFilter,             // AutomationTable
    sizeof(PCPIN_DESCRIPTOR),                       // PinSize
    SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportPins),    // PinCount
    A2dpHpSpeakerWaveMiniportPins,                  // Pins
    sizeof(PCNODE_DESCRIPTOR),                      // NodeSize
    SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportNodes),   // NodeCount
    A2dpHpSpeakerWaveMiniportNodes,                 // Nodes
    SIZEOF_ARRAY(A2dpHpSpeakerWaveMiniportConnections),// ConnectionCount
    A2dpHpSpeakerWaveMiniportConnections,           // Connections
    0,                                              // CategoryCount
    NULL                                            // Categories  - use defaults (audio, render, capture)
};

#endif // _SYSVAD_A2DPHPSPEAKERWAVTABLE_H_



<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/TestEffectsList.cpp<|end_filename|>
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//

#include <stdafx.h>
#include <memory>
#include <wil\resultmacros.h>
#include <wil\com.h>
#include <wil\winrt.h>
#include <wil\wrl.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <functiondiscoverykeys.h>
#include <windows.media.effects.h>
#include "avdevqueryhelpers.h"
#include <AudioStreamCategoryConversions.h>

using namespace Windows::Media::Effects;

class CTestEffectsList
{
    BEGIN_TEST_CLASS(CTestEffectsList)
        START_APPVERIFIFER
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioengineextensionapo.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"Elevated")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
        END_APPVERIFIER
    END_TEST_CLASS()

protected:
    BEGIN_TEST_METHOD(TestGetEffectsList)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"7E8037CA-8670-4170-8EC8-C7B02B22A7B4")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"FD1ED3C6-54E0-432B-B3C9-5EAC5DE2D3E1")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Test IAudioSystemEffects2 and IAudioSystemEffects3 usage from APOs - TestEffectsList")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"30")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"60")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestEffectsList")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()
};

GUID AudioEffectTypeToGUID(Windows::Media::Effects::AudioEffectType type)
{
    switch(type)
    {
        case AudioEffectType_AcousticEchoCancellation:
            return AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION;
        case AudioEffectType_NoiseSuppression:
            return AUDIO_EFFECT_TYPE_NOISE_SUPPRESSION;
        case AudioEffectType_AutomaticGainControl:
            return AUDIO_EFFECT_TYPE_AUTOMATIC_GAIN_CONTROL;
        case AudioEffectType_BeamForming:
            return AUDIO_EFFECT_TYPE_BEAMFORMING;
        case AudioEffectType_ConstantToneRemoval:
            return AUDIO_EFFECT_TYPE_CONSTANT_TONE_REMOVAL;
        case AudioEffectType_Equalizer:
            return AUDIO_EFFECT_TYPE_EQUALIZER;
        case AudioEffectType_LoudnessEqualizer:
            return AUDIO_EFFECT_TYPE_LOUDNESS_EQUALIZER;
        case AudioEffectType_BassBoost:
            return AUDIO_EFFECT_TYPE_BASS_BOOST;
        case AudioEffectType_VirtualSurround:
            return AUDIO_EFFECT_TYPE_VIRTUAL_SURROUND;
        case AudioEffectType_VirtualHeadphones:
            return AUDIO_EFFECT_TYPE_VIRTUAL_HEADPHONES;
        case AudioEffectType_SpeakerFill:
            return AUDIO_EFFECT_TYPE_SPEAKER_FILL;
        case AudioEffectType_RoomCorrection:
            return AUDIO_EFFECT_TYPE_ROOM_CORRECTION;
        case AudioEffectType_BassManagement:
            return AUDIO_EFFECT_TYPE_BASS_MANAGEMENT;
        case AudioEffectType_EnvironmentalEffects:
            return AUDIO_EFFECT_TYPE_ENVIRONMENTAL_EFFECTS;
        case AudioEffectType_SpeakerProtection:
            return AUDIO_EFFECT_TYPE_SPEAKER_PROTECTION;
        case AudioEffectType_SpeakerCompensation:
            return AUDIO_EFFECT_TYPE_SPEAKER_COMPENSATION;
        case AudioEffectType_DynamicRangeCompression:
            return AUDIO_EFFECT_TYPE_DYNAMIC_RANGE_COMPRESSION;
        case AudioEffectType_FarFieldBeamForming:
            return AUDIO_EFFECT_TYPE_FAR_FIELD_BEAMFORMING;
        case AudioEffectType_DeepNoiseSuppression:
            return AUDIO_EFFECT_TYPE_DEEP_NOISE_SUPPRESSION;
    }
    return GUID_NULL;
}

void CTestEffectsList::TestGetEffectsList()
{
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);

    wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));

    EDataFlow dataFlows[] = { eRender, eCapture };

    for (EDataFlow dataFlow : dataFlows)
    {
        wil::com_ptr_nothrow<IMMDeviceCollection> spDevices;
        VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATE_ACTIVE, &spDevices));

        UINT cDevices = 0;
        VERIFY_SUCCEEDED(spDevices->GetCount(&cDevices));

        for (UINT i = 0; i < cDevices; i++)
        {
            wil::com_ptr_nothrow<IMMDevice> spEndpoint;
            VERIFY_SUCCEEDED(spDevices->Item(i, &spEndpoint));

            wil::unique_prop_variant var;
            wil::com_ptr_nothrow<IPropertyStore> spPropertyStore;
            VERIFY_SUCCEEDED(spEndpoint->OpenPropertyStore(STGM_READ, &spPropertyStore));
            VERIFY_SUCCEEDED(spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var));
            VERIFY_ARE_EQUAL(VT_LPWSTR, var.vt);
            LOG_OUTPUT(L"Testing endpoint: %s", var.pwszVal);

            wil::unique_cotaskmem_string spDeviceId;
            VERIFY_SUCCEEDED(spEndpoint->GetId(&spDeviceId));
            wil::unique_cotaskmem_string spEndpointPnpInstanceId;
            VERIFY_SUCCEEDED(mmdDevGetInterfaceIdFromMMDeviceId(spDeviceId.get(), &spEndpointPnpInstanceId));
            HSTRING deviceId;
            VERIFY_SUCCEEDED(WindowsCreateString(spEndpointPnpInstanceId.get(), (UINT32)wcslen(spEndpointPnpInstanceId.get()), &deviceId));

            // Get effects list using IAudioSystemEffects2
            wil::com_ptr_nothrow<IAudioEffectsManagerStatics> audioEffectsManagerStatics =
                wil::GetActivationFactory<IAudioEffectsManagerStatics>(RuntimeClass_Windows_Media_Effects_AudioEffectsManager);

            wil::com_ptr_nothrow<Windows::Foundation::Collections::IVectorView<AudioEffect*>> effects;
            AUDIO_STREAM_CATEGORY audioCategory;

            if (dataFlow == eRender)
            {
                wil::com_ptr_nothrow<IAudioRenderEffectsManager> audioRenderEffectsManager;
                VERIFY_SUCCEEDED(audioEffectsManagerStatics->CreateAudioRenderEffectsManager(deviceId, Windows::Media::Render::AudioRenderCategory::AudioRenderCategory_Communications, &audioRenderEffectsManager));
                VERIFY_IS_NOT_NULL(audioRenderEffectsManager);
                VERIFY_SUCCEEDED(audioRenderEffectsManager->GetAudioRenderEffects(&effects));

                VERIFY_SUCCEEDED(MediaRenderCategory_To_AUDIO_STREAM_CATEGORY(Windows::Media::Render::AudioRenderCategory::AudioRenderCategory_Communications, &audioCategory));
            }
            else
            {
                wil::com_ptr_nothrow<IAudioCaptureEffectsManager> audioCaptureEffectsManager;
                VERIFY_SUCCEEDED(audioEffectsManagerStatics->CreateAudioCaptureEffectsManager(deviceId, Windows::Media::Capture::MediaCategory::MediaCategory_Communications, &audioCaptureEffectsManager));
                VERIFY_IS_NOT_NULL(audioCaptureEffectsManager);
                VERIFY_SUCCEEDED(audioCaptureEffectsManager->GetAudioCaptureEffects(&effects));

                VERIFY_SUCCEEDED(MediaCaptureCategory_To_AUDIO_STREAM_CATEGORY(Windows::Media::Capture::MediaCategory::MediaCategory_Communications, &audioCategory));
            }

            UINT32 numEffects = 0;
            effects->get_Size(&numEffects);

            // Get effects lists using IAudioSystemEffects3
            wil::com_ptr_nothrow<IAudioClient2> spAudioClient;
            VERIFY_SUCCEEDED(spEndpoint->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<void**>(&spAudioClient)));

            AudioClientProperties clientProperties = {};
            clientProperties.cbSize = sizeof(AudioClientProperties);
            clientProperties.bIsOffload = FALSE;
            clientProperties.eCategory = audioCategory;
            clientProperties.Options = AUDCLNT_STREAMOPTIONS_NONE;
            VERIFY_SUCCEEDED(spAudioClient->SetClientProperties(&clientProperties));

            wil::unique_cotaskmem_ptr<WAVEFORMATEX> mixFormat;
            VERIFY_SUCCEEDED(spAudioClient->GetMixFormat(wil::out_param_ptr<WAVEFORMATEX**>(mixFormat)));

            VERIFY_SUCCEEDED(spAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 20 * 10000, 0, mixFormat.get(), nullptr));

            wil::com_ptr_nothrow<IAudioEffectsManager> audioEffectsManager;
            VERIFY_SUCCEEDED(spAudioClient->GetService(IID_PPV_ARGS(&audioEffectsManager)));
            VERIFY_IS_NOT_NULL(audioEffectsManager);

            wil::unique_cotaskmem_array_ptr<AUDIO_EFFECT> controllableEffects;
            UINT32 numControllableEffects;
            VERIFY_SUCCEEDED(audioEffectsManager->GetAudioEffects(&controllableEffects, &numControllableEffects));

            // Verify 'effects' is a subset of 'controllableEffects'
            VERIFY_IS_GREATER_THAN_OR_EQUAL(numControllableEffects, numEffects);
            for (UINT32 i = 0; i < numEffects; i++)
            {
                bool found = false;
                for (UINT32 j = 0; j < numControllableEffects && !found; j++)
                {
                    AudioEffectType effectType;
                    wil::com_ptr_nothrow<IAudioEffect> spEffect;
                    effects->GetAt(i, &spEffect);
                    spEffect->get_AudioEffectType(&effectType);
                    if (IsEqualGUID(AudioEffectTypeToGUID(effectType), controllableEffects[j].id))
                    {
                        found = true;
                    }
                }
                VERIFY_IS_TRUE(found);
            }

            // Verify effects not present in 'effects' are disabled
            for (UINT32 i = 0; i < numControllableEffects; i++)
            {
                bool found = false;
                for (UINT32 j = 0; j < numEffects && !found; j++)
                {
                    AudioEffectType effectType;
                    wil::com_ptr_nothrow<IAudioEffect> spEffect;
                    effects->GetAt(j, &spEffect);
                    spEffect->get_AudioEffectType(&effectType);
                    if (IsEqualGUID(controllableEffects[i].id, AudioEffectTypeToGUID(effectType)))
                    {
                        found = true;
                    }
                }
                if (!found)
                {
                    VERIFY_ARE_EQUAL(controllableEffects[i].state, AUDIO_EFFECT_STATE_OFF);
                }
            }
        }
    }
}


<|start_filename|>storage/class/classpnp/src/autorun.c<|end_filename|>
/*++

Copyright (C) Microsoft Corporation, 1991 - 2010

Module Name:

    autorun.c

Abstract:

    Code for support of media change detection in the class driver

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#include "classp.h"
#include "debug.h"

#ifdef DEBUG_USE_WPP
#include "autorun.tmh"
#endif

#define GESN_TIMEOUT_VALUE (0x4)
#define GESN_BUFFER_SIZE (0x8)
#define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS   (2)

#define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
#define MCN_REG_SUBKEY_NAME                   (L"MediaChangeNotification")
#define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
#define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME  (L"AlwaysEnableMCN")

const GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;

//
// Only send polling irp when device is fully powered up, a
// power down irp is not in progress, and the screen is on.
//
// NOTE:   This helps close a window in time where a polling irp could cause
//         a drive to spin up right after it has powered down. The problem is
//         that SCSIPORT, ATAPI and SBP2 will be in the process of powering
//         down (which may take a few seconds), but won't know that. It would
//         then get a polling irp which will be put into its queue since it
//         the disk isn't powered down yet. Once the disk is powered down it
//         will find the polling irp in the queue and then power up the
//         device to do the poll. They do not want to check if the polling
//         irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
//         path and would slow down all I/Os. A better way to fix this
//         would be to serialize the polling and power down irps so that
//         only one of them is sent to the device at a time.
//
__inline
BOOLEAN
ClasspCanSendPollingIrp(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
    )
{
    return ((fdoExtension->DevicePowerState == PowerDeviceD0) &&
            (fdoExtension->PowerDownInProgress == FALSE) &&
            (ClasspScreenOff == FALSE));
}

BOOLEAN
ClasspIsMediaChangeDisabledDueToHardwareLimitation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PUNICODE_STRING RegistryPath
    );

NTSTATUS
ClasspMediaChangeDeviceInstanceOverride(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    OUT PBOOLEAN Enabled
    );

BOOLEAN
ClasspIsMediaChangeDisabledForClass(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PUNICODE_STRING RegistryPath
    );

VOID
ClasspSetMediaChangeStateEx(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN MEDIA_CHANGE_DETECTION_STATE NewState,
    IN BOOLEAN Wait,
    IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
    );

RTL_QUERY_REGISTRY_ROUTINE ClasspMediaChangeRegistryCallBack;

VOID
ClasspSendMediaStateIrp(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMEDIA_CHANGE_DETECTION_INFO Info,
    IN ULONG CountDown
    );

IO_WORKITEM_ROUTINE ClasspFailurePredict;

NTSTATUS
ClasspInitializePolling(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN BOOLEAN AllowDriveToSleep
    );


IO_WORKITEM_ROUTINE ClasspDisableGesn;

IO_COMPLETION_ROUTINE ClasspMediaChangeDetectionCompletion;

KDEFERRED_ROUTINE ClasspTimerTick;

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
EXT_CALLBACK ClasspTimerTickEx;
#endif

BOOLEAN ClasspScreenOff = FALSE;

//
// Tick timer related defines.
//
#define TICK_TIMER_PERIOD_IN_MSEC    1000
#define TICK_TIMER_DELAY_IN_MSEC     1000

#if ALLOC_PRAGMA

#pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
#pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
#pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
#pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
#pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
#pragma alloc_text(PAGE, ClasspInitializePolling)
#pragma alloc_text(PAGE, ClasspDisableGesn)

#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
#pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)

#pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)

#pragma alloc_text(PAGE, ClasspInitializeGesn)
#pragma alloc_text(PAGE, ClasspMcnControl)

#endif

// ISSUE -- make this public?
VOID
ClassSendEjectionNotification(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    //
    // For post-NT5.1 work, need to move EjectSynchronizationEvent
    // to be a MUTEX so we can attempt to grab it here and benefit
    // from deadlock detection.  This will allow checking if the media
    // has been locked by programs before broadcasting these events.
    // (what's the point of broadcasting if the media is not locked?)
    //
    // This would currently only be a slight optimization.  For post-NT5.1,
    // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
    // thereby cleaning up a lot of the ejection code.  Then, when the
    // ejection request occured, we could see if any locks for the media
    // existed.  if locked, broadcast.  if not, we send the eject irp.
    //

    //
    // for now, just always broadcast.  make this a public routine,
    // so class drivers can add special hacks to broadcast this for their
    // non-MMC-compliant devices also from sense codes.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassSendEjectionNotification: media EJECT_REQUEST"));
    ClassSendNotification(FdoExtension,
                           &GUID_IO_MEDIA_EJECT_REQUEST,
                           0,
                           NULL);
    return;
}


_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
ClassSendNotification(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ const GUID * Guid,
    _In_ ULONG  ExtraDataSize,
    _In_reads_bytes_opt_(ExtraDataSize) PVOID  ExtraData
    )
{
    PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
    ULONG requiredSize;
    NTSTATUS status;

    status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
                         ExtraDataSize,
                         &requiredSize);

    if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff)) {
        // MAX_USHORT, max total size for these events!
        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                   "Error sending event: size too large! (%x)\n",
                   requiredSize));
        return;
    }

    notification = ExAllocatePoolZero(NonPagedPoolNx,
                                      requiredSize,
                                      'oNcS');

    //
    // if none allocated, exit
    //

    if (notification == NULL) {
        return;
    }

    //
    // Prepare and send the request!
    //

    notification->Version = 1;
    notification->Size = (USHORT)(requiredSize);
    notification->FileObject = NULL;
    notification->NameBufferOffset = -1;
    notification->Event = *Guid;

    if (ExtraData != NULL && ExtraDataSize != 0) {
        RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
    }

    IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
                                           notification,
                                           NULL, NULL);

    FREE_POOL(notification);
    return;
}


NTSTATUS
ClasspInterpretGesnData(
    IN  PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN  PNOTIFICATION_EVENT_STATUS_HEADER Header,
    OUT PBOOLEAN ResendImmediately
    )

/*++

Routine Description:

    This routine will interpret the data returned for a GESN command, and
    (if appropriate) set the media change event, and broadcast the
    appropriate events to user mode for applications who care.

Arguments:

    FdoExtension - the device

    DataBuffer - the resulting data from a GESN event.
        requires at least EIGHT valid bytes (header == 4, data == 4)

    ResendImmediately - whether or not to immediately resend the request.
        this should be FALSE if there was no event, FALSE if the reported
        event was of the DEVICE BUSY class, else true.

Return Value:

    STATUS_SUCCESS if successful, an error code otherwise

Notes:

    DataBuffer must be at least four bytes of valid data (header == 4 bytes),
    and have at least eight bytes of allocated memory (all events == 4 bytes).

    The call to StartNextPacket may occur before this routine is completed.
    the operational change notifications are informational in nature, and
    while useful, are not neccessary to ensure proper operation.  For example,
    if the device morphs to no longer supporting WRITE commands, all further
    write commands will fail.  There exists a small timing window wherein
    IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response.  If
    a device supports software write protect, it is expected that the
    application can handle such a case.

    NOTE: perhaps setting the updaterequired byte to one should be done here.
    if so, it relies upon the setting of a 32-byte value to be an atomic
    operation.  unfortunately, there is no simple way to notify a class driver
    which wants to know that the device behavior requires updating.

    Not ready events may be sent every second.  For example, if we were
    to minimize the number of asynchronous notifications, an application may
    register just after a large busy time was reported.  This would then
    prevent the application from knowing the device was busy until some
    arbitrarily chosen timeout has occurred.  Also, the GESN request would
    have to still occur, since it checks for non-busy events (such as user
    keybutton presses and media change events) as well.  The specification
    states that the lower-numered events get reported first, so busy events,
    while repeating, will only be reported when all other events have been
    cleared from the device.

--*/

{
    PMEDIA_CHANGE_DETECTION_INFO info;
    LONG dataLength;
    LONG requiredLength;
    NTSTATUS status = STATUS_SUCCESS;

    info = FdoExtension->MediaChangeDetectionInfo;

    //
    // note: don't allocate anything in this routine so that we can
    //       always just 'return'.
    //

    *ResendImmediately = FALSE;
    if (Header->NEA) {
        return status;
    }
    if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) {
        return status;
    }

    //
    // HACKHACK - REF #0001
    // This loop is only taken initially, due to the inability to reliably
    // auto-detect drives that report events correctly at boot.  When we
    // detect this behavior during the normal course of running, we will
    // disable the hack, allowing more efficient use of the system.  This
    // should occur "nearly" instantly, as the drive should have multiple
    // events queue'd (ie. power, morphing, media).
    //

    if (info->Gesn.HackEventMask) {

        //
        // all events use the low four bytes of zero to indicate
        // that there was no change in status.
        //

        UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
        UCHAR lowestSetBit;
        UCHAR thisEventBit = (1 << Header->NotificationClass);

        if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit)) {

            //
            // The drive is reporting an event that wasn't requested
            //

            return STATUS_DEVICE_PROTOCOL_ERROR;
        }

        //
        // some bit magic here... this results in the lowest set bit only
        //

        lowestSetBit = info->Gesn.EventMask;
        lowestSetBit &= (info->Gesn.EventMask - 1);
        lowestSetBit ^= (info->Gesn.EventMask);

        if (thisEventBit != lowestSetBit) {

            //
            // HACKHACK - REF #0001
            // the first time we ever see an event set that is not the lowest
            // set bit in the request (iow, highest priority), we know that the
            // hack is no longer required, as the device is ignoring "no change"
            // events when a real event is waiting in the other requested queues.
            //

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                       "Classpnp => GESN::NONE: Compliant drive found, "
                       "removing GESN hack (%x, %x)\n",
                       thisEventBit, info->Gesn.EventMask));

            info->Gesn.HackEventMask = FALSE;

        } else if (thisEvent == 0) { // NOTIFICATION_*_EVENT_NO_CHANGE

            //
            // HACKHACK - REF #0001
            // note: this hack prevents poorly implemented firmware from constantly
            //       returning "No Event".  we do this by cycling through the
            //       supported list of events here.
            //

            SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
            CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);

            //
            // if we have cycled through all supported event types, then
            // we need to reset the events we are asking about. else we
            // want to resend this request immediately in case there was
            // another event pending.
            //

            if (info->Gesn.EventMask == 0) {
                info->Gesn.EventMask         = info->Gesn.NoChangeEventMask;
                info->Gesn.NoChangeEventMask = 0;
            } else {
                *ResendImmediately = TRUE;
            }
            return status;
        }

    } // end if (info->Gesn.HackEventMask)

    dataLength =
        (Header->EventDataLength[0] << 8) |
        (Header->EventDataLength[1] & 0xff);
    dataLength -= 2;
    requiredLength = 4; // all events are four bytes

    if (dataLength < requiredLength) {
        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                   "Classpnp => GESN returned only %x bytes data for fdo %p\n",
                   dataLength, FdoExtension->DeviceObject));

        return STATUS_DEVICE_PROTOCOL_ERROR;
    }
    if (dataLength != requiredLength) {
        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                   "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
                   dataLength, FdoExtension->DeviceObject));
        // dataLength = 4;
    }

    NT_ASSERT(dataLength == 4);

    if ((Header->ClassEventData[0] & 0xf) == 0)
    {
        // a zero event is a "no change event, so do not retry
        return status;
    }

    // because a event other than "no change" occurred,
    // we should immediately resend this request.
    *ResendImmediately = TRUE;


/*
    ClassSendNotification(FdoExtension,
                           &GUID_IO_GENERIC_GESN_EVENT,
                           sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
                           Header)
*/



    switch (Header->NotificationClass) {

    case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS: { // 0x01

        PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo =
            (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData);
        ULONG event;

        if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED) {
            break;
        }

        event = (opChangeInfo->Operation[0] << 8) |
                (opChangeInfo->Operation[1]     ) ;

        // Workaround some hardware that is buggy but prevalent in the market
        // This hardware has the property that it will report OpChange events repeatedly,
        // causing us to retry immediately so quickly that we will eventually disable
        // GESN to prevent an infinite loop.
        // (only one valid OpChange event type now, only two ever defined)
        if (info->MediaChangeRetryCount >= 4) {

            //
            // HACKHACK - REF #0002
            // Some drives incorrectly report OpChange/Change (001b/0001h) events
            // continuously when the tray has been ejected.  This causes this routine
            // to set ResendImmediately to "TRUE", and that results in our cycling
            // 32 times immediately resending.  At that point, we give up detecting
            // the infinite retry loop, and disable GESN on these drives.  This
            // prevents Media Eject Request (from eject button) from being reported.
            // Thus, instead we should attempt to workaround this issue by detecting
            // this behavior.
            //

            static UCHAR const OpChangeMask = 0x02;

            // At least one device reports "temporarily busy" (which is useless) on eject
            // At least one device reports "OpChange" repeatedly when re-inserting media
            // All seem to work well using this workaround

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN,
                        "Classpnp => GESN OpChange events are broken.  Working around this "
                        "problem in software (for fdo %p)\n",
                        FdoExtension->DeviceObject));


            // OpChange is not the only bit set -- Media class is required....
            NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1);

            //
            // Force the use of the hackhack (ref #0001) to workaround the
            // issue noted this hackhack (ref #0002).
            //
            SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask);
            CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask);
            info->Gesn.HackEventMask = TRUE;

            //
            // don't request the opChange event again.  use the method
            // defined by hackhack (ref #0001) as the workaround.
            //

            if (info->Gesn.EventMask == 0) {
                info->Gesn.EventMask         = info->Gesn.NoChangeEventMask;
                info->Gesn.NoChangeEventMask = 0;
                *ResendImmediately = FALSE;
            } else {
                *ResendImmediately = TRUE;
            }

            break;
        }


        if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) |
            (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE)) {

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                       "Classpnp => GESN says features added/changedfor fdo %p\n",
                       FdoExtension->DeviceObject));

            // don't notify that new media arrived, just set the
            // DO_VERIFY to force a FS reload.

            if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
                          FILE_REMOVABLE_MEDIA) &&
                (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
                (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
                ) {

                SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
            }

            //
            // If there is a class specific error handler, call it with
            // a "fake" media change error in case it needs to update
            // internal structures as though a media change occurred.
            //

            if (FdoExtension->CommonExtension.DevInfo->ClassError != NULL) {

                SCSI_REQUEST_BLOCK srb = {0};
                UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
                PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
                PSCSI_REQUEST_BLOCK srbPtr;

                SENSE_DATA sense = {0};
                NTSTATUS tempStatus;
                BOOLEAN retry;

                tempStatus = STATUS_MEDIA_CHANGED;
                retry = FALSE;

                sense.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT;

                sense.AdditionalSenseLength = sizeof(SENSE_DATA) -
                    RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);

                sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION;
                sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;

                //
                // Send the right type of SRB to the class driver
                //
                if ((FdoExtension->CommonExtension.DriverExtension->SrbSupport &
                     CLASS_SRB_STORAGE_REQUEST_BLOCK) != 0) {
                    #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
                    status = InitializeStorageRequestBlock(srbEx,
                                                           STORAGE_ADDRESS_TYPE_BTL8,
                                                           CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
                                                           1,
                                                           SrbExDataTypeScsiCdb16);
                    if (NT_SUCCESS(status)) {
                        SrbSetCdbLength(srbEx, 6);
                        srbEx->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
                        SrbSetSenseInfoBuffer(srbEx, &sense);
                        SrbSetSenseInfoBufferLength(srbEx, sizeof(sense));
                        srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
                    } else {
                        // should not happen. Revert to legacy SRB.
                        NT_ASSERT(FALSE);
                        srb.CdbLength = 6;
                        srb.Length    = sizeof(SCSI_REQUEST_BLOCK);
                        srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
                        srb.SenseInfoBuffer = &sense;
                        srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
                        srbPtr = &srb;
                    }
                } else {
                    srb.CdbLength = 6;
                    srb.Length    = sizeof(SCSI_REQUEST_BLOCK);
                    srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
                    srb.SenseInfoBuffer = &sense;
                    srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
                    srbPtr = &srb;
                }

                FdoExtension->CommonExtension.DevInfo->ClassError(FdoExtension->DeviceObject,
                                                                  srbPtr,
                                                                  &tempStatus,
                                                                  &retry);

            } // end class error handler

        }
        break;
    }

    case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3

        PNOTIFICATION_EXTERNAL_STATUS externalInfo =
            (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
        DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0};

        //
        // unfortunately, due to time constraints, we will only notify
        // about keys being pressed, and not released.  this makes keys
        // single-function, but simplifies the code significantly.
        //

        if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) {
            break;
        }

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                   "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
                   externalInfo->ExternalEvent, externalInfo->ExternalStatus,
                   (externalInfo->Request[0] << 8) | externalInfo->Request[1]
                   ));

        externalData.Version = 1;
        externalData.DeviceClass = 0;
        externalData.ButtonStatus = externalInfo->ExternalEvent;
        externalData.Request =
            (externalInfo->Request[0] << 8) |
            (externalInfo->Request[1] & 0xff);
        KeQuerySystemTime(&(externalData.SystemTime));
        externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
        ClassSendNotification(FdoExtension,
                               &GUID_IO_DEVICE_EXTERNAL_REQUEST,
                               sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
                               &externalData);
        return status;
    }

    case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4

        PNOTIFICATION_MEDIA_STATUS mediaInfo =
            (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                   "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
                   mediaInfo->MediaEvent, mediaInfo->MediaStatus));

        if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
            (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) {


            if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
                          FILE_REMOVABLE_MEDIA) &&
                (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
                (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
                ) {

                SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);

            }
            InterlockedIncrement((volatile LONG *)&FdoExtension->MediaChangeCount);
            ClasspSetMediaChangeStateEx(FdoExtension,
                                        MediaPresent,
                                        FALSE,
                                        TRUE);

        } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) {

            ClasspSetMediaChangeStateEx(FdoExtension,
                                        MediaNotPresent,
                                        FALSE,
                                        TRUE);

        } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) {

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                       "Classpnp => GESN Ejection request received!\n"));
            ClassSendEjectionNotification(FdoExtension);

        }
        break;

    }

    case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events...

        PNOTIFICATION_BUSY_STATUS busyInfo =
            (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
        DEVICE_EVENT_BECOMING_READY busyData = {0};

        //
        // NOTE: we never actually need to immediately retry for these
        //       events: if one exists, the device is busy, and if not,
        //       we still don't want to retry.
        //

        *ResendImmediately = FALSE;

        //
        // else we want to report the approximated time till it's ready.
        //

        busyData.Version = 1;
        busyData.Reason = busyInfo->DeviceBusyStatus;
        busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
                                         (busyInfo->Time[1] & 0xff);

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                   "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
                   busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
                   busyData.Estimated100msToReady
                   ));

        //
        // Ignore the notification if the time is small
        //
        if (busyData.Estimated100msToReady < GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS) {
            break;
        }


        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media BECOMING_READY"));
        ClassSendNotification(FdoExtension,
                               &GUID_IO_DEVICE_BECOMING_READY,
                               sizeof(DEVICE_EVENT_BECOMING_READY),
                               &busyData);
        break;
    }

    default: {

        break;

    }

    } // end switch on notification class
    return status;
}

/*++////////////////////////////////////////////////////////////////////////////

ClasspInternalSetMediaChangeState()

Routine Description:

    This routine will (if appropriate) set the media change event for the
    device.  The event will be set if the media state is changed and
    media change events are enabled.  Otherwise the media state will be
    tracked but the event will not be set.

    This routine will lock out the other media change routines if possible
    but if not a media change notification may be lost after the enable has
    been completed.

Arguments:

    FdoExtension - the device

    MediaPresent - indicates whether the device has media inserted into it
                   (TRUE) or not (FALSE).

Return Value:

    none

--*/
VOID
ClasspInternalSetMediaChangeState(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN MEDIA_CHANGE_DETECTION_STATE NewState,
    IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
    )
{
#if DBG
    PCSZ states[] = {"Unknown", "Present", "Not Present", "Unavailable"};
#endif
    MEDIA_CHANGE_DETECTION_STATE oldMediaState;
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
    CLASS_MEDIA_CHANGE_CONTEXT mcnContext;
    PIO_WORKITEM workItem;

    if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable))) {
        return;
    }

    if(info == NULL) {
        return;
    }

    oldMediaState = InterlockedExchange(
        (PLONG)(&info->MediaChangeDetectionState),
        (LONG)NewState);

    if((oldMediaState == MediaUnknown) && (!KnownStateChange)) {

        //
        // The media was in an indeterminate state before - don't notify for
        // this change.
        //

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassSetMediaChangeState: State was unknown - this may "
                    "not be a change\n"));
        return;

    } else if(oldMediaState == NewState) {

        //
        // Media is in the same state it was before.
        //

        return;
    }
    
    //
    // Inform PartMgr that the media changed. It will need to propagate
    // DO_VERIFY_VOLUME to each partition. Ensure that only one work item
    // updates the disk's properties at any given time.
    //
    if (InterlockedCompareExchange((volatile LONG *)&FdoExtension->PrivateFdoData->UpdateDiskPropertiesWorkItemActive, 1, 0) == 0) {

        workItem = IoAllocateWorkItem(FdoExtension->DeviceObject);

        if (workItem) {

            IoQueueWorkItem(workItem, ClasspUpdateDiskProperties, DelayedWorkQueue, workItem);

        } else {

            InterlockedExchange((volatile LONG *)&FdoExtension->PrivateFdoData->UpdateDiskPropertiesWorkItemActive, 0);
        }
    }

    if(info->MediaChangeDetectionDisableCount != 0) {
#if DBG
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassSetMediaChangeState: MCN not enabled, state "
                    "changed from %s to %s\n",
                    states[oldMediaState], states[NewState]));
#endif
        return;

    }
#if DBG
    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                "ClassSetMediaChangeState: State change from %s to %s\n",
                states[oldMediaState], states[NewState]));
#endif

    //
    // make the data useful -- it used to always be zero.
    //
    mcnContext.MediaChangeCount = FdoExtension->MediaChangeCount;
    mcnContext.NewState = NewState;

    if (NewState == MediaPresent) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media ARRIVAL"));
        ClassSendNotification(FdoExtension,
                               &GUID_IO_MEDIA_ARRIVAL,
                               sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
                               &mcnContext);

    }
    else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable)) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media REMOVAL"));
        ClassSendNotification(FdoExtension,
                               &GUID_IO_MEDIA_REMOVAL,
                               sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
                               &mcnContext);

    } else {

        //
        // Don't notify of changed going to unknown.
        //

        return;
    }

    return;
} // end ClasspInternalSetMediaChangeState()

/*++////////////////////////////////////////////////////////////////////////////

ClassSetMediaChangeState()

Routine Description:

    This routine will (if appropriate) set the media change event for the
    device.  The event will be set if the media state is changed and
    media change events are enabled.  Otherwise the media state will be
    tracked but the event will not be set.

    This routine will lock out the other media change routines if possible
    but if not a media change notification may be lost after the enable has
    been completed.

Arguments:

    FdoExtension - the device

    MediaPresent - indicates whether the device has media inserted into it
                   (TRUE) or not (FALSE).

    Wait - indicates whether the function should wait until it can acquire
           the synchronization lock or not.

Return Value:

    none

--*/

VOID
#pragma prefast(suppress:26165, "The mutex won't be acquired in the case of a timeout.")
ClasspSetMediaChangeStateEx(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN MEDIA_CHANGE_DETECTION_STATE NewState,
    IN BOOLEAN Wait,
    IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
    LARGE_INTEGER zero;
    NTSTATUS status;

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "> ClasspSetMediaChangeStateEx"));

    //
    // Reset SMART status on media removal as the old status may not be
    // valid when there is no media in the device or when new media is
    // inserted.
    //

    if (NewState == MediaNotPresent) {

        FdoExtension->FailurePredicted = FALSE;
        FdoExtension->FailureReason = 0;

    }


    zero.QuadPart = 0;

    if(info == NULL) {
        return;
    }

    status = KeWaitForMutexObject(&info->MediaChangeMutex,
                                  Executive,
                                  KernelMode,
                                  FALSE,
                                  ((Wait == TRUE) ? NULL : &zero));

    if(status == STATUS_TIMEOUT) {

        //
        // Someone else is in the process of setting the media state
        //

        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
        return;
    }

    //
    // Change the media present state and signal an event, if applicable
    //

    ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange);

    KeReleaseMutex(&info->MediaChangeMutex, FALSE);

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "< ClasspSetMediaChangeStateEx"));

    return;
} // end ClassSetMediaChangeStateEx()

_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
ClassSetMediaChangeState(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ MEDIA_CHANGE_DETECTION_STATE NewState,
    _In_ BOOLEAN Wait
    )
{
    ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE);
    return;
}

/*++////////////////////////////////////////////////////////////////////////////

ClasspMediaChangeDetectionCompletion()

Routine Description:

    This routine handles the completion of the test unit ready irps used to
    determine if the media has changed.  If the media has changed, this code
    signals the named event to wake up other system services that react to
    media change (aka AutoPlay).

Arguments:

    DeviceObject - the object for the completion
    Irp - the IRP being completed
    Context - the SRB from the IRP

Return Value:

    NTSTATUS

--*/
NTSTATUS
ClasspMediaChangeDetectionCompletion(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
    PCLASS_PRIVATE_FDO_DATA fdoData;
    PMEDIA_CHANGE_DETECTION_INFO info;
    NTSTATUS status;
    BOOLEAN retryImmediately = FALSE;
    PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER) Context;

    _Analysis_assume_(Srb != NULL);

    //
    // Since the class driver created this request, it's completion routine
    // will not get a valid device object handed in.  Use the one in the
    // irp stack instead
    //

    DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
    fdoExtension = DeviceObject->DeviceExtension;
    fdoData = fdoExtension->PrivateFdoData;
    info         = fdoExtension->MediaChangeDetectionInfo;

    NT_ASSERT(info->MediaChangeIrp != NULL);
    NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp));

    /*
     *  HACK for IoMega 2GB Jaz drive:
     *  This drive spins down on its own to preserve the media.
     *  When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
     *  InterpretSenseInfo routine would then call ClassSendStartUnit to spin the media up, which defeats the
     *  purpose of the spindown.
     *  So in this case, make this into a successful TUR.
     *  This allows the drive to stay spun down until it is actually accessed again.
     *  (If the media were actually removed, TUR would fail with 2/3a/0 ).
     *  This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
     *  is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
     */

    if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
        TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {

        PVOID senseData = SrbGetSenseInfoBuffer(Srb);

        if (senseData) {

            BOOLEAN validSense = TRUE;
            UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
            UCHAR senseKey = 0;
            UCHAR additionalSenseCode = 0;

            validSense = ScsiGetSenseKeyAndCodes(senseData,
                                                 senseInfoBufferLength,
                                                 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
                                                 &senseKey,
                                                 &additionalSenseCode,
                                                 NULL);

            if (validSense &&
                senseKey == SCSI_SENSE_NOT_READY &&
                additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) {
                Srb->SrbStatus = SRB_STATUS_SUCCESS;
            }
        }
    }

    //
    // use InterpretSenseInfo routine to check for media state, and also
    // to call ClassError() with correct parameters.
    //
    status = STATUS_SUCCESS;
    if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {

        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.",
                    DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));

        InterpretSenseInfoWithoutHistory(DeviceObject,
                                         Irp,
                                         (PSCSI_REQUEST_BLOCK)Srb,
                                         IRP_MJ_SCSI,
                                         0,
                                         0,
                                         &status,
                                         NULL);
    }
    else {

        fdoData->LoggedTURFailureSinceLastIO = FALSE;

        if (!info->Gesn.Supported) {

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));

            //
            // success != media for GESN case
            //

            ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);

        }
        else {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
        }
    }

    if (info->Gesn.Supported) {

        if (status == STATUS_DATA_OVERRUN) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - Overrun"));
            status = STATUS_SUCCESS;
        }

        if (!NT_SUCCESS(status)) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status));
        } else {

            //
            // for GESN, need to interpret the results of the data.
            // this may also require an immediate retry
            //

            if (Irp->IoStatus.Information == 8 ) {
                ClasspInterpretGesnData(fdoExtension,
                                        (PVOID)info->Gesn.Buffer,
                                        &retryImmediately);
            }

        } // end of NT_SUCCESS(status)

    } // end of Info->Gesn.Supported

    //
    // free port-allocated sense buffer, if any.
    //

    if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
        FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
    }

    //
    // Remember the IRP and SRB for use the next time.
    //

    NT_ASSERT(IoGetNextIrpStackLocation(Irp));
    IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;

    //
    // Reset the MCN timer.
    //

    ClassResetMediaChangeTimer(fdoExtension);

    //
    // run a sanity check to make sure we're not recursing continuously
    //

    if (retryImmediately) {

        info->MediaChangeRetryCount++;

        if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) {

            //
            // Disable GESN on this device.
            // Create a work item to set the value in the registry
            //

            PIO_WORKITEM workItem;

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: Disabling GESN for device %p", DeviceObject));

            workItem = IoAllocateWorkItem(DeviceObject);

            if (workItem) {
                IoQueueWorkItem(workItem, ClasspDisableGesn, DelayedWorkQueue, workItem);
            }

            info->Gesn.Supported  = 0;
            info->Gesn.EventMask  = 0;
            info->Gesn.BufferSize = 0;
            info->MediaChangeRetryCount = 0;
            retryImmediately = FALSE;
        }

    } else {

        info->MediaChangeRetryCount = 0;

    }


    //
    // release the remove lock....
    //

    {
        UCHAR uniqueValue = 0;
        ClassAcquireRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
        ClassReleaseRemoveLock(DeviceObject, Irp);


        //
        // set the irp as not in use
        //
        {
#if DBG
        volatile LONG irpWasInUse;
        irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
            #if _MSC_FULL_VER != 13009111        // This compiler always takes the wrong path here.
                NT_ASSERT(irpWasInUse);
            #endif
#else
            InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
#endif
    }

        //
        // now send it again before we release our last remove lock
        //

        if (retryImmediately) {
            ClasspSendMediaStateIrp(fdoExtension, info, 0);
        }
        else {
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - not retrying immediately"));
        }

        //
        // release the temporary remove lock
        //

        ClassReleaseRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
    }

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspMediaChangeDetectionCompletion"));

    return STATUS_MORE_PROCESSING_REQUIRED;
}

/*++////////////////////////////////////////////////////////////////////////////

ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:


--*/
PIRP
ClasspPrepareMcnIrp(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMEDIA_CHANGE_DETECTION_INFO Info,
    IN BOOLEAN UseGesn
)
{
    PSCSI_REQUEST_BLOCK srb;
    PSTORAGE_REQUEST_BLOCK srbEx;
    PIO_STACK_LOCATION irpStack;
    PIO_STACK_LOCATION nextIrpStack;
    NTSTATUS status;
    PCDB cdb;
    PIRP irp;
    PVOID buffer;
    UCHAR bufferLength;
    ULONG srbFlags;
    ULONG timeOutValue;
    UCHAR cdbLength;
    PVOID dataBuffer;
    ULONG dataTransferLength;

    //
    // Setup the IRP to perform a test unit ready.
    //

    irp = Info->MediaChangeIrp;

    if (irp == NULL) {
        NT_ASSERT(irp);
        return NULL;
    }

    //
    // don't keep sending this if the device is being removed.
    //

    status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
    if (status == REMOVE_COMPLETE) {
        NT_ASSERT(status != REMOVE_COMPLETE);
        return NULL;
    }
    else if (status == REMOVE_PENDING) {
        ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
        return NULL;
    }
    else {
        NT_ASSERT(status == NO_REMOVE);
    }

    IoReuseIrp(irp, STATUS_NOT_SUPPORTED);

    /*
     *  For the driver that creates an IRP, there is no 'current' stack location.
     *  Step down one IRP stack location so that the extra top one
     *  becomes our 'current' one.
     */
    IoSetNextIrpStackLocation(irp);

    /*
     *  Cache our device object in the extra top IRP stack location
     *  so we have it in our completion routine.
     */
    irpStack = IoGetCurrentIrpStackLocation(irp);
    irpStack->DeviceObject = FdoExtension->DeviceObject;

    //
    // If the irp is sent down when the volume needs to be
    // verified, CdRomUpdateGeometryCompletion won't complete
    // it since it's not associated with a thread.  Marking
    // it to override the verify causes it always be sent
    // to the port driver
    //

    irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;

    nextIrpStack = IoGetNextIrpStackLocation(irp);
    nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb.Srb);

    //
    // Prepare the SRB for execution.
    //

    buffer = Info->SenseBuffer;
    bufferLength = Info->SenseBufferLength;

    NT_ASSERT(bufferLength > 0);
    RtlZeroMemory(buffer, bufferLength);

    srbFlags = FdoExtension->SrbFlags;
    SET_FLAG(srbFlags, Info->SrbFlags);

    timeOutValue = FdoExtension->TimeOutValue * 2;
    if (timeOutValue == 0) {

        if (FdoExtension->TimeOutValue == 0) {

            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                       "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
                       "is set to zero?! -- resetting to 10\n"));
            timeOutValue = 10 * 2;  // reasonable default

        } else {

            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                       "ClassSendTestUnitIrp: Someone set "
                       "srb->TimeOutValue to zero?! -- resetting to %x\n",
                       FdoExtension->TimeOutValue * 2));
            timeOutValue = FdoExtension->TimeOutValue * 2;

        }

    }

    if (!UseGesn) {
        nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
        irp->MdlAddress = NULL;

        SET_FLAG(srbFlags, SRB_FLAGS_NO_DATA_TRANSFER);

        //
        // Set SRB_FLAGS_NO_KEEP_AWAKE for non-cdrom devices if these requests should
        // not prevent devices from going to sleep.
        //
        if ((FdoExtension->DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) &&
            (ClasspScreenOff == TRUE)) {
            SET_FLAG(srbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
        }

        cdbLength = 6;
        dataBuffer = NULL;
        dataTransferLength = 0;

    } else {
        NT_ASSERT(Info->Gesn.Buffer);

        nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
        irp->MdlAddress = Info->Gesn.Mdl;

        SET_FLAG(srbFlags, SRB_FLAGS_DATA_IN);
        cdbLength = 10;
        dataBuffer = Info->Gesn.Buffer;
        dataTransferLength = Info->Gesn.BufferSize;
        timeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN

    }

    //
    // SRB used here is the MediaChangeSrb in _MEDIA_CHANGE_DETECTION_INFO.
    //
    srb = nextIrpStack->Parameters.Scsi.Srb;
    if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = (PSTORAGE_REQUEST_BLOCK)nextIrpStack->Parameters.Scsi.Srb;

        status = InitializeStorageRequestBlock(srbEx,
                                               STORAGE_ADDRESS_TYPE_BTL8,
                                               CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
                                               1,
                                               SrbExDataTypeScsiCdb16);
        if (!NT_SUCCESS(status)) {
            // should not happen
            NT_ASSERT(FALSE);
            return NULL;
        }

        srbEx->RequestTag         = SP_UNTAGGED;
        srbEx->RequestAttribute   = SRB_SIMPLE_TAG_REQUEST;
        srbEx->SrbFunction        = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->SrbStatus          = 0;
        srbEx->OriginalRequest    = irp;
        srbEx->SrbFlags           = srbFlags;
        srbEx->TimeOutValue       = timeOutValue;
        srbEx->DataBuffer         = dataBuffer;
        srbEx->DataTransferLength = dataTransferLength;

        SrbSetScsiStatus(srbEx, 0);
        SrbSetSenseInfoBuffer(srbEx, buffer);
        SrbSetSenseInfoBufferLength(srbEx, bufferLength);
        SrbSetCdbLength(srbEx, cdbLength);

        cdb = SrbGetCdb(srbEx);

    } else {
        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));

        srb->QueueTag        = SP_UNTAGGED;
        srb->QueueAction     = SRB_SIMPLE_TAG_REQUEST;
        srb->Length          = sizeof(SCSI_REQUEST_BLOCK);
        srb->Function        = SRB_FUNCTION_EXECUTE_SCSI;
        srb->SenseInfoBuffer = buffer;
        srb->SenseInfoBufferLength = bufferLength;
        srb->SrbStatus       = 0;
        srb->ScsiStatus      = 0;
        srb->OriginalRequest = irp;

        srb->SrbFlags        = srbFlags;
        srb->TimeOutValue    = timeOutValue;
        srb->CdbLength       = cdbLength;
        srb->DataBuffer      = dataBuffer;
        srb->DataTransferLength = dataTransferLength;

        cdb = (PCDB) &srb->Cdb[0];

    }

    if (cdb) {
        if (!UseGesn) {
            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
        } else {
            cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode =
                SCSIOP_GET_EVENT_STATUS;
            cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
            cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] =
                (UCHAR)((Info->Gesn.BufferSize) >> 8);
            cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] =
                (UCHAR)((Info->Gesn.BufferSize) & 0xff);
            cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest =
                Info->Gesn.EventMask;
        }
    }

    IoSetCompletionRoutine(irp,
                           ClasspMediaChangeDetectionCompletion,
                           srb,
                           TRUE,
                           TRUE,
                           TRUE);

    return irp;

}

/*++////////////////////////////////////////////////////////////////////////////

ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
VOID
ClasspSendMediaStateIrp(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMEDIA_CHANGE_DETECTION_INFO Info,
    IN ULONG CountDown
    )
{
    BOOLEAN requestPending = FALSE;
    LONG irpInUse;

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspSendMediaStateIrp"));

    if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) ||
         (FdoExtension->DevicePowerState != PowerDeviceD0)
         ) &&
        (!Info->MediaChangeIrpLost)) {

        //
        // the device may be stopped, powered down, or otherwise queueing io,
        // so should not timeout the autorun irp (yet) -- set to zero ticks.
        // scattered code relies upon this to not prematurely "lose" an
        // autoplay irp that was queued.
        //

        Info->MediaChangeIrpTimeInUse = 0;
    }

    //
    // if the irp is not in use, mark it as such.
    //

    irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0);

    if (irpInUse) {

        LONG timeInUse;

        timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse);

        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspSendMediaStateIrp: irp in use for "
                    "%x seconds when synchronizing for MCD\n", timeInUse));

        if (Info->MediaChangeIrpLost == FALSE) {

            if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) {

                //
                // currently set to five minutes.  hard to imagine a drive
                // taking that long to spin up.
                //

                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                            "CdRom%d: Media Change Notification has lost "
                            "it's irp and doesn't know where to find it.  "
                            "Leave it alone and it'll come home dragging "
                            "it's stack behind it.\n",
                            FdoExtension->DeviceNumber));
                Info->MediaChangeIrpLost = TRUE;
            }
        }

        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp - irpInUse"));
        return;

    }

    TRY {

        if (Info->MediaChangeDetectionDisableCount != 0) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckMediaState: device %p has "
                        " detection disabled \n", FdoExtension->DeviceObject));
            LEAVE;
        }

        if (FdoExtension->DevicePowerState != PowerDeviceD0) {

            //
            // It's possible that the device went to D3 while the screen was
            // off so we need to make sure that we send the IRP regardless
            // of the device's power state in order to wake the device back
            // up when the screen comes back on.
            // When the screen is off we set the SRB_FLAG_NO_KEEP_AWAKE flag
            // so that the lower driver does not power-up the device for this
            // request.  When the screen comes back on, however, we want to
            // resume checking for media presence so we no longer set the flag.
            // When the device is in D3 we also stop the polling timer as well.
            //

            //
            // NOTE: we don't increment the time in use until our power state
            // changes above.  this way, we won't "lose" the autoplay irp.
            // it's up to the lower driver to determine if powering up is a
            // good idea.
            //

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassCheckMediaState: device %p needs to powerup "
                        "to handle this io (may take a few extra seconds).\n",
                        FdoExtension->DeviceObject));
        }

        Info->MediaChangeIrpTimeInUse = 0;
        Info->MediaChangeIrpLost = FALSE;

        if (CountDown == 0) {

            PIRP irp;

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassCheckMediaState: timer expired\n"));

            if (Info->MediaChangeDetectionDisableCount != 0) {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                            "ClassCheckMediaState: detection disabled\n"));
                LEAVE;
            }

            //
            // Prepare the IRP for the test unit ready
            //

            irp = ClasspPrepareMcnIrp(FdoExtension,
                                      Info,
                                      Info->Gesn.Supported);

            //
            // Issue the request.
            //

            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
                        "ClasspSendMediaStateIrp: Device %p getting TUR "
                        " irp %p\n", FdoExtension->DeviceObject, irp));

            if (irp == NULL) {
                LEAVE;
            }


            //
            // note: if we send it to the class dispatch routines, there is
            //       a timing window here (since they grab the remove lock)
            //       where we'd be removed. ELIMINATE the window by grabbing
            //       the lock ourselves above and sending it to the lower
            //       device object directly or to the device's StartIo
            //       routine (which doesn't acquire the lock).
            //

            requestPending = TRUE;

            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "  ClasspSendMediaStateIrp - calling IoCallDriver."));
            IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
        }

    } FINALLY {

        if(requestPending == FALSE) {
#if DBG
            irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
            #if _MSC_FULL_VER != 13009111        // This compiler always takes the wrong path here.
                NT_ASSERT(irpInUse);
            #endif
#else
            InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
#endif
        }

    }

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp"));

    return;
} // end ClasspSendMediaStateIrp()

/*++////////////////////////////////////////////////////////////////////////////

ClassCheckMediaState()

Routine Description:

    This routine is called by the class driver to test for a media change
    condition and/or poll for disk failure prediction.  It should be called
    from the class driver's IO timer routine once per second.

Arguments:

    FdoExtension - the device extension

Return Value:

    none

--*/
VOID
ClassCheckMediaState(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
    LONG countDown;

    if(info == NULL) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassCheckMediaState: detection not enabled\n"));
        return;
    }

    //
    // Media change support is active and the IRP is waiting. Decrement the
    // timer.  There is no MP protection on the timer counter.  This code
    // is the only code that will manipulate the timer counter and only one
    // instance of it should be running at any given time.
    //

    countDown = InterlockedDecrement(&(info->MediaChangeCountDown));

    //
    // Try to acquire the media change event.  If we can't do it immediately
    // then bail out and assume the caller will try again later.
    //
    ClasspSendMediaStateIrp(FdoExtension,
                            info,
                            countDown);

    return;
} // end ClassCheckMediaState()

/*++////////////////////////////////////////////////////////////////////////////

ClassResetMediaChangeTimer()

Routine Description:

    Resets the media change count down timer to the default number of seconds.

Arguments:

    FdoExtension - the device to reset the timer for

Return Value:

    None

--*/
VOID
ClassResetMediaChangeTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;

    if(info != NULL) {
        InterlockedExchange(&(info->MediaChangeCountDown),
                            MEDIA_CHANGE_DEFAULT_TIME);
    }
    return;
} // end ClassResetMediaChangeTimer()

/*++////////////////////////////////////////////////////////////////////////////

ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
NTSTATUS
ClasspInitializePolling(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN BOOLEAN AllowDriveToSleep
    )
{
    PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;

    PMEDIA_CHANGE_DETECTION_INFO info;
    PIRP irp;

    PAGED_CODE();

    if (FdoExtension->MediaChangeDetectionInfo != NULL) {
        return STATUS_SUCCESS;
    }

    info = ExAllocatePoolZero(NonPagedPoolNx,
                              sizeof(MEDIA_CHANGE_DETECTION_INFO),
                              CLASS_TAG_MEDIA_CHANGE_DETECTION);

    if (info != NULL) {
        FdoExtension->KernelModeMcnContext.FileObject      = (PVOID)-1;
        FdoExtension->KernelModeMcnContext.DeviceObject    = (PVOID)-1;
        FdoExtension->KernelModeMcnContext.LockCount       = 0;
        FdoExtension->KernelModeMcnContext.McnDisableCount = 0;

        /*
         *  Allocate an IRP to carry the Test-Unit-Ready.
         *  Allocate an extra IRP stack location
         *  so we can cache our device object in the top location.
         */
        irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE);

        if (irp != NULL) {

            PVOID buffer;
            BOOLEAN GesnSupported = FALSE;

            buffer = ExAllocatePoolZero(
                         NonPagedPoolNxCacheAligned,
                         SENSE_BUFFER_SIZE_EX,
                         CLASS_TAG_MEDIA_CHANGE_DETECTION);

            if (buffer != NULL) {

                info->MediaChangeIrp = irp;
                info->SenseBuffer = buffer;
                info->SenseBufferLength = SENSE_BUFFER_SIZE_EX;

                //
                // Set default values for the media change notification
                // configuration.
                //

                info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
                info->MediaChangeDetectionDisableCount = 0;

                //
                // Assume that there is initially no media in the device
                // only notify upper layers if there is something there
                //

                info->MediaChangeDetectionState = MediaUnknown;

                info->MediaChangeIrpTimeInUse = 0;
                info->MediaChangeIrpLost = FALSE;

                //
                // setup all extra flags we'll be setting for this irp
                //
                info->SrbFlags = 0;
                if (AllowDriveToSleep) {
                    SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
                }
                SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
                SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
                SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);

                KeInitializeMutex(&info->MediaChangeMutex, 0x100);

                //
                // It is ok to support media change events on this
                // device.
                //

                FdoExtension->MediaChangeDetectionInfo = info;

                //
                // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
                //       when the device supports DVD (no need to
                //       check for FILE_DEVICE_DVD, as it's not a
                //       valid check).
                //

                if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {

                    NTSTATUS status;

                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                               "ClasspInitializePolling: Testing for GESN\n"));
                    status = ClasspInitializeGesn(FdoExtension, info);
                    if (NT_SUCCESS(status)) {
                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                                   "ClasspInitializePolling: GESN available "
                                   "for %p\n", FdoExtension->DeviceObject));
                        NT_ASSERT(info->Gesn.Supported );
                        NT_ASSERT(info->Gesn.Buffer     != NULL);
                        NT_ASSERT(info->Gesn.BufferSize != 0);
                        NT_ASSERT(info->Gesn.EventMask  != 0);
                        GesnSupported = TRUE;
                    } else {
                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                                   "ClasspInitializePolling: GESN *NOT* available "
                                   "for %p\n", FdoExtension->DeviceObject));
                    }
                }

                if (GesnSupported == FALSE) {
                    NT_ASSERT(info->Gesn.Supported == 0);
                    NT_ASSERT(info->Gesn.Buffer == NULL);
                    NT_ASSERT(info->Gesn.BufferSize == 0);
                    NT_ASSERT(info->Gesn.EventMask  == 0);
                    info->Gesn.Supported = 0; // just in case....
                }

                //
                // Register for screen state notification. Will use this to
                // determine user presence.
                //
                if (ScreenStateNotificationHandle == NULL) {
                    PoRegisterPowerSettingCallback(fdo,
                                                   &GUID_CONSOLE_DISPLAY_STATE,
                                                   &ClasspPowerSettingCallback,
                                                   NULL,
                                                   &ScreenStateNotificationHandle);
                }

                return STATUS_SUCCESS;
            }

            IoFreeIrp(irp);
        }

        FREE_POOL(info);
    }

    //
    // nothing to free here
    //
    return STATUS_INSUFFICIENT_RESOURCES;

} // end ClasspInitializePolling()

NTSTATUS
ClasspInitializeGesn(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMEDIA_CHANGE_DETECTION_INFO Info
    )
{
    PNOTIFICATION_EVENT_STATUS_HEADER header;
    CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown;
    PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
    NTSTATUS status = STATUS_NOT_SUPPORTED;
    PIRP irp;
    KEVENT event;
    BOOLEAN retryImmediately;
    ULONG i;
    ULONG atapiResets;
    ULONG srbFlags;

    PAGED_CODE();
    NT_ASSERT(Info == FdoExtension->MediaChangeDetectionInfo);

    //
    // read if we already know the abilities of the device
    //

    ClassGetDeviceParameter(FdoExtension,
                            CLASSP_REG_SUBKEY_NAME,
                            CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                            (PULONG)&detectionState);

    if (detectionState == ClassDetectionUnsupported) {
        goto ExitWithError;
    }

    //
    // check if the device has a hack flag saying never to try this.
    //

    if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
                  FDO_HACK_GESN_IS_BAD)) {

        ClassSetDeviceParameter(FdoExtension,
                                CLASSP_REG_SUBKEY_NAME,
                                CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                                ClassDetectionUnsupported);
        goto ExitWithError;

    }


    //
    // else go through the process since we allocate buffers and
    // get all sorts of device settings.
    //

    if (Info->Gesn.Buffer == NULL) {
        Info->Gesn.Buffer = ExAllocatePoolZero(
                                NonPagedPoolNxCacheAligned,
                                GESN_BUFFER_SIZE,
                                '??cS');
    }
    if (Info->Gesn.Buffer == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitWithError;
    }
    if (Info->Gesn.Mdl != NULL) {
        IoFreeMdl(Info->Gesn.Mdl);
    }
    Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer,
                                   GESN_BUFFER_SIZE,
                                   FALSE, FALSE, NULL);
    if (Info->Gesn.Mdl == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitWithError;
    }

    MmBuildMdlForNonPagedPool(Info->Gesn.Mdl);
    Info->Gesn.BufferSize = GESN_BUFFER_SIZE;
    Info->Gesn.EventMask = 0;

    //
    // all items are prepared to use GESN (except the event mask, so don't
    // optimize this part out!).
    //
    // now see if it really works. we have to loop through this because
    // many SAMSUNG (and one COMPAQ) drives timeout when requesting
    // NOT_READY events, even when the IMMEDIATE bit is set. :(
    //
    // using a drive list is cumbersome, so this might fix the problem.
    //

    deviceDescriptor = FdoExtension->DeviceDescriptor;
    atapiResets = 0;
    retryImmediately = TRUE;
    for (i = 0; i < 16 && retryImmediately == TRUE; i++) {

        irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE);
        if (irp == NULL) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto ExitWithError;
        }

        if (Info->MediaChangeSrb.Srb.Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
            srbFlags = Info->MediaChangeSrb.SrbEx.SrbFlags;
        } else {
            srbFlags = Info->MediaChangeSrb.Srb.SrbFlags;
        }
        NT_ASSERT(TEST_FLAG(srbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));

        //
        // replace the completion routine with a different one this time...
        //

        KeInitializeEvent(&event, SynchronizationEvent, FALSE);
        IoSetCompletionRoutine(irp,
                               ClassSignalCompletion,
                               &event,
                               TRUE, TRUE, TRUE);

        status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);

        if (status == STATUS_PENDING) {
            status = KeWaitForSingleObject(&event,
                                           Executive,
                                           KernelMode,
                                           FALSE,
                                           NULL);
            NT_ASSERT(NT_SUCCESS(status));
        }
        ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);

        if (SRB_STATUS(Info->MediaChangeSrb.Srb.SrbStatus) != SRB_STATUS_SUCCESS) {

            InterpretSenseInfoWithoutHistory(FdoExtension->DeviceObject,
                                             irp,
                                             &(Info->MediaChangeSrb.Srb),
                                             IRP_MJ_SCSI,
                                             0,
                                             0,
                                             &status,
                                             NULL);
        }

        if ((deviceDescriptor->BusType == BusTypeAtapi) &&
            (Info->MediaChangeSrb.Srb.SrbStatus == SRB_STATUS_BUS_RESET)
            ) {

            //
            // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
            // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
            // the two.  if we get this status four time consecutively,
            // stop trying this command.  it is too late to change ATAPI
            // at this point, so special-case this here. (07/10/2001)
            // NOTE: any value more than 4 may cause the device to be
            //       marked missing.
            //

            atapiResets++;
            if (atapiResets >= 4) {
                status = STATUS_IO_DEVICE_ERROR;
                goto ExitWithError;
            }
        }

        if (status == STATUS_DATA_OVERRUN) {
            status = STATUS_SUCCESS;
        }

        if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
            (status == STATUS_TIMEOUT) ||
            (status == STATUS_IO_DEVICE_ERROR) ||
            (status == STATUS_IO_TIMEOUT)
            ) {

            //
            // with these error codes, we don't ever want to try this command
            // again on this device, since it reacts poorly.
            //

            ClassSetDeviceParameter(FdoExtension,
                                    CLASSP_REG_SUBKEY_NAME,
                                    CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                                    ClassDetectionUnsupported);
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                       "Classpnp => GESN test failed %x for fdo %p\n",
                       status, FdoExtension->DeviceObject));
            goto ExitWithError;


        }

        if (!NT_SUCCESS(status)) {

            //
            // this may be other errors that should not disable GESN
            // for all future start_device calls.
            //

            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
                       "Classpnp => GESN test failed %x for fdo %p\n",
                       status, FdoExtension->DeviceObject));
            goto ExitWithError;
        }

        if (i == 0) {

            //
            // the first time, the request was just retrieving a mask of
            // available bits.  use this to mask future requests.
            //

            header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer);

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                       "Classpnp => Fdo %p supports event mask %x\n",
                       FdoExtension->DeviceObject, header->SupportedEventClasses));


            if (TEST_FLAG(header->SupportedEventClasses,
                          NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                           "Classpnp => GESN supports MCN\n"));
            }
            if (TEST_FLAG(header->SupportedEventClasses,
                          NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                           "Classpnp => GESN supports DeviceBusy\n"));
            }
            if (TEST_FLAG(header->SupportedEventClasses,
                          NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)) {

                if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
                              FDO_HACK_GESN_IGNORE_OPCHANGE)) {
                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                                "Classpnp => GESN supports OpChange, but "
                                "must ignore these events for compatibility\n"));
                    CLEAR_FLAG(header->SupportedEventClasses,
                               NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK);
                } else {
                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                                "Classpnp => GESN supports OpChange\n"));
                }
            }
            Info->Gesn.EventMask = header->SupportedEventClasses;

            //
            // realistically, we are only considering the following events:
            //    EXTERNAL REQUEST - this is being tested for play/stop/etc.
            //    MEDIA STATUS - autorun and ejection requests.
            //    DEVICE BUSY - to allow us to predict when media will be ready.
            // therefore, we should not bother querying for the other,
            // unknown events. clear all but the above flags.
            //

            Info->Gesn.EventMask &=
                NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK |
                NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK   |
                NOTIFICATION_MEDIA_STATUS_CLASS_MASK       |
                NOTIFICATION_DEVICE_BUSY_CLASS_MASK        ;


            //
            // HACKHACK - REF #0001
            // Some devices will *never* report an event if we've also requested
            // that it report lower-priority events.  this is due to a
            // misunderstanding in the specification wherein a "No Change" is
            // interpreted to be a real event.  what should occur is that the
            // device should ignore "No Change" events when multiple event types
            // are requested unless there are no other events waiting.  this
            // greatly reduces the number of requests that the host must send
            // to determine if an event has occurred. Since we must work on all
            // drives, default to enabling the hack until we find evidence of
            // proper firmware.
            //
            if (Info->Gesn.EventMask == 0) {

                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                           "Classpnp => GESN supported, but not mask we care "
                           "about (%x) for FDO %p\n",
                           header->SupportedEventClasses,
                           FdoExtension->DeviceObject));
                goto ExitWithError;

            } else if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) {

                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                           "Classpnp => GESN hack not required for FDO %p\n",
                           FdoExtension->DeviceObject));

            } else {

                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                           "Classpnp => GESN hack enabled for FDO %p\n",
                           FdoExtension->DeviceObject));
                Info->Gesn.HackEventMask = 1;

            }

        } else {

            //
            // not the first time looping through, so interpret the results.
            //

            status = ClasspInterpretGesnData(FdoExtension,
                                             (PVOID)Info->Gesn.Buffer,
                                             &retryImmediately);

            if (!NT_SUCCESS(status)) {

                //
                // This drive does not support GESN correctly
                //

                ClassSetDeviceParameter(FdoExtension,
                                        CLASSP_REG_SUBKEY_NAME,
                                        CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                                        ClassDetectionUnsupported);
                goto ExitWithError;
            }
        }

    } // end loop of GESN requests....

    //
    // we can only use this if it can be relied upon for media changes,
    // since we are (by definition) no longer going to be polling via
    // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
    // for this command (although a filter driver, such as one for burning
    // cd's, might still fake those errors).
    //
    // since we also rely upon NOT_READY events to change the cursor
    // into a "wait" cursor; GESN is still more reliable than other
    // methods, and includes eject button requests, so we'll use it
    // without DEVICE_BUSY in Windows Vista.
    //

    if (TEST_FLAG(Info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                   "Classpnp => Enabling GESN support for fdo %p\n",
                   FdoExtension->DeviceObject));
        Info->Gesn.Supported = TRUE;

        ClassSetDeviceParameter(FdoExtension,
                                CLASSP_REG_SUBKEY_NAME,
                                CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                                ClassDetectionSupported);

        return STATUS_SUCCESS;

    }

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
               "Classpnp => GESN available but not enabled for fdo %p\n",
               FdoExtension->DeviceObject));
    goto ExitWithError;

    // fall through...

ExitWithError:
    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
               "Classpnp => GESN support detection failed  for fdo %p with status %08x\n",
               FdoExtension->DeviceObject, status));


    if (Info->Gesn.Mdl) {
        IoFreeMdl(Info->Gesn.Mdl);
        Info->Gesn.Mdl = NULL;
    }
    FREE_POOL(Info->Gesn.Buffer);
    Info->Gesn.Supported  = 0;
    Info->Gesn.EventMask  = 0;
    Info->Gesn.BufferSize = 0;
    return STATUS_NOT_SUPPORTED;

}


//
//  Work item to set the hack flag in the registry to disable GESN
//  on devices that sends too many events
//

VOID
ClasspDisableGesn(
    IN PDEVICE_OBJECT Fdo,
    IN PVOID Context
    )
{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
    PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;

    PAGED_CODE();

    //
    // Set the hack flag in the registry
    //
    ClassSetDeviceParameter(fdoExtension,
                            CLASSP_REG_SUBKEY_NAME,
                            CLASSP_REG_MMC_DETECTION_VALUE_NAME,
                            ClassDetectionUnsupported);
    _Analysis_assume_(WorkItem != NULL);
    IoFreeWorkItem(WorkItem);
}

/*++////////////////////////////////////////////////////////////////////////////

ClassInitializeTestUnitPolling()

Routine Description:

    This routine will initialize MCN regardless of the settings stored
    in the registry.  This should be used with caution, as some devices
    react badly to constant io. (i.e. never spin down, continuously cycling
    media in changers, ejection of media, etc.)  It is highly suggested to
    use ClassInitializeMediaChangeDetection() instead.

Arguments:

    FdoExtension is the device to poll

    AllowDriveToSleep says whether to attempt to allow the drive to sleep
        or not.  This only affects system-known spin down states, so if a
        drive spins itself down, this has no effect until the system spins
        it down.

Return Value:

--*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClassInitializeTestUnitPolling(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ BOOLEAN AllowDriveToSleep
    )
{
    return ClasspInitializePolling(FdoExtension, AllowDriveToSleep);
} // end ClassInitializeTestUnitPolling()

/*++////////////////////////////////////////////////////////////////////////////

ClassInitializeMediaChangeDetection()

Routine Description:

    This routine checks to see if it is safe to initialize MCN (the back end
    to autorun) for a given device.  It will then check the device-type wide
    key "Autorun" in the service key (for legacy reasons), and then look in
    the device-specific key to potentially override that setting.

    If MCN is to be enabled, all neccessary structures and memory are
    allocated and initialized.

    This routine MUST be called only from the ClassInit() callback.

Arguments:

    FdoExtension - the device to initialize MCN for, if appropriate

    EventPrefix - unused, legacy argument.  Set to zero.

Return Value:

--*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
ClassInitializeMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ PUCHAR EventPrefix
    )
{
    PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
    NTSTATUS status;

    PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
                                                fdo->DriverObject);

    BOOLEAN disabledForBadHardware;
    BOOLEAN disabled;
    BOOLEAN instanceOverride;

    UNREFERENCED_PARAMETER(EventPrefix);

    PAGED_CODE();

    //
    // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
    //       called in the context of the ClassInitDevice callback. If called
    //       after then this check will have already been made and the
    //       once a second timer will not have been enabled.
    //

    disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation(
                                FdoExtension,
                                &(driverExtension->RegistryPath)
                                );

    if (disabledForBadHardware) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassInitializeMCN: Disabled due to hardware"
                    "limitations for this device"));
        return;
    }

    //
    // autorun should now be enabled by default for all media types.
    //

    disabled = ClasspIsMediaChangeDisabledForClass(
                    FdoExtension,
                    &(driverExtension->RegistryPath)
                    );

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                "ClassInitializeMCN: Class    MCN is %s\n",
                (disabled ? "disabled" : "enabled")));

    status = ClasspMediaChangeDeviceInstanceOverride(
                FdoExtension,
                &instanceOverride);  // default value

    if (!NT_SUCCESS(status)) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassInitializeMCN: Instance using default\n"));
    } else {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassInitializeMCN: Instance override: %s MCN\n",
                    (instanceOverride ? "Enabling" : "Disabling")));
        disabled = !instanceOverride;
    }

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                "ClassInitializeMCN: Instance MCN is %s\n",
                (disabled ? "disabled" : "enabled")));

    if (disabled) {
        return;
    }

    //
    // Do not allow drive to sleep for all types of devices initially.
    // For non-cdrom devices, allow devices to go to sleep if it's
    // unlikely a media change will occur (e.g. user not present).
    //
    ClasspInitializePolling(FdoExtension, FALSE);

    return;
} // end ClassInitializeMediaChangeDetection()

/*++////////////////////////////////////////////////////////////////////////////

ClasspMediaChangeDeviceInstanceOverride()

Routine Description:

    The user can override the global setting to enable or disable Autorun on a
    specific cdrom device via the control panel.  This routine checks and/or
    sets this value.

Arguments:

    FdoExtension - the device to set/get the value for
    Value        - the value to use in a set
    SetValue     - whether to set the value

Return Value:

    TRUE - Autorun is disabled
    FALSE - Autorun is not disabled (Default)

--*/
NTSTATUS
ClasspMediaChangeDeviceInstanceOverride(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    OUT PBOOLEAN Enabled
    )
{
    HANDLE                   deviceParameterHandle = NULL;  // cdrom instance key
    HANDLE                   driverParameterHandle = NULL;  // cdrom specific key
    RTL_QUERY_REGISTRY_TABLE queryTable[3];
    OBJECT_ATTRIBUTES        objectAttributes;
    UNICODE_STRING           subkeyName;
    NTSTATUS                 status = STATUS_UNSUCCESSFUL;
    ULONG                    alwaysEnable = FALSE;
    ULONG                    alwaysDisable = FALSE;
    ULONG                    i;

    PAGED_CODE();

    TRY {

        status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo,
                                          PLUGPLAY_REGKEY_DEVICE,
                                          KEY_ALL_ACCESS,
                                          &deviceParameterHandle
                                          );
        if (!NT_SUCCESS(status)) {

            //
            // this can occur when a new device is added to the system
            // this is due to cdrom.sys being an 'essential' driver
            //
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassMediaChangeDeviceInstanceDisabled: "
                        "Could not open device registry key [%lx]\n", status));
            LEAVE;
        }

        RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
        InitializeObjectAttributes(&objectAttributes,
                                   &subkeyName,
                                   OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                   deviceParameterHandle,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwCreateKey(&driverParameterHandle,
                             KEY_READ,
                             &objectAttributes,
                             0,
                             (PUNICODE_STRING) NULL,
                             REG_OPTION_NON_VOLATILE,
                             NULL);

        if (!NT_SUCCESS(status)) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassMediaChangeDeviceInstanceDisabled: "
                        "subkey could not be created. %lx\n", status));
            LEAVE;
        }

        //
        // Default to not changing autorun behavior, based upon setting
        // registryValue to zero.
        //

        for (i=0;i<2;i++) {

            RtlZeroMemory(&queryTable[0], sizeof(queryTable));

            queryTable[0].Flags         = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
            queryTable[0].DefaultType   = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
            queryTable[0].DefaultLength = 0;

            if (i==0) {
                queryTable[0].Name          = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME;
                queryTable[0].EntryContext  = &alwaysDisable;
                queryTable[0].DefaultData   = &alwaysDisable;
            } else {
                queryTable[0].Name          = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME;
                queryTable[0].EntryContext  = &alwaysEnable;
                queryTable[0].DefaultData   = &alwaysEnable;
            }

            //
            // don't care if it succeeds, since we set defaults above
            //

            RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                   (PWSTR)driverParameterHandle,
                                   queryTable,
                                   NULL,
                                   NULL);
        }

    } FINALLY {

        if (driverParameterHandle) ZwClose(driverParameterHandle);
        if (deviceParameterHandle) ZwClose(deviceParameterHandle);

    }

    if (alwaysEnable && alwaysDisable) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
                    "Both Enable and Disable set -- DISABLE"));
        NT_ASSERT(NT_SUCCESS(status));
        status = STATUS_SUCCESS;
        *Enabled = FALSE;

    } else if (alwaysDisable) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
                    "DISABLE"));
        NT_ASSERT(NT_SUCCESS(status));
        status = STATUS_SUCCESS;
        *Enabled = FALSE;

    } else if (alwaysEnable) {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
                    "ENABLE"));
        NT_ASSERT(NT_SUCCESS(status));
        status = STATUS_SUCCESS;
        *Enabled = TRUE;

    } else {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
                    "DEFAULT"));
        status = STATUS_UNSUCCESSFUL;

    }

    return status;

} // end ClasspMediaChangeDeviceInstanceOverride()

/*++////////////////////////////////////////////////////////////////////////////

ClasspIsMediaChangeDisabledDueToHardwareLimitation()

Routine Description:

    The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
    which to never enable MediaChangeNotification.

    The user can override the global setting to enable or disable Autorun on a
    specific cdrom device via the control panel.

Arguments:

    FdoExtension -
    RegistryPath - pointer to the unicode string inside
                   ...\CurrentControlSet\Services\Cdrom

Return Value:

    TRUE - no autorun.
    FALSE - Autorun may be enabled

--*/
BOOLEAN
ClasspIsMediaChangeDisabledDueToHardwareLimitation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PUNICODE_STRING RegistryPath
    )
{
    PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
    OBJECT_ATTRIBUTES objectAttributes = {0};
    HANDLE serviceKey = NULL;
    RTL_QUERY_REGISTRY_TABLE parameters[2] = {0};

    UNICODE_STRING deviceUnicodeString;
    ANSI_STRING deviceString;
    ULONG mediaChangeNotificationDisabled = FALSE;

    NTSTATUS status;


    PAGED_CODE();

    //
    // open the service key.
    //

    InitializeObjectAttributes(&objectAttributes,
                               RegistryPath,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    status = ZwOpenKey(&serviceKey,
                       KEY_READ,
                       &objectAttributes);

    NT_ASSERT(NT_SUCCESS(status));


    if(!NT_SUCCESS(status)) {

        //
        // always take the safe path.  if we can't open the service key,
        // disable autorun
        //

        return TRUE;

    }

    TRY {
        //
        // Determine if drive is in a list of those requiring
        // autorun to be disabled.  this is stored in a REG_MULTI_SZ
        // named AutoRunAlwaysDisable.  this is required as some autochangers
        // must load the disc to reply to ChkVerify request, causing them
        // to cycle discs continuously.
        //

        PWSTR nullMultiSz;
        PUCHAR vendorId;
        PUCHAR productId;
        PUCHAR revisionId;
        ULONG  length;
        ULONG  offset;

        deviceString.Buffer        = NULL;
        deviceUnicodeString.Buffer = NULL;

        //
        // there may be nothing to check against
        //

        if ((deviceDescriptor->VendorIdOffset == 0) &&
            (deviceDescriptor->ProductIdOffset == 0)) {
            LEAVE;
        }

        length = 0;

        if (deviceDescriptor->VendorIdOffset == 0) {
            vendorId = NULL;
        } else {
            vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
            length = (ULONG)strlen((PCSZ)vendorId);
        }

        if ( deviceDescriptor->ProductIdOffset == 0 ) {
            productId = NULL;
        } else {
            productId = (PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset;
            length += (ULONG)strlen((PCSZ)productId);
        }

        if ( deviceDescriptor->ProductRevisionOffset == 0 ) {
            revisionId = NULL;
        } else {
            revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
            length += (ULONG)strlen((PCSZ)revisionId);
        }

        //
        // allocate a buffer for the string
        //

        deviceString.Length = (USHORT)( length );
        deviceString.MaximumLength = deviceString.Length + 1;
        deviceString.Buffer = (PCHAR)ExAllocatePoolZero( NonPagedPoolNx,
                                                         deviceString.MaximumLength,
                                                         CLASS_TAG_AUTORUN_DISABLE );
        if (deviceString.Buffer == NULL) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassMediaChangeDisabledForHardware: Unable to alloc "
                        "string buffer\n" ));
            LEAVE;
        }

        //
        // copy strings to the buffer
        //
        offset = 0;

        if (vendorId != NULL) {
            RtlCopyMemory(deviceString.Buffer + offset,
                          vendorId,
                          strlen((PCSZ)vendorId));
            offset += (ULONG)strlen((PCSZ)vendorId);
        }

        if ( productId != NULL ) {
            RtlCopyMemory(deviceString.Buffer + offset,
                          productId,
                          strlen((PCSZ)productId));
            offset += (ULONG)strlen((PCSZ)productId);
        }
        if ( revisionId != NULL ) {
            RtlCopyMemory(deviceString.Buffer + offset,
                          revisionId,
                          strlen((PCSZ)revisionId));
            offset += (ULONG)strlen((PCSZ)revisionId);
        }

        NT_ASSERT(offset == deviceString.Length);

        #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1)
        deviceString.Buffer[deviceString.Length] = '\0';  // Null-terminated

        //
        // convert to unicode as registry deals with unicode strings
        //

        status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
                                               &deviceString,
                                               TRUE
                                               );
        if (!NT_SUCCESS(status)) {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                        "ClassMediaChangeDisabledForHardware: cannot convert "
                        "to unicode %lx\n", status));
            LEAVE;
        }

        //
        // query the value, setting valueFound to true if found
        //
        nullMultiSz = L"\0";
        parameters[0].QueryRoutine  = ClasspMediaChangeRegistryCallBack;
        parameters[0].Flags         = RTL_QUERY_REGISTRY_REQUIRED;
        parameters[0].Name          = L"AutoRunAlwaysDisable";
        parameters[0].EntryContext  = &mediaChangeNotificationDisabled;
        parameters[0].DefaultType   = REG_MULTI_SZ;
        parameters[0].DefaultData   = nullMultiSz;
        parameters[0].DefaultLength = 0;

        status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                        serviceKey,
                                        parameters,
                                        &deviceUnicodeString,
                                        NULL);

        if ( !NT_SUCCESS(status) ) {
            LEAVE;
        }

    } FINALLY {

        FREE_POOL( deviceString.Buffer );
        if (deviceUnicodeString.Buffer != NULL) {
            RtlFreeUnicodeString( &deviceUnicodeString );
        }

        ZwClose(serviceKey);
    }

    if (mediaChangeNotificationDisabled) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassMediaChangeDisabledForHardware: "
                    "Device is on disable list\n"));
        return TRUE;
    }
    return FALSE;

} // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()

/*++////////////////////////////////////////////////////////////////////////////

ClasspIsMediaChangeDisabledForClass()

Routine Description:

    The user must specify that AutoPlay is to run on the platform
    by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
    Services\<SERVICE>\Autorun:REG_DWORD:1.

    The user can override the global setting to enable or disable Autorun on a
    specific cdrom device via the control panel.

Arguments:

    FdoExtension -
    RegistryPath - pointer to the unicode string inside
                   ...\CurrentControlSet\Services\Cdrom

Return Value:

    TRUE - Autorun is disabled for this class
    FALSE - Autorun is enabled for this class

--*/
BOOLEAN
ClasspIsMediaChangeDisabledForClass(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PUNICODE_STRING RegistryPath
    )
{
    OBJECT_ATTRIBUTES objectAttributes = {0};
    HANDLE serviceKey = NULL;
    HANDLE parametersKey = NULL;
    RTL_QUERY_REGISTRY_TABLE parameters[3] = {0};

    UNICODE_STRING paramStr;

    //
    //  Default to ENABLING MediaChangeNotification (!)
    //

    ULONG mcnRegistryValue = 1;

    NTSTATUS status;


    PAGED_CODE();

    //
    // open the service key.
    //

    InitializeObjectAttributes(&objectAttributes,
                               RegistryPath,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    status = ZwOpenKey(&serviceKey,
                       KEY_READ,
                       &objectAttributes);

    NT_ASSERT(NT_SUCCESS(status));

    if(!NT_SUCCESS(status)) {

        //
        // return the default value, which is the
        // inverse of the registry setting default
        // since this routine asks if it's disabled
        //

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: Defaulting to %s\n",
                    (mcnRegistryValue ? "Enabled" : "Disabled")));
        return (BOOLEAN)(!mcnRegistryValue);

    }

    //
    // Open the parameters key (if any) beneath the services key.
    //

    RtlInitUnicodeString(&paramStr, L"Parameters");

    InitializeObjectAttributes(&objectAttributes,
                               &paramStr,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               serviceKey,
                               NULL);

    status = ZwOpenKey(&parametersKey,
                       KEY_READ,
                       &objectAttributes);

    if (!NT_SUCCESS(status)) {
        parametersKey = NULL;
    }



    //
    // Check for the Autorun value.
    //

    parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    parameters[0].Name          = L"Autorun";
    parameters[0].EntryContext  = &mcnRegistryValue;
    parameters[0].DefaultType   = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
    parameters[0].DefaultData   = &mcnRegistryValue;
    parameters[0].DefaultLength = sizeof(ULONG);

    // ignore failures
    RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
                           serviceKey,
                           parameters,
                           NULL,
                           NULL);

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
                "<Service>/Autorun flag = %d\n", mcnRegistryValue));

    if(parametersKey != NULL) {

        // ignore failures
        RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
                               parametersKey,
                               parameters,
                               NULL,
                               NULL);
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
                    "<Service>/Parameters/Autorun flag = %d\n",
                    mcnRegistryValue));
        ZwClose(parametersKey);

    }
    ZwClose(serviceKey);

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
                "Autoplay for device %p is %s\n",
                FdoExtension->DeviceObject,
                (mcnRegistryValue ? "on" : "off")
                ));

    //
    // return if it is _disabled_, which is the
    // inverse of the registry setting
    //

    return (BOOLEAN)(!mcnRegistryValue);
} // end ClasspIsMediaChangeDisabledForClass()

/*++////////////////////////////////////////////////////////////////////////////

ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
ClassEnableMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
    LONG oldCount;

    PAGED_CODE();

    if(info == NULL) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
                    "ClassEnableMediaChangeDetection: not initialized\n"));
        return;
    }

    (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
                                UserRequest,
                                KernelMode,
                                FALSE,
                                NULL);

    oldCount = --info->MediaChangeDetectionDisableCount;

    NT_ASSERT(oldCount >= 0);

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassEnableMediaChangeDetection: Disable count "
                "reduced to %d - ",
                info->MediaChangeDetectionDisableCount));

    if(oldCount == 0) {

        //
        // We don't know what state the media is in anymore.
        //

        ClasspInternalSetMediaChangeState(FdoExtension,
                                          MediaUnknown,
                                          FALSE
                                          );

        //
        // Reset the MCN timer.
        //

        ClassResetMediaChangeTimer(FdoExtension);

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD is enabled\n"));

    } else {

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD still disabled\n"));

    }


    //
    // Let something else run.
    //

    KeReleaseMutex(&info->MediaChangeMutex, FALSE);

    return;
} // end ClassEnableMediaChangeDetection()

/*++////////////////////////////////////////////////////////////////////////////

ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
ULONG BreakOnMcnDisable = FALSE;

_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
ClassDisableMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;

    PAGED_CODE();

    if(info == NULL) {
        return;
    }

    (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
                               UserRequest,
                               KernelMode,
                               FALSE,
                               NULL);

    info->MediaChangeDetectionDisableCount++;

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassDisableMediaChangeDetection: "
                "disable count is %d\n",
                info->MediaChangeDetectionDisableCount));

    KeReleaseMutex(&info->MediaChangeMutex, FALSE);

    return;
} // end ClassDisableMediaChangeDetection()

/*++////////////////////////////////////////////////////////////////////////////

ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!

Routine Description:

    This routine will cleanup any resources allocated for MCN.  It is called
    by classpnp during remove device, and therefore is not typically required
    by external drivers.

Arguments:

Return Value:

--*/
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
ClassCleanupMediaChangeDetection(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;

    PAGED_CODE()

    if(info == NULL) {
        return;
    }

    FdoExtension->MediaChangeDetectionInfo = NULL;

    if (info->Gesn.Mdl) {
        IoFreeMdl(info->Gesn.Mdl);
    }
    FREE_POOL(info->Gesn.Buffer);
    IoFreeIrp(info->MediaChangeIrp);
    FREE_POOL(info->SenseBuffer);
    FREE_POOL(info);
    return;
} // end ClassCleanupMediaChangeDetection()

/*++////////////////////////////////////////////////////////////////////////////

ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
NTSTATUS
ClasspMcnControl(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PIRP Irp,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PCOMMON_DEVICE_EXTENSION commonExtension =
        (PCOMMON_DEVICE_EXTENSION) FdoExtension;

    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer;

    PFILE_OBJECT fileObject = irpStack->FileObject;
    PFILE_OBJECT_EXTENSION fsContext = NULL;

    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    //
    // Check to make sure we have a file object extension to keep track of this
    // request.  If not we'll fail it before synchronizing.
    //

    TRY {

        if(fileObject != NULL) {
            fsContext = ClassGetFsContext(commonExtension, fileObject);
        }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL
            fsContext = &FdoExtension->KernelModeMcnContext;
        }

        if (fsContext == NULL) {

            //
            // This handle isn't setup correctly.  We can't let the
            // operation go.
            //

            status = STATUS_INVALID_PARAMETER;
            LEAVE;
        }

        if(request->PreventMediaRemoval) {

            //
            // This is a lock command.  Reissue the command in case bus or
            // device was reset and the lock was cleared.
            //

            ClassDisableMediaChangeDetection(FdoExtension);
            InterlockedIncrement((volatile LONG *)&(fsContext->McnDisableCount));

        } else {

            if(fsContext->McnDisableCount == 0) {
                status = STATUS_INVALID_DEVICE_STATE;
                LEAVE;
            }

            InterlockedDecrement((volatile LONG *)&(fsContext->McnDisableCount));
            ClassEnableMediaChangeDetection(FdoExtension);
        }

    } FINALLY {

        Irp->IoStatus.Status = status;

        FREE_POOL(Srb);

        ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp);
        ClassCompleteRequest(FdoExtension->DeviceObject,
                             Irp,
                             IO_NO_INCREMENT);
    }
    return status;
} // end ClasspMcnControl(

/*++////////////////////////////////////////////////////////////////////////////

ClasspMediaChangeRegistryCallBack()

Routine Description:

    This callback for a registry SZ or MULTI_SZ is called once for each
    SZ in the value.  It will attempt to match the data with the
    UNICODE_STRING passed in as Context, and modify EntryContext if a
    match is found.  Written for ClasspCheckRegistryForMediaChangeCompletion

Arguments:

    ValueName     - name of the key that was opened
    ValueType     - type of data stored in the value (REG_SZ for this routine)
    ValueData     - data in the registry, in this case a wide string
    ValueLength   - length of the data including the terminating null
    Context       - unicode string to compare against ValueData
    EntryContext  - should be initialized to 0, will be set to 1 if match found

Return Value:

    STATUS_SUCCESS
    EntryContext will be 1 if found

--*/
_Function_class_(RTL_QUERY_REGISTRY_ROUTINE)
_IRQL_requires_max_(PASSIVE_LEVEL)
_IRQL_requires_same_
NTSTATUS
ClasspMediaChangeRegistryCallBack(
    _In_z_ PWSTR ValueName,
    _In_ ULONG ValueType,
    _In_reads_bytes_opt_(ValueLength) PVOID ValueData,
    _In_ ULONG ValueLength,
    _In_opt_ PVOID Context,
    _In_opt_ PVOID EntryContext
    )
{
    PULONG valueFound;
    PUNICODE_STRING deviceString;
    PWSTR keyValue;

    PAGED_CODE();
    UNREFERENCED_PARAMETER(ValueName);

    if (ValueData == NULL ||
        Context == NULL ||
        EntryContext == NULL) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // if we have already set the value to true, exit
    //

    valueFound = EntryContext;
    if ((*valueFound) != 0) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMcnRegCB: already set to true\n"));
        return STATUS_SUCCESS;
    }

    if (ValueLength == sizeof(WCHAR)) {
        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMcnRegCB: NULL string should "
                    "never be passed to registry call-back!\n"));
        return STATUS_SUCCESS;
    }


    //
    // if the data is not a terminated string, exit
    //

    if (ValueType != REG_SZ) {
        return STATUS_SUCCESS;
    }

    deviceString = Context;
    keyValue = ValueData;
    ValueLength -= sizeof(WCHAR); // ignore the null character

    //
    // do not compare more memory than is in deviceString
    //

    if (ValueLength > deviceString->Length) {
        ValueLength = deviceString->Length;
    }

    //
    // if the strings match, disable autorun
    //

    if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: Match found\n"));
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: DeviceString at %p\n",
                    deviceString->Buffer));
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: KeyValue at %p\n",
                    keyValue));
        (*valueFound) = TRUE;
    }

    return STATUS_SUCCESS;
} // end ClasspMediaChangeRegistryCallBack()

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
VOID
ClasspTimerTickEx(
    _In_ PEX_TIMER Timer,
    _In_opt_ PVOID Context
)
{
    KDPC dummyDpc = { 0 }; 
    
    UNREFERENCED_PARAMETER(Timer);
    //
    // This is just a wrapper around ClasspTimerTick that allows us to make
    // the TickTimer a no-wake EX_TIMER.
    // We pass in a dummy DPC b/c ClasspTimerTick expects a non-NULL parameter
    // for the DPC.  However, ClasspTimerTick does not actually reference it.
    //
    ClasspTimerTick(&dummyDpc, Context, NULL, NULL);
}
#endif

/*++////////////////////////////////////////////////////////////////////////////

ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject -
    Irp -

Return Value:

--*/
_Function_class_(KDEFERRED_ROUTINE)
_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_min_(DISPATCH_LEVEL)
_IRQL_requires_(DISPATCH_LEVEL)
_IRQL_requires_same_
VOID
ClasspTimerTick(
    _In_ PKDPC Dpc,
    _In_opt_ PVOID DeferredContext,
    _In_opt_ PVOID SystemArgument1,
    _In_opt_ PVOID SystemArgument2
    )
{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeferredContext;
    PCOMMON_DEVICE_EXTENSION commonExtension;
    PDEVICE_OBJECT DeviceObject;
    ULONG isRemoved;

    UNREFERENCED_PARAMETER(Dpc);
    UNREFERENCED_PARAMETER(SystemArgument1);
    UNREFERENCED_PARAMETER(SystemArgument2);

    NT_ASSERT(fdoExtension != NULL);
    _Analysis_assume_(fdoExtension != NULL);

    commonExtension = &fdoExtension->CommonExtension;
    DeviceObject = fdoExtension->DeviceObject;
    NT_ASSERT(commonExtension->IsFdo);

    //
    // Do any media change work
    //
#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
    isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);

    //
    // We stop the timer before deleting the device.  It's safe to keep going
    // if the flag value is REMOVE_PENDING because the removal thread will be
    // blocked trying to stop the timer.
    //

    NT_ASSERT(isRemoved != REMOVE_COMPLETE);

    //
    // This routine is reasonably safe even if the device object has a pending
    // remove

    if (!isRemoved) {

        PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo;

        //
        // Do any media change detection work
        //

        if ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
            (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) {

            ClassCheckMediaState(fdoExtension);

        }

        //
        // Do any failure prediction work
        //
        if ((info != NULL) && (info->Method != FailurePredictionNone)) {

            ULONG countDown;

            if (ClasspCanSendPollingIrp(fdoExtension)) {

                //
                // Synchronization is not required here since the Interlocked
                // locked instruction guarantees atomicity. Other code that
                // resets CountDown uses InterlockedExchange which is also
                // atomic.
                //
                countDown = InterlockedDecrement((volatile LONG *)&info->CountDown);
                if (countDown == 0) {

                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspTimerTick: Send FP irp for %p\n",
                                   DeviceObject));

                    if(info->WorkQueueItem == NULL) {

                        info->WorkQueueItem =
                            IoAllocateWorkItem(fdoExtension->DeviceObject);

                        if(info->WorkQueueItem == NULL) {

                            //
                            // Set the countdown to one minute in the future.
                            // we'll try again then in the hopes there's more
                            // free memory.
                            //

                            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,  "ClassTimerTick: Couldn't allocate "
                                           "item - try again in one minute\n"));
                            InterlockedExchange((volatile LONG *)&info->CountDown, 60);

                        } else {

                            //
                            // Grab the remove lock so that removal will block
                            // until the work item is done.
                            //

                            ClassAcquireRemoveLock(fdoExtension->DeviceObject,
                                                   info->WorkQueueItem);

                            IoQueueWorkItem(info->WorkQueueItem,
                                            ClasspFailurePredict,
                                            DelayedWorkQueue,
                                            info);
                        }

                    } else {

                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  "ClasspTimerTick: Failure "
                                       "Prediction work item is "
                                       "already active for device %p\n",
                                    DeviceObject));

                    }
                } // end (countdown == 0)

            } else {
                //
                // If device is sleeping then just rearm polling timer
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
                            DeviceObject));
            }

        } // end failure prediction polling

        //
        // Give driver a chance to do its own specific work
        //

        if (commonExtension->DriverExtension->InitData.ClassTick != NULL) {

            commonExtension->DriverExtension->InitData.ClassTick(DeviceObject);

        } // end device specific tick handler
    } // end check for removed

#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
    ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
} // end ClasspTimerTick()

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
BOOLEAN
ClasspUpdateTimerNoWakeTolerance(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
/*
Routine Description:

    Updates the no-wake timer's tolerance based on system state.   

    If the timer is not allocated, initialized, or enabled then this function
    does nothing.

    If the timer is enabled but the no-wake tolerance has *not* changed from
    its previous value then this function does nothing.

    If the timer is enabled and the no-wake tolerance has changed from its
    previous value then this function *will* set/reset the tick timer.

Arguments:

    FdoExtension for the device that has the timer whose tolerance needs updating.

Returns:

    TRUE if the timer was set/reset.
    FALSE if the timer was not set/reset.

*/
{
    PCLASS_PRIVATE_FDO_DATA fdoData = NULL;

    if (FdoExtension->CommonExtension.IsFdo) {
        fdoData = FdoExtension->PrivateFdoData;
    }

    if (fdoData != NULL &&
        fdoData->TickTimer != NULL &&
        fdoData->TimerInitialized &&
        fdoData->TickTimerEnabled) {

        LONGLONG noWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);

        //
        // Set the no-wake tolerance to "unlimited" if the conditions below
        // are met.  An "unlimited" no-wake tolerance means that the timer
        // will *never* wake the processor if the processor is in a
        // low-power state.
        //  1. The screen is off.
        //  2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
        //  3. This is a disk device.
        // Otherwise the tolerance is set to the normal, default tolerable delay.
        //
        if (ClasspScreenOff &&
            FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
            FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
            noWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
        }

        //
        // The new tolerance is different from the current tolerance so we need
        // to set/reset the timer with the new tolerance value.
        //
        if (fdoData->CurrentNoWakeTolerance != noWakeTolerance) {
            EXT_SET_PARAMETERS parameters;
            LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
            LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.

            ExInitializeSetTimerParameters(&parameters);
            parameters.NoWakeTolerance = noWakeTolerance;
            fdoData->CurrentNoWakeTolerance = noWakeTolerance;

            ExSetTimer(fdoData->TickTimer,
                       dueTime,
                       period,
                       &parameters);
            
            return TRUE;
        }
    }

    return FALSE;
}
#endif

NTSTATUS
ClasspInitializeTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
/*
Routine Description:

    This routine will attempt to initialize the tick timer.
    The caller should call ClasspEnableTmer() to actually start the timer.

    If the caller just needs to check if the timer is initialized, the caller
    should simply check FdoExtension->PrivateFdoData->TimerInitialized rather
    than call this function.

    The caller should subsequently call ClasspDeleteTimer() when they are done
    with the timer.

Arguments:

    FdoExtension

Return Value:

    STATUS_SUCCESS if the timer is initialized (the timer may already have been
        initialized by a previous call).
    A non-success status if the timer is not initialized.

*/
{
    PCLASS_PRIVATE_FDO_DATA fdoData = NULL;

    if (FdoExtension->CommonExtension.IsFdo) {
        fdoData = FdoExtension->PrivateFdoData;
    }

    if (fdoData == NULL) {
        return STATUS_UNSUCCESSFUL;
    }

    if (fdoData->TimerInitialized == FALSE) {
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
        NT_ASSERT(fdoData->TickTimer == NULL);
        //
        // The tick timer is a no-wake timer, which means it will not wake
        // the processor while the processor is in a low power state until
        // the timer's no-wake tolerance is reached.
        //
        fdoData->TickTimer = ExAllocateTimer(ClasspTimerTickEx, FdoExtension, EX_TIMER_NO_WAKE);
        if (fdoData->TickTimer == NULL) {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
#else
        KeInitializeDpc(&fdoData->TickTimerDpc, ClasspTimerTick, FdoExtension);
        KeInitializeTimer(&fdoData->TickTimer);        
#endif
        fdoData->TimerInitialized = TRUE;
    }

    return STATUS_SUCCESS;
}

VOID
ClasspDeleteTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
/*
Routine Description:

    This routine will attempt to de-initialize and free the tick timer.
    This routine should only be called after a successful call to
    ClasspInitializeTimer().

Arguments:

    FdoExtension

Return Value:

    None.

*/
{
    PCLASS_PRIVATE_FDO_DATA fdoData = NULL;

    if (FdoExtension->CommonExtension.IsFdo) {
        fdoData = FdoExtension->PrivateFdoData;
        if (fdoData != NULL) {
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
            if (fdoData->TickTimer != NULL) {
                EXT_DELETE_PARAMETERS parameters;
                ExInitializeDeleteTimerParameters(&parameters);
                ExDeleteTimer(fdoData->TickTimer, TRUE, FALSE, &parameters);
                fdoData->TickTimer = NULL;
            }
#endif
            fdoData->TimerInitialized = FALSE;
            fdoData->TickTimerEnabled = FALSE;
        }
    }
}

/*++////////////////////////////////////////////////////////////////////////////

ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine will enable the tick timer.  ClasspInitializeTimer() should
    first be called to initialize the timer.  Use ClasspDisableTimer() to
    disable the timer and then call this function to re-enable it.

Arguments:

    FdoExtension

Return Value:

    None.

--*/
VOID
ClasspEnableTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PCLASS_PRIVATE_FDO_DATA fdoData = NULL;

    if (FdoExtension->CommonExtension.IsFdo) {
        fdoData = FdoExtension->PrivateFdoData;
    }

    if (fdoData != NULL) {
        //
        // The timer should have already been initialized, but if that's not
        // the case it's not the end of the world.  We can attempt to
        // initialize it now.
        //
        NT_ASSERT(fdoData->TimerInitialized);
        if (fdoData->TimerInitialized == FALSE) {
            NTSTATUS status;
            status = ClasspInitializeTimer(FdoExtension);
            if (NT_SUCCESS(status) == FALSE) {
                return;
            }
        }

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
        if (fdoData->TickTimer != NULL) {
            EXT_SET_PARAMETERS parameters;
            LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
            LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.

            ExInitializeSetTimerParameters(&parameters);

            //
            // Set the no-wake tolerance to "unlimited" if the conditions below
            // are met.  An "unlimited" no-wake tolerance means that the timer
            // will *never* wake the processor if the processor is in a
            // low-power state.
            //  1. The screen is off.
            //  2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
            //  3. This is a disk device.
            // Otherwise the tolerance is set to the normal tolerable delay.
            //
            if (ClasspScreenOff &&
                FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
                FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
                parameters.NoWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
            } else {
                parameters.NoWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);
            }

            fdoData->CurrentNoWakeTolerance = parameters.NoWakeTolerance;

            ExSetTimer(fdoData->TickTimer,
                       dueTime,
                       period,
                       &parameters);

            fdoData->TickTimerEnabled = TRUE;
        } else {
            NT_ASSERT(fdoData->TickTimer != NULL);
        }
#else
        //
        // Start the periodic tick timer using a coalescable timer with some delay
        //
        {
            LARGE_INTEGER timeout;
            timeout.QuadPart = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000) * (-1);
            KeSetCoalescableTimer(&fdoData->TickTimer,
                                  timeout, TICK_TIMER_PERIOD_IN_MSEC, TICK_TIMER_DELAY_IN_MSEC,
                                  &fdoData->TickTimerDpc);
            fdoData->TickTimerEnabled = TRUE;
        }
#endif

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  "ClasspEnableTimer: Periodic tick timer enabled "
                    "for device %p\n", FdoExtension->DeviceObject));

    }

} // end ClasspEnableTimer()

/*++////////////////////////////////////////////////////////////////////////////

ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    FdoExtension

Return Value:

--*/
VOID
ClasspDisableTimer(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
{
    PCLASS_PRIVATE_FDO_DATA fdoData = NULL;

    if (FdoExtension->CommonExtension.IsFdo) {
        fdoData = FdoExtension->PrivateFdoData;
    }

    if (fdoData && fdoData->TimerInitialized == TRUE) {

        //
        // we are only going to stop the actual timer in remove device routine
        // or when done transitioning to D3 (timer will be started again when
        // done transitioning to D0).
        //
        // it is the responsibility of the code within the timer routine to
        // check if the device is removed and not processing io for the final
        // call.
        // this keeps the code clean and prevents lots of bugs.
        //
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
        NT_ASSERT(fdoData->TickTimer != NULL);
        ExCancelTimer(fdoData->TickTimer, NULL);
#else
        KeCancelTimer(&fdoData->TickTimer);
#endif
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  "ClasspDisableTimer: Periodic tick timer disabled "
                    "for device %p\n", FdoExtension->DeviceObject));
        fdoData->TickTimerEnabled = FALSE;

    } else {

        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,  "ClasspDisableTimer: Timer never initialized\n"));

    }

    return;
} // end ClasspDisableTimer()

/*++////////////////////////////////////////////////////////////////////////////

ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented

Routine Description:

    This routine

Arguments:

    DeviceObject - Device object
    Context - Context (PFAILURE_PREDICTION_INFO)

Return Value:

Note:  this function can be called (via the workitem callback) after the paging device is shut down,
         so it must be PAGE LOCKED.
--*/
VOID
ClasspFailurePredict(
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID Context
    )
{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_WORKITEM workItem;
    STORAGE_PREDICT_FAILURE checkFailure = {0};
    SCSI_ADDRESS scsiAddress = {0};
    PFAILURE_PREDICTION_INFO Info = (PFAILURE_PREDICTION_INFO)Context;

    NTSTATUS status;

    if (Info == NULL) {
        NT_ASSERT(Info != NULL);
        return;
    }

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,  "ClasspFailurePredict: Polling for failure\n"));

    //
    // Mark the work item as inactive and reset the countdown timer.  we
    // can't risk freeing the work item until we've released the remove-lock
    // though - if we do it might get reused as a tag before we can release
    // the lock.
    //

    InterlockedExchange((volatile LONG *)&Info->CountDown, Info->Period);
    workItem = InterlockedExchangePointer(&(Info->WorkQueueItem), NULL);

    if (ClasspCanSendPollingIrp(fdoExtension)) {

        KEVENT event;
        PDEVICE_OBJECT topOfStack;
        PIRP irp = NULL;
        IO_STATUS_BLOCK ioStatus;
        NTSTATUS activateStatus = STATUS_UNSUCCESSFUL;

        //
        // Take an active reference on the device to ensure it is powered up
        // while we do the failure prediction query.
        //
        if (fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
            activateStatus = ClasspPowerActivateDevice(DeviceObject);
        }

        KeInitializeEvent(&event, SynchronizationEvent, FALSE);

        topOfStack = IoGetAttachedDeviceReference(DeviceObject);

        //
        // Send down irp to see if drive is predicting failure
        //

        irp = IoBuildDeviceIoControlRequest(
                        IOCTL_STORAGE_PREDICT_FAILURE,
                        topOfStack,
                        NULL,
                        0,
                        &checkFailure,
                        sizeof(STORAGE_PREDICT_FAILURE),
                        FALSE,
                        &event,
                        &ioStatus);


        if (irp != NULL) {


            status = IoCallDriver(topOfStack, irp);
            if (status == STATUS_PENDING) {
                (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
                status = ioStatus.Status;
            }


        } else {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }

        if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) {

            checkFailure.PredictFailure = 512;

            //
            // Send down irp to get scsi address
            //
            KeInitializeEvent(&event, SynchronizationEvent, FALSE);

            RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS));
            irp = IoBuildDeviceIoControlRequest(
                IOCTL_SCSI_GET_ADDRESS,
                topOfStack,
                NULL,
                0,
                &scsiAddress,
                sizeof(SCSI_ADDRESS),
                FALSE,
                &event,
                &ioStatus);

            if (irp != NULL) {


                status = IoCallDriver(topOfStack, irp);
                if (status == STATUS_PENDING) {
                    (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
                }

            }

            ClassNotifyFailurePredicted(fdoExtension,
                                    (PUCHAR)&checkFailure,
                                    sizeof(checkFailure),
                                    (BOOLEAN)(fdoExtension->FailurePredicted == FALSE),
                                    2,
                                    scsiAddress.PathId,
                                    scsiAddress.TargetId,
                                    scsiAddress.Lun);

            fdoExtension->FailurePredicted = TRUE;

        }

        ObDereferenceObject(topOfStack);

        //
        // Update the failure prediction query time and release the active
        // reference.
        //

        KeQuerySystemTime(&(Info->LastFailurePredictionQueryTime));

        if (NT_SUCCESS(activateStatus)) {
            ClasspPowerIdleDevice(DeviceObject);
        }
    }    

    ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem);
    IoFreeWorkItem(workItem);
    return;
} // end ClasspFailurePredict()

/*++////////////////////////////////////////////////////////////////////////////

ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented

Routine Description:

Arguments:

Return Value:

--*/
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
ClassNotifyFailurePredicted(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_reads_bytes_(BufferSize) PUCHAR Buffer,
    _In_ ULONG BufferSize,
    _In_ BOOLEAN LogError,
    _In_ ULONG UniqueErrorValue,
    _In_ UCHAR PathId,
    _In_ UCHAR TargetId,
    _In_ UCHAR Lun
    )
{
    PIO_ERROR_LOG_PACKET logEntry;
    EVENT_DESCRIPTOR eventDescriptor;
    PCLASS_DRIVER_EXTENSION driverExtension;

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,  "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject));

    //
    // Fire off a WMI event
    //
    ClassWmiFireEvent(FdoExtension->DeviceObject,
                                   (LPGUID)&StoragePredictFailureEventGuid,
                                   0,
                                   BufferSize,
                                   Buffer);
    //
    // Log an error into the eventlog
    //

    if (LogError)
    {
        logEntry = IoAllocateErrorLogEntry(
                            FdoExtension->DeviceObject,
                           sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG)));

        if (logEntry != NULL)
        {

            logEntry->FinalStatus     = STATUS_SUCCESS;
            logEntry->ErrorCode       = IO_WRN_FAILURE_PREDICTED;
            logEntry->SequenceNumber  = 0;
            logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
            logEntry->IoControlCode   = IOCTL_STORAGE_PREDICT_FAILURE;
            logEntry->RetryCount      = 0;
            logEntry->UniqueErrorValue = UniqueErrorValue;
            logEntry->DumpDataSize    = 3;

            logEntry->DumpData[0] = PathId;
            logEntry->DumpData[1] = TargetId;
            logEntry->DumpData[2] = Lun;

            //
            // Write the error log packet.
            //

            IoWriteErrorLogEntry(logEntry);
        }
    }

    //
    // Send ETW event if LogError is TRUE. ClassInterpretSenseInfo sets this
    // to FALSE. So if failure is predicted for the first time and UniqueErrorValue
    // is 4 (used by ClassInterpretSenseInfo) then send ETW event.
    //

    if ((LogError == TRUE) ||
        ((FdoExtension->FailurePredicted == FALSE) && (UniqueErrorValue == 4))) {

#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
        driverExtension = IoGetDriverObjectExtension(FdoExtension->DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);

        if ((driverExtension != NULL) && (driverExtension->EtwHandle != 0)) {
            EventDescCreate(&eventDescriptor,
                            1,    // Id
                            0,    // Version
                            0,    // Channel
                            0,    // Level
                            0,    // Task
                            0,    // OpCode
                            0);   // Keyword

            EtwWrite(driverExtension->EtwHandle,
                     &eventDescriptor,
                     NULL,
                     0,
                     NULL);
        }
    }

} // end ClassNotifyFailurePredicted()

/*++////////////////////////////////////////////////////////////////////////////

ClassSetFailurePredictionPoll()

Routine Description:

    This routine enables polling for failure prediction, setting the timer
    to fire every N seconds as specified by the PollingPeriod.

Arguments:

    FdoExtension - the device to setup failure prediction for.

    FailurePredictionMethod - specific failure prediction method to use
        if set to FailurePredictionNone, will disable failure detection

    PollingPeriod - if 0 then no change to current polling timer

Return Value:

    NT Status

--*/
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
ClassSetFailurePredictionPoll(
    _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    _In_ FAILURE_PREDICTION_METHOD FailurePredictionMethod,
    _In_ ULONG PollingPeriod
    )
{
    PFAILURE_PREDICTION_INFO info;
    NTSTATUS status;

    PAGED_CODE();

    if (FdoExtension->FailurePredictionInfo == NULL) {

        if (FailurePredictionMethod != FailurePredictionNone) {

            info = ExAllocatePoolZero(NonPagedPoolNx,
                                      sizeof(FAILURE_PREDICTION_INFO),
                                      CLASS_TAG_FAILURE_PREDICT);

            if (info == NULL) {

                return STATUS_INSUFFICIENT_RESOURCES;

            }

            KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE);

            info->WorkQueueItem = NULL;
            info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD;

            KeQuerySystemTime(&(info->LastFailurePredictionQueryTime));

        } else {

            //
            // FaultPrediction has not been previously initialized, nor
            // is it being initialized now. No need to do anything.
            //
            return STATUS_SUCCESS;

        }

        FdoExtension->FailurePredictionInfo = info;

    } else {

        info = FdoExtension->FailurePredictionInfo;

    }

    /*
     *  Make sure the user-mode thread is not suspended while we hold the synchronization event.
     */
    KeEnterCriticalRegion();

    (VOID)KeWaitForSingleObject(&info->Event,
                                 UserRequest,
                                 KernelMode,
                                 FALSE,
                                 NULL);


    //
    // Reset polling period and counter. Setup failure detection type
    //

    if (PollingPeriod != 0) {

        InterlockedExchange((volatile LONG *)&info->Period, PollingPeriod);
    }

    InterlockedExchange((volatile LONG *)&info->CountDown, info->Period);

    info->Method = FailurePredictionMethod;
    if (FailurePredictionMethod != FailurePredictionNone) {

        ClasspEnableTimer(FdoExtension);

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,  "ClassEnableFailurePredictPoll: Enabled for "
                    "device %p\n", FdoExtension->DeviceObject));

    } else {

        ClasspDisableTimer(FdoExtension);
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,  "ClassEnableFailurePredictPoll: Disabled for "
                    "device %p\n", FdoExtension->DeviceObject));
    }
    status = STATUS_SUCCESS;


    KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE);

    KeLeaveCriticalRegion();

    return status;
} // end ClassSetFailurePredictionPoll()

BOOLEAN
ClasspFailurePredictionPeriodMissed(
    _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
    )
/*
Routine Description:
    This routine can be used to determine if a failure prediction polling
    period has been missed.  That is, the time since the last failure
    prediction IOCTL has been sent is greater than the failure prediction
    polling period.  This can happen if failure prediction polling was
    disabled, such as when the device is in D3 or when the screen is off.

Parameters:
    FdoExtension - FDO extension.  The caller should make sure the FDO
        extension is valid.  The FailurePredictionInfo structure should also
        be valid and the failure prediction method should not be "none".

Returns:
    TRUE if there was one or more failure prediction polling periods that was
    missed.
    FALSE otherwise.
*/
{
    LARGE_INTEGER currentTime;
    LARGE_INTEGER timeDifference;                    
    BOOLEAN missedPeriod = FALSE;

    NT_ASSERT(FdoExtension);
    NT_ASSERT(FdoExtension->FailurePredictionInfo);
    NT_ASSERT(FdoExtension->FailurePredictionInfo->Method != FailurePredictionNone);

    //
    // Find the difference between the last failure prediction
    // query and the current time and convert it to seconds.
    //
    KeQuerySystemTime(&currentTime);
    timeDifference.QuadPart = currentTime.QuadPart - FdoExtension->FailurePredictionInfo->LastFailurePredictionQueryTime.QuadPart;
    timeDifference.QuadPart /= (10LL * 1000LL * 1000LL);

    if (timeDifference.QuadPart >= FdoExtension->FailurePredictionInfo->Period) {                    
        missedPeriod = TRUE;
    }

    return missedPeriod;
}




<|start_filename|>gnss/gnssUmdf/Device.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    device.h

Abstract:

    This file contains the device definitions.

Environment:

    Windows User-Mode Driver Framework

--*/

#pragma once

class CQueue;

class CDevice
{
public:
    // This is called from PnP manager in response to AddDevice call
    static EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd;

private:
    CDevice(_In_ WDFDEVICE Device);
    ~CDevice();

    static EVT_WDF_OBJECT_CONTEXT_CLEANUP OnCleanup;
    static EVT_WDF_DEVICE_D0_ENTRY OnD0Entry;
    static EVT_WDF_DEVICE_D0_EXIT OnD0Exit;

    NTSTATUS Initialize();
    CQueue* GetQueue();

    WDFDEVICE _Device = nullptr;
    CQueue* _Queue = nullptr;
};

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CDevice, GetDeviceObject);


<|start_filename|>audio/tests/PinResourceHelpers/HalfApp.h<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft. All rights reserved.
//
// File Name:
//
//  HalfApp.h
//
// Abstract:
//
//  CHalfApp declarations
//
// -------------------------------------------------------------------------------
#pragma once

#define BUF_LEN_IN_MS   1000 // 1 second

// ------------------------------------------------------------------------------
// Data flow
enum STACKWISE_DATAFLOW { render, capture };


// ------------------------------------------------------------------------------
// Structs used to hold results of format records
typedef struct
{
    WAVEFORMATEXTENSIBLE wfxEx;
    UINT32 fundamentalPeriodInFrames;
    UINT32 defaultPeriodInFrames;
    UINT32 minPeriodInFrames;
    UINT32 maxPeriodInFrames;
    UINT32 maxPeriodInFramesExtended;
} FORMAT_RECORD, * PFORMAT_RECORD;

struct DeviceDescriptor
{
    IMMDevice*              pDevice;
    LPWSTR                  pwstrAudioEndpointId;
    LPWSTR                  pwstrAudioEndpointFriendlyName;
    STACKWISE_DATAFLOW      dataFlow;
    EndpointConnectorType   eConnectorType;
    UINT                    uConnectorId;
    GUID                    mode;
    ULONG                   cModes;
    GUID*                   pModes;
    ULONG                   cFormatRecords;
    PFORMAT_RECORD          pFormatRecords;
    PWAVEFORMATEX           pPreferredFormat;
    UINT32                  u32DefaultPeriodicityInFrames;
    UINT32                  u32FundamentalPeriodicityInFrames;
    UINT32                  u32MinPeriodicityInFrames;
    UINT32                  u32MaxPeriodicityInFrames;
    bool                    bIsPortCls;
    bool                    bIsAVStream;
    bool                    bIsBluetooth;
    bool                    bIsSideband;
    bool                    bIsMVA;
};

class CHalfApp
{
public:
    // Basic pin infos
    wil::com_ptr_nothrow<IMMDevice>             m_pDevice;
    wil::unique_cotaskmem_string                m_pwstrDeviceId;
    wil::unique_cotaskmem_string                m_pwstrDeviceFriendlyName;
    STACKWISE_DATAFLOW                          m_DataFlow;
    EndpointConnectorType                       m_ConnectorType;
    UINT                                        m_uConnectorId;
    GUID                                        m_Mode;
    ULONG                                       m_cModes;
    GUID*                                       m_pModes; // All processing modes
    bool                                        m_bIsPortCls;
    bool                                        m_bIsAVStream;
    bool                                        m_bIsBluetooth;
    bool                                        m_bIsSideband;
    bool                                        m_bIsMVA;

    // Formats
    wil::unique_cotaskmem_ptr<WAVEFORMATEX>     m_pPreferredFormat; 
    UINT32                                      m_u32DefaultPeriodicityInFrames;
    UINT32                                      m_u32FundamentalPeriodicityInFrames;
    UINT32                                      m_u32MinPeriodicityInFrames;
    UINT32                                      m_u32MaxPeriodicityInFrames;

    PFORMAT_RECORD                              m_pFormatRecords;
    ULONG                                       m_cFormatRecords;

    wil::unique_cotaskmem_ptr<WAVEFORMATEX>     m_pCurrentFormat; 
    UINT32                                      m_u32CurrentDefaultPeriodicityInFrames;
    UINT32                                      m_u32CurrentFundamentalPeriodicityInFrames;
    UINT32                                      m_u32CurrentMinPeriodicityInFrames;
    UINT32                                      m_u32CurrentMaxPeriodicityInFrames;

    // Audio endpoint interfaces
    wil::com_ptr_nothrow<IAudioDeviceEndpoint>  m_pAudioDeviceEndpoint;
    wil::com_ptr_nothrow<IAudioEndpoint>        m_pAudioEndpoint;
    wil::com_ptr_nothrow<IAudioEndpointControl> m_pAudioEndpointControl;
    wil::com_ptr_nothrow<IAudioEndpointRT>      m_pAudioEndpointRT;
    wil::com_ptr_nothrow<IAudioClock>           m_pAudioClock;

    // Data buffer
    wil::unique_hlocal_ptr<BYTE>                m_pbSineToneDataBuffer;
    DWORD                                       m_dwSineToneDataBufferSize;
    DWORD                                       m_dwSineToneDataBufferPosition;

    // Stream
    HNSTIME                                     m_hnsPeriod = 0;
    FLOAT32                                     m_f32EndpointFrameRate = 0.F;
    HNSTIME                                     m_hnsEndpointLatency = 0;
    bool                                        m_bIsEventCapable = false;
    bool                                        m_bStreamInitialized = false;
    volatile bool                               m_bStreamThreadTerminate = false;
    bool                                        m_bYieldActive = false;
    bool                                        m_bIsBackupTimerRequired = true;

    wil::unique_handle                          m_hStreamThread;

    LPTHREAD_START_ROUTINE                      m_pStreamRoutine = nullptr;
    wil::unique_event_nothrow                   m_hProcessThreadStartedEvent;
    wil::unique_event_nothrow                   m_hTerminate;
    wil::unique_event_nothrow                   m_hEndpointBufferCompleteEvent;
    wil::unique_handle                          m_hTimer;

    wil::critical_section                       m_CritSec;

    typedef enum
    {
        ET_DO_NOTHING = 0x00,
        ET_TERMINATE = 0x01,
        ET_OBJECT_BUFFER_COMPLETE = 0x02,
        ET_CLIENT_RELEASE_BUFFER = 0x04,
        ET_TIMER = 0x08,
        ET_PAUSE_PUMP = 0x10,
        ET_RESUME_PUMP = 0x20,
    } TEventType;

    CHalfApp(
        _In_ DeviceDescriptor descriptor
    )
        : m_DataFlow(descriptor.dataFlow)
        , m_ConnectorType(descriptor.eConnectorType)
        , m_uConnectorId(descriptor.uConnectorId)
        , m_cModes(descriptor.cModes)
        , m_cFormatRecords(descriptor.cFormatRecords)
        , m_u32DefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames)
        , m_u32FundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames)
        , m_u32MinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames)
        , m_u32MaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames)
        , m_u32CurrentDefaultPeriodicityInFrames(descriptor.u32DefaultPeriodicityInFrames)
        , m_u32CurrentFundamentalPeriodicityInFrames(descriptor.u32FundamentalPeriodicityInFrames)
        , m_u32CurrentMinPeriodicityInFrames(descriptor.u32MinPeriodicityInFrames)
        , m_u32CurrentMaxPeriodicityInFrames(descriptor.u32MaxPeriodicityInFrames)
        , m_bIsPortCls(descriptor.bIsPortCls)
        , m_bIsAVStream(descriptor.bIsAVStream)
        , m_bIsBluetooth(descriptor.bIsBluetooth)
        , m_bIsSideband(descriptor.bIsSideband)
        , m_bIsMVA(descriptor.bIsMVA)
    {
        m_pDevice = descriptor.pDevice;
        m_pAudioDeviceEndpoint = nullptr;
        m_pAudioEndpointControl = nullptr;

        m_pwstrDeviceId = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointId, wcslen(descriptor.pwstrAudioEndpointId));
        m_pwstrDeviceFriendlyName = wil::make_cotaskmem_string_nothrow(descriptor.pwstrAudioEndpointFriendlyName, wcslen(descriptor.pwstrAudioEndpointFriendlyName));
        m_Mode = descriptor.mode;

        m_pModes = new GUID[descriptor.cModes];
        memcpy(m_pModes, descriptor.pModes, sizeof(GUID)*(descriptor.cModes));
        m_pFormatRecords = new FORMAT_RECORD[descriptor.cFormatRecords];
        memcpy(m_pFormatRecords, descriptor.pFormatRecords, sizeof(FORMAT_RECORD)*(descriptor.cFormatRecords));
        
        CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pPreferredFormat));
        CloneWaveFormat(descriptor.pPreferredFormat, wil::out_param(m_pCurrentFormat)); // Use preferred format as current format
    }

    ~CHalfApp(void)
    {
        if (m_pModes)
            delete[] m_pModes;
        if (m_pFormatRecords)
            delete[] m_pFormatRecords;
    }

    // Audio endpoint interfaces
    HRESULT    InitializeEndpoint();
    HRESULT    ReleaseEndpoint();
    HRESULT    StartEndpoint();
    HRESULT    StopEndpoint();
    HRESULT    ResetEndpoint();

    // Sine tone data buffer
    HRESULT    CreateSineToneDataBuffer(WAVEFORMATEX* pWfx);
    HRESULT    ReleaseSineToneDataBuffer();




    // Stream
    HRESULT    InitializeAndSetBuffer(HNSTIME hnsPeriod, UINT32 u32LatencyCoefficient);
    HRESULT    InitializeStream(HNSTIME requestedPeriodicity, UINT32 u32LatencyCoefficient);
    HRESULT    CleanupStream();
    HRESULT    StartStream();
    HRESULT    StopStream();
    static DWORD CALLBACK   RenderStreamRoutine(PVOID lpParameter);
    static DWORD CALLBACK   CaptureStreamRoutine(PVOID lpParameter);
    void        SignalAndWaitForThread();



    HRESULT SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley);
    void CancelTimer(HANDLE timer);
    HRESULT     GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition);

    // Multiple pin instances
    HRESULT    GetCurrentAvailiablePinInstanceCount(UINT32* pAvailablePinInstanceCount);
    HRESULT    GetSecondHalfApp(AUDIO_SIGNALPROCESSINGMODE secondMode, CHalfApp** ppSecondHalfApp);

    // Only for loopback testcases
    HRESULT    GetHostHalfApp(CHalfApp** ppHostHalfApp);
};

// {571BF784-A9D6-420A-BCA0-C579A8710236}
DEFINE_GUID(IID_IHalfAppContainer,
    0x571bf784, 0xa9d6, 0x420a, 0xbc, 0xa0, 0xc5, 0x79, 0xa8, 071, 0x02, 0x36);

DECLARE_INTERFACE_IID_(
IHalfAppContainer,
IUnknown,
"571BF784-A9D6-420A-BCA0-C579A8710236")
{
    STDMETHOD(GetHalfApp)(THIS_ CHalfApp ** ppHalfApp) PURE;
};


<|start_filename|>serial/serenum/string.c<|end_filename|>
/*++
Copyright (c) 1998  Microsoft Corporation

Module Name:

    STRING.C

Abstract:

    This module contains the functions used to parse the PNP COM ID
    and save it in the appropriate UNICODE STRINGS.  The main function
    that is called is Serenum_ParseData.  All other functions are called
    by this main function.


Environment:

    kernel mode only

Notes:



--*/

#include "pch.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, Serenum_ParseData)

// Called by ParseData:
#pragma alloc_text (PAGE, Serenum_GetDevDesc)
#pragma alloc_text (PAGE, Serenum_GetDevCompId)
#pragma alloc_text (PAGE, Serenum_GetDevClass)
#pragma alloc_text (PAGE, Serenum_GetDevSerialNo)
#pragma alloc_text (PAGE, Serenum_GetDevName)
#pragma alloc_text (PAGE, Serenum_GetDevPnPRev)
#pragma alloc_text (PAGE, Serenum_GetDevOtherID)

#pragma alloc_text (PAGE, Serenum_InitMultiString)
#pragma alloc_text (PAGE, Serenum_SzCopy)
#pragma alloc_text (PAGE, Serenum_StrLen)

// Called by the above functions:
#pragma alloc_text (PAGE, Serenum_FixptToAscii)
#pragma alloc_text (PAGE, Serenum_HToI)

#endif

NTSTATUS
Serenum_ParseData(        PFDO_DEVICE_DATA FdoData, 
   _In_reads_(BufferLen) PCHAR ReadBuffer, ULONG BufferLen,
                          PUNICODE_STRING hardwareIDs, PUNICODE_STRING compIDs,
                          PUNICODE_STRING deviceIDs, PUNICODE_STRING PDeviceDesc,
                          PUNICODE_STRING serialNo, PUNICODE_STRING pnpRev
                          )
/*++

Routine Description:
    Parses the PNP COM ID out of the buffer which is passed as the
    first parameter and then saves the appropriate IDs as
    UNICODE_STRINGS in the other passed parameters.

Return value:
    NTSTATUS

--*/

{
   LPSTR pOtherId = NULL;
   LPSTR pPnpRev = NULL;
   LPSTR pDevNodeName = NULL;
   LPSTR pSerNo = NULL;
   LPSTR pClass = NULL;
   LPSTR pCompIdStar = NULL;
   LPSTR pDesc = NULL;
   PCHAR pStrBuffer = NULL;

   NTSTATUS status = STATUS_SUCCESS;

   LPSTR pDevName = NULL;
   LPSTR pCompId = NULL;

   int start;

   BOOLEAN isMouse = FALSE;
   PCHAR pMouseID = NULL;
   ULONG mouseIDLen;

   UNREFERENCED_PARAMETER(BufferLen);
   PAGED_CODE();
   
   //
   // Allocate the string buffers
   //

   pStrBuffer = ExAllocatePoolZero(PagedPool, MAX_DEVNODE_NAME * 7 + 1,SERENUM_POOL_TAG);

   if (pStrBuffer == NULL) {
      status = STATUS_INSUFFICIENT_RESOURCES;
      goto DoneParsingErr;
   } else {
      PCHAR pCurBuffer = pStrBuffer;

      pOtherId = pCurBuffer;
      *pOtherId = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;

      pPnpRev = pCurBuffer;
      *pPnpRev = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;

      pDevNodeName = pCurBuffer;
      *pDevNodeName = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;

      pSerNo = pCurBuffer;
      *pSerNo = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;

      pClass = pCurBuffer;
      *pClass = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;

      pCompIdStar = pCurBuffer;
      pCompId = pCompIdStar + 1;
      *pCompIdStar = '\0';
      pCurBuffer += MAX_DEVNODE_NAME + 1;

      pDesc = pCurBuffer;
      *pDesc = '\0';
      pCurBuffer += MAX_DEVNODE_NAME;
   }

   start = Serenum_SzCopy ( NAME_SERENUM, pDevNodeName, MAX_DEVNODE_NAME);
   pDevName = pDevNodeName + start;

   start = 0;

   RtlInitUnicodeString(hardwareIDs, NULL);
   RtlInitUnicodeString(compIDs, NULL);
   RtlInitUnicodeString(deviceIDs, NULL);
   RtlInitUnicodeString(pnpRev, NULL);
   RtlInitUnicodeString(serialNo, NULL);


   //
   // OtherID
   //

   if(ReadBuffer!=NULL&&BufferLen>=MAX_DEVNODE_NAME){
       start = Serenum_GetDevOtherID(ReadBuffer, BufferLen, pOtherId);
   }

   if (start > 16||start==0) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                      ("Other ID string too long\n"));
      status = STATUS_UNSUCCESSFUL;
      goto DoneParsingErr;
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Other ID: %s\n", pOtherId));

   //
   // See if this is a mouse
   //

   SerenumScanOtherIdForMouse(ReadBuffer, BufferLen, &pMouseID, &mouseIDLen );

   if (pMouseID != NULL && (*pMouseID == 'M' || *pMouseID == 'B')) {
      isMouse = TRUE;
   }

   //
   // PNP revision number
   //

   status = Serenum_GetDevPnPRev(FdoData, ReadBuffer, BufferLen, pPnpRev, &start);

   if (!NT_SUCCESS(status)) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("PNP ID string bad\n"));
      goto DoneParsingErr;
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("PNP Revision: %s\n", pPnpRev));

   //
   // PNP device node name
   // EISA ID followed by Product ID
   //

   Serenum_GetDevName(ReadBuffer, BufferLen, pDevName, &start);
   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Node name: %s\n",
                                               pDevNodeName));

   //
   // Device serial number
   //
   if(ReadBuffer!=NULL){
       Serenum_GetDevSerialNo(ReadBuffer, BufferLen, pSerNo, &start);
   }

   if (Serenum_StrLen(pSerNo)) {
      //
      // This field exists - Make sure it is correct length.
      //

      if (Serenum_StrLen(pSerNo) != 8) {
         Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Serial number wrong"
                                                     " length\n"));
         *pSerNo = '\0';
         status = STATUS_UNSUCCESSFUL;
         goto DoneParsingErr;
      }
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Serial Number: %s\n", pSerNo));

   //
   // PNP class identifier
   //
   if(ReadBuffer!=NULL){
       Serenum_GetDevClass(ReadBuffer, BufferLen, pClass, &start);
   }

   if (Serenum_StrLen(pClass) > 32) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Class ID string too long\n"
                                                 ));
      status = STATUS_UNSUCCESSFUL;
      goto DoneParsingErr;
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Class: %s\n", pClass));

   if (_stricmp(pClass, "MOUSE") == 0) {
       RtlStringCchCopyA(pClass, MAX_DEVNODE_NAME, "SERIAL_MOUSE");
   }

   //
   // Compatible device ID
   //
   *pCompIdStar = '*';
   if(ReadBuffer!=NULL&&pCompId!=NULL){
       Serenum_GetDevCompId(ReadBuffer, BufferLen, pCompId, &start);
   }

   if (Serenum_StrLen(pCompId) > 40) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Compatible driver ID"
                                                  " string too long\n"));
      status = STATUS_UNSUCCESSFUL;
      goto DoneParsingErr;
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Compatible driver ID: %s\n",
                                               pCompId));

   //
   // End-user legible Product Description
   //
   if(ReadBuffer!=NULL){
       Serenum_GetDevDesc (ReadBuffer, BufferLen, pDesc, &start);
   }

   if (Serenum_StrLen(pDesc) > 40) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Device Description too"
                                                  " long\n"));
      status = STATUS_UNSUCCESSFUL;
      goto DoneParsingErr;
   }
   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Description: %s\n",
                                               pDesc));

   DoneParsingErr:
   if (pStrBuffer != NULL) {
      //
      // send back the good bits so that routine knows what driver to load
      //

      Serenum_InitMultiString (FdoData, hardwareIDs, pDevNodeName, pDevName,
                               NULL);

      if (Serenum_StrLen(pCompId) > 0) {
         if (!isMouse) {
            Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
                                    NULL);
         } else {
            Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
                                    "SERIAL_MOUSE", NULL);
         }
      } else {
         if (isMouse) {
            Serenum_InitMultiString(FdoData, compIDs, "SERIAL_MOUSE", NULL);
         }
      }

      Serenum_InitMultiString(FdoData, deviceIDs, pDevNodeName, NULL);

      Serenum_InitMultiString(FdoData, PDeviceDesc, pDesc, NULL);
      
      if (Serenum_StrLen(pSerNo)) {
         Serenum_InitMultiString(FdoData, serialNo, pSerNo, NULL);
      }
      if (Serenum_StrLen(pPnpRev)) {
         Serenum_InitMultiString(FdoData, pnpRev, pPnpRev, NULL);
      }

      ExFreePoolWithTag(pStrBuffer,SERENUM_POOL_TAG);
   }

   return status;
}

NTSTATUS
Serenum_InitMultiString(PFDO_DEVICE_DATA FdoData, PUNICODE_STRING MultiString,
                        ...)
/*++

    This routine will take a null terminated list of ascii strings and combine
    them together to generate a unicode multi-string block

Arguments:

    MultiString - a unicode structure in which a multi-string will be built
    ...         - a null terminated list of narrow strings which will be
             combined together. This list must contain at least a
        trailing NULL

Return Value:

    NTSTATUS

--*/
{
   ANSI_STRING ansiString;
   NTSTATUS status;
   PCSTR rawString;
   ULONG multiLength = 0;
   UNICODE_STRING unicodeString;
   va_list ap;
   ULONG i;

   PAGED_CODE();

#if !DBG
   UNREFERENCED_PARAMETER(FdoData);
#endif


   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                   ("Entering Serenum_InitMultiString\n"));

   va_start(ap,MultiString);

   //
   // Make sure that we won't leak memory
   //

   ASSERT(MultiString->Buffer == NULL);

   rawString = va_arg(ap, PCSTR);

   while (rawString != NULL) {
      RtlInitAnsiString(&ansiString, rawString);
      multiLength += RtlAnsiStringToUnicodeSize(&(ansiString));
      rawString = va_arg(ap, PCSTR);
   }

   va_end( ap );

   if (multiLength == 0) {
      //
      // Done
      //
      RtlInitUnicodeString(MultiString, NULL);
      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                      ("Leaving Serenum_InitMultiString (1)\n"));

      return STATUS_SUCCESS;
   }

   //
   // We need an extra null
   //
   multiLength += sizeof(WCHAR);

   MultiString->MaximumLength = (USHORT)multiLength;
   MultiString->Buffer = ExAllocatePoolZero(PagedPool, multiLength,SERENUM_POOL_TAG);
   MultiString->Length = 0;

   if (MultiString->Buffer == NULL) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                      ("Leaving Serenum_InitMultiString (2)\n"));

      return STATUS_INSUFFICIENT_RESOURCES;
   }

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                   ("Allocated %lu bytes for buffer\n", multiLength));

#if DBG
   RtlFillMemory(MultiString->Buffer, multiLength, 0xff);
#endif

   unicodeString.Buffer = MultiString->Buffer;
   unicodeString.MaximumLength = (USHORT) multiLength;
   unicodeString.Length = 0;

   va_start(ap, MultiString);
   rawString = va_arg(ap, PCSTR);

   while (rawString != NULL) {

      RtlInitAnsiString(&ansiString,rawString);
      status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE);
      if (!NT_SUCCESS(status)) {
        Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Serenum_InitMultiString: Call to RtlAnsiStringToUnicodeString Failed.\n"));
        return status;        
      }

      //
      // We don't allocate memory, so if something goes wrong here,
      // its the function that's at fault
      //
      ASSERT(NT_SUCCESS(status));

      //
      // Check for any commas and replace them with NULLs
      //

      ASSERT(unicodeString.Length % sizeof(WCHAR) == 0);

      for (i = 0; i < (unicodeString.Length / sizeof(WCHAR)); i++) {
         if (unicodeString.Buffer[i] == L'\x2C' ||
             unicodeString.Buffer[i] == L'\x0C' ) {
            unicodeString.Buffer[i] = L'\0';
         }
      }

      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer: %ws\n",
                                                  unicodeString.Buffer));

      //
      // Move the buffers along
      //
      unicodeString.Buffer += ((unicodeString.Length / sizeof(WCHAR)) + 1);
      unicodeString.MaximumLength -= (unicodeString.Length + sizeof(WCHAR));
      unicodeString.Length = 0;

      //
      // Next
      //

      rawString = va_arg(ap, PCSTR);
   } // while

   va_end(ap);

   ASSERT(unicodeString.MaximumLength == sizeof(WCHAR));

   //
   // Stick the final null there
   //

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer last addr: "
                                               "%p\n", unicodeString.Buffer));

   unicodeString.Buffer[0] = L'\0';

   //
   // Include the nulls in the length of the string
   //

   MultiString->Length = (USHORT)multiLength;
   MultiString->MaximumLength = MultiString->Length;

   Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                   ("Leaving Serenum_InitMultiString (3)\n"));

   return STATUS_SUCCESS;
}

int
Serenum_StrLen (
    _In_ LPSTR string)
// Measures the length of a string
{
    int i;
    PAGED_CODE();
    
    if (string == NULL) {
        return 0;
    }
    for (i=0; string[i] != '\0'; i++) {
    }
    return i;
}

int
Serenum_SzCopy (
    _In_              LPSTR source,
    _Out_writes_(len) PCHAR dest,
                      int len
    )
// Copies a string
// Assumes the buffer is already allocated to be copied into
{
    int i;

    PAGED_CODE();
    
    ASSERT (source);
    ASSERT (dest);

    for (i=0; source[i] != '\0' && i < len; i++) {
        *dest++ = source[i];
    }
    return i;
}

//
// String extraction functions:
//
int
Serenum_GetDevOtherID(
    _In_reads_(len)               PCHAR input,
                                  int   len,
     _Out_writes_(MAX_DEVNODE_NAME)LPSTR output
    )
{
    int tail;
    CHAR c;

    PAGED_CODE();
    
    tail = 0;
    c = input[tail++];
    while((tail < 17 ) && (c != '(') && (c != '(' - 0x20) && tail < len ) {
            *output++ = c;
#pragma warning(suppress: 6385)
            c = input[tail++];
    }
    *output = '\0';
    return(tail-1);
}


/****************************************************************************
 *
 *
 ***************************************************************************/
int
Serenum_HToI(char c)
{
    PAGED_CODE();
    
    if('0' <= c  &&  c <= '9')
    return(c - '0');

    if('A' <= c  &&  c <= 'F')
    return(c - 'A' + 10);

    if('a' <= c  &&  c <= 'f')
    return(c - 'a' + 10);

    return(-1);
}

void
Serenum_FixptToAscii(
    int n, 
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output)
/****************************************************************************
 *
 *
 ***************************************************************************/
{
    int tmp;

    PAGED_CODE();
    
    tmp = n / 100;

    if(tmp >= 10)
        *output++ = (CHAR)('0' + (tmp / 10));

    *output++ = (CHAR)('0' + (tmp % 10));
    *output++ = '.';

    tmp = n % 100;

    *output++ = (CHAR)('0' + (tmp / 10));
    *output++ = (CHAR)('0' + (tmp % 10));

    *output = '\0';
}

/****************************************************************************
 *
 *
 ***************************************************************************/
NTSTATUS
Serenum_GetDevPnPRev(              
                                   PFDO_DEVICE_DATA FdoData, 
    _In_reads_(len)               PCHAR input,  
                                   int len,
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
    _Inout_                        int   *start
    )
{
   int tail;
   int i;
   char delta;
   char c, begin_PnP, end_PnP_pos;
   int sum, chk_sum, msd, lsd;

   UNREFERENCED_PARAMETER(FdoData);
   PAGED_CODE();

   if (output == NULL  ||  input == NULL) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                      ("GetDevPnPRev Failed, NULL pointer!\n"));
      return STATUS_UNSUCCESSFUL;
   }
   __analysis_assume(len>=MAX_DEVNODE_NAME);
   *output = '\0';

   tail = *start;

   if ( tail >= len ) { 
       Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                       ("GetDevPnPRev Failed, insufficient input buffer\n"));
       return STATUS_UNSUCCESSFUL;
   }

   if (input[tail] == 0) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                      ("GetDevPnPRev Failed, input buffer empty!\n"));
      return STATUS_UNSUCCESSFUL;
   }
   
   c = input[tail++];

   while ((tail < 256) && (c != '(') && (c != '(' - 0x20) && tail < len) {
      c = input[tail++];
   }

   if (c != '('  &&  c != '(' - 0x20) {
      Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                      ("GetDevPnPRev Failed, no Begin PnP char!\n"));
      return STATUS_UNSUCCESSFUL;
   }

   begin_PnP = c;
   delta = '(' - begin_PnP;

   if ( tail+9 >= len ) {
       Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                       ("GetDevPnPRev Failed, insufficient input buffer\n"));
       return STATUS_UNSUCCESSFUL;
   } 

#pragma warning(suppress: 6385)
   if ( input[tail + 9] != ')' - delta) {

      //
      // compute checksum
      //

      sum = c;
      i = tail;
      while ( (i < 256)  &&  (c !=  ( ')' - delta)) && i < len ) {
         c = input[i++];
         sum += c;
      }

      if ( i < 3 || i-2 >= len) {
          Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                          ("GetDevPnPRev Failed, insufficient input buffer\n"));
          return STATUS_UNSUCCESSFUL;
      } 
      
      msd = input[i-3];
      lsd = input[i-2];
      
      sum -= msd;
      sum -= lsd;

      msd += delta;
      lsd += delta;

      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                      ("checksum from device  (chars) = %c%c\n", (char)msd,
                       (char)lsd));

      msd = Serenum_HToI((char)msd);
      if (msd < 0) {
         Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                         ("Bad msd checksum digit\n"));
         return STATUS_UNSUCCESSFUL;
      }

      lsd = Serenum_HToI((char)lsd);
      if (lsd < 0) {
         Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                         ("Bad lsd checksum digit\n"));
         return STATUS_UNSUCCESSFUL;
      }

      chk_sum = (msd << 4) + lsd;

      sum &= 0xff;

      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                      ("checksum read from device = %0x\n", chk_sum));
      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
                      ("Computed checksum = %0x\n", sum));

      end_PnP_pos = (char)i;

      if ( c - begin_PnP  !=  ')' - '(' ) {
         Serenum_KdPrint(FdoData,  SER_DBG_SS_ERROR,
                         ("GetDevPnPRev Failed,BeginPnP didn't match "
                          "EndPnP\n"));
         Serenum_KdPrint(FdoData,  SER_DBG_SS_ERROR,
                         ("begin_PnP = %02x   end_PnP = %02x\n", begin_PnP, c));
         return STATUS_UNSUCCESSFUL;
      }

      //
      // check the checksum
      //

      if (chk_sum != sum) {
         Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                         ("checksum Failed! Continuing...\n"));
         //          return STATUS_UNSUCCESSFUL;     // Commented out in Memphis
      }

      i = end_PnP_pos;

      if ( i < 3 || i-2 >= len) {
          Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                          ("GetDevPnPRev Failed, insufficient input buffer\n"));
          return STATUS_UNSUCCESSFUL;
      } 
      input[i-3] = ')' - delta;       // trash the checksum
      input[i-2] = '\0';              // since we are done with it
   }

   if ( tail+1 >= len ) {
       Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
                       ("GetDevPnPRev Failed, insufficient input buffer\n"));
       return STATUS_UNSUCCESSFUL;
   } 
   
   if (input[tail] > 0x3f ||
       input[tail+1] > 0x3f) {

      Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Bad PnP Rev digits\n"));
      return STATUS_UNSUCCESSFUL;
   }

   i = (input[tail++] & 0x3f) << 6;
   i |= (input[tail++]) & 0x3f;

   Serenum_FixptToAscii(i, output);

   i = tail;

   //
   // get ride of Mouse'output 0x20 bias in the string
   //
   while ( (i < 256) && delta && i < len ) {
      input[i] += delta;
      c = input[i++];
      if ( c == ')' ) {
         delta = 0;    // indicate we are done
      }
   }

   *start = tail;

   return STATUS_SUCCESS;
}

/****************************************************************************
 *
 *
 ***************************************************************************/
void Serenum_GetDevName(
    _In_reads_(len)          PCHAR input,
                              int   len,
    _Out_writes_(MAX_DEVNAME) PCHAR output,
    _Inout_                   int  *start
    )
{

    int tail;
    char c;

    PAGED_CODE();
    
    if(output == NULL  ||  input == NULL)
        return;

    tail = *start;

    // EISA ID
    if ( tail >= len ) { *start = tail; goto Exit; }
    *output++ = input[tail++];
    if ( tail >= len ) { *start = tail; goto Exit; }
    *output++ = input[tail++];
    if ( tail >= len ) { *start = tail; goto Exit; }
    *output++ = input[tail++];

    // Product ID

    if ( tail >= len ) { *start = tail; goto Exit; }
    c = input[tail++];
    if(Serenum_HToI(c) >= 0)
        *output++ = c;

    if ( tail >= len ) { *start = tail; goto Exit; }
    c = input[tail++];
    if(Serenum_HToI(c) >= 0)
        *output++ = c;

    if ( tail >= len ) { *start = tail; goto Exit; }
    c = input[tail++];
    if(Serenum_HToI(c) >= 0)
        *output++ = c;

    if ( tail >= len ) { *start = tail; goto Exit; }
    c = input[tail++];
    if(Serenum_HToI(c) >= 0)
        *output++ = c;

    Exit:
    *output = '\0';

    *start = tail;

    return;
}

/****************************************************************************
 *
 *
 ***************************************************************************/
void Serenum_GetDevSerialNo(
    _In_reads_(len)               PCHAR input,
                                   int   len,
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
    _Inout_                        int *start
    )
{

    int tail, cnt;
    char c;

    PAGED_CODE();

	*output = '\0';

    tail = *start;

    if ( tail >= len ) { return; }
    
    if( input[tail++] != '\\')
        return;

    if ( tail >= len ) { *start = tail-1; return; }
    c = input[tail++];

    cnt = 0;

    while(cnt < 8 && tail < 256 && ( c != '\\') && ( c != ')') && tail < len ) {
        cnt++;
        if(Serenum_HToI(c) < 0)
            break;

        *output++ = c;
        c = input[tail++];
    }

    *output = '\0';

    *start = tail - 1;

    return;
}

/****************************************************************************
 *
 *
 ***************************************************************************/
void Serenum_GetDevClass(
    _In_reads_(len)               PCHAR input,
                                   int   len,
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
    _Inout_                        int   *start
    )
{

    int tail, cnt;
    char c;

    PAGED_CODE();
    
    *output = '\0';

    tail = *start;

    if ( tail >= len ) { return; }
    
    if( input[tail++] != '\\')
        return;

    if ( tail >= len ) { *start = tail-1; return; }
    c = input[tail++];

    cnt = 0;

    while(tail < 256 && ( c != '\\') && ( c != ')') && tail < len && cnt < MAX_DEVNODE_NAME - 1) {
        cnt++;
        *output++ = c;
        c = input[tail++];
    }
    *output = '\0';

    *start = tail - 1;

    return;
}


void Serenum_GetDevCompId(
    _In_reads_(len)               PCHAR input,
                                   int   len,
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
    _Inout_                        int   *start
    )
/****************************************************************************
 *
 *
 ***************************************************************************/
{

    int tail, cnt;
    char c;

    PAGED_CODE();

    *output = '\0';

    tail = *start;

    if ( tail >= len ) { return; }

    if( input[tail++] != '\\')
        return;

    if ( tail >= len ) { *start = tail-1; return; }
    c = input[tail++];

    cnt = 0;

    while(tail < 256 && ( c != '\\') && ( c != ')') && tail < len && cnt < MAX_DEVNODE_NAME - 2 ) {
        cnt++;
        *output++ = c;
        //
        // Put a * after every comma
        //
        if ('\x0C' == c || '\x2C' == c) {
            cnt++;
            *output++ = '*';
        }
        c = input[tail++];
    }

    *output = '\0';

    *start = tail - 1;
}

void
Serenum_GetDevDesc(
    _In_reads_(len)               PCHAR input,
                                   int   len,
    _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
    _Inout_                        int   *start
    )
/****************************************************************************
 *
 *
 ***************************************************************************/
{

    int tail, cnt;
    char c;

    PAGED_CODE();

    *output = '\0';

    tail = *start;

    if ( tail >= len ) { return; }
    
    if( input[tail++] != '\\')
        return;

    if ( tail >= len ) { *start = tail-1; return; }
    c = input[tail++];

    cnt = 0;

    while(tail < 256 && ( c != '\\') && ( c != ')')  && tail < len && cnt < MAX_DEVNODE_NAME - 1) {
        cnt++;
        *output++ = c;    
        c = input[tail++];
    }

    *output = '\0';

    *start = tail - 1;
}




<|start_filename|>general/SimpleMediaSource/MediaSource/SimpleMediaStream.h<|end_filename|>
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//

#ifndef SIMPLEMEDIASTREAM_H
#define SIMPLEMEDIASTREAM_H

#include "SimpleMediaSource.h"

namespace winrt::WindowsSample::implementation
{
    struct SimpleMediaStream : winrt::implements<SimpleMediaStream, IMFMediaStream2>
    {
        friend struct SimpleMediaSource;

    public:
        // IMFMediaEventGenerator
        IFACEMETHODIMP BeginGetEvent(IMFAsyncCallback* pCallback, IUnknown* punkState) override;
        IFACEMETHODIMP EndGetEvent(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent) override;
        IFACEMETHODIMP GetEvent(DWORD dwFlags, IMFMediaEvent** ppEvent) override;
        IFACEMETHODIMP QueueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue) override;

        // IMFMediaStream
        IFACEMETHODIMP GetMediaSource(IMFMediaSource** ppMediaSource) override;
        IFACEMETHODIMP GetStreamDescriptor(IMFStreamDescriptor** ppStreamDescriptor) override;
        IFACEMETHODIMP RequestSample(IUnknown* pToken) override;

        // IMFMediaStream2
        IFACEMETHODIMP SetStreamState(_In_ MF_STREAM_STATE state) override;
        IFACEMETHODIMP GetStreamState(_Out_ MF_STREAM_STATE* pState) override;

        // Non-interface methods.
        HRESULT Initialize(_In_ SimpleMediaSource* pSource, _In_ DWORD streamId, _In_ MFSampleAllocatorUsage allocatorUsage);
        HRESULT Start();
        HRESULT Stop();
        HRESULT Shutdown();
        HRESULT SetSampleAllocator(_In_ IMFVideoSampleAllocator* pAllocator);

        DWORD Id() const { return m_dwStreamId; }
        MFSampleAllocatorUsage SampleAlloactorUsage() const { return m_allocatorUsage; }

        void SetRGBMask(ULONG rgbMask) { winrt::slim_lock_guard lock(m_Lock);  m_rgbMask = rgbMask; }
        ULONG GetRGBMask() { winrt::slim_lock_guard lock(m_Lock);  return m_rgbMask; }

    protected:
        HRESULT _CheckShutdownRequiresLock();
        HRESULT _SetStreamAttributes(IMFAttributes* pAttributeStore);
        HRESULT _SetStreamDescriptorAttributes(IMFAttributes* pAttributeStore);

    private:
        winrt::slim_mutex  m_Lock;

        wil::com_ptr_nothrow<IMFMediaSource> m_parent;
        wil::com_ptr_nothrow<IMFMediaEventQueue> m_spEventQueue;
        wil::com_ptr_nothrow<IMFAttributes> m_spAttributes;
        wil::com_ptr_nothrow<IMFStreamDescriptor> m_spStreamDesc;
        wil::com_ptr_nothrow<IMFVideoSampleAllocator> m_spSampleAllocator;
        wistd::unique_ptr<SimpleFrameGenerator> m_spFrameGenerator;

        bool m_bIsShutdown = false;
        MF_STREAM_STATE m_streamState = MF_STREAM_STATE_STOPPED;
        ULONG m_rgbMask = KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_BLUE;

        DWORD m_dwStreamId;
        MFSampleAllocatorUsage m_allocatorUsage;
    };
}

#endif


<|start_filename|>general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_usersvc/ServiceWin32API.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    ServiceWin32API.cpp

Abstract:

    Implements functions to start/run/stop the service.

Environment:

    User mode

--*/

#include "stdafx.h"

//
// Settings of the service
//
#define SERVICE_NAME             L"OsrUsbFx2UmUserSvc"
#define SERVICE_FLAGS_RUNNING    0x1

//
// Service context
//
SERVICE_STATUS_HANDLE SvcStatusHandle     = NULL;
volatile LONG         SvcControlFlags     = 0;
HANDLE                SvcStoppedEvent     = NULL;
HANDLE                SvcStopRequestEvent = NULL;
HANDLE                SvcStopWaitObject   = NULL;

//
// Device interface context
//
HCMNOTIFICATION   InterfaceNotificationHandle = NULL;
SRWLOCK           DeviceListLock              = SRWLOCK_INIT;
DEVICE_LIST_ENTRY DeviceList;

//
// Debugging support during development time
//
//#define DEBUG_SUPPORT

/*++

Routine Description:

    Entry point for the service.

Arguments:

    Argc - The number of command line arguments

    Argv - The array of command line arguments

Return Value:

    VOID

--*/
INT
wmain(
   INT    Argc,
   WCHAR *Argv[]
   )
{
    UNREFERENCED_PARAMETER(Argc);
    UNREFERENCED_PARAMETER(Argv);

    SERVICE_TABLE_ENTRY serviceTable[] =
    {
        { SERVICE_NAME, ServiceMain },
        { NULL, NULL }
    };

   return StartServiceCtrlDispatcher(serviceTable);
}

VOID WINAPI ServiceMain(
    DWORD Argc,
    PWSTR *Argv
    )
{
    DWORD Err = ERROR_SUCCESS;

    UNREFERENCED_PARAMETER(Argc);
    UNREFERENCED_PARAMETER(Argv);

    //
    // Initialize global variables
    //
    InitializeDeviceListHead(&DeviceList);

#ifdef DEBUG_SUPPORT

    while (!IsDebuggerPresent()) {
        Sleep(1000);
    }

    __debugbreak();

#endif

    SvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME,
                                                 ServiceCtrlHandler);

    if (SvcStatusHandle == NULL)
    {
        Err = GetLastError();
        goto cleanup;
    }

    UpdateServiceStatus(SvcStatusHandle,
                        SERVICE_START_PENDING,
                        ERROR_SUCCESS);

    //
    // Setup device interface context
    //
    Err = SetupDeviceInterfaceContext();

    UpdateServiceStatus(SvcStatusHandle,
                        SERVICE_START_PENDING,
                        ERROR_SUCCESS);

    //
    // Initialize device control
    //
    OsrFx2InitializeDevice();

    //
    // Register callback function for stop event
    //
    SvcStopRequestEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (SvcStopRequestEvent == NULL)
    {
        Err = GetLastError();
        goto cleanup;
    }

    if (!RegisterWaitForSingleObject(&SvcStopWaitObject,
                                     SvcStopRequestEvent,
                                     ServiceStopCallback,
                                     NULL,
                                     INFINITE,
                                     WT_EXECUTEONLYONCE | WT_EXECUTEINPERSISTENTTHREAD))
    {
        Err = GetLastError();
        goto cleanup;
    }

    //
    // Create stopped event for the running worker thread
    //
    SvcStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (SvcStoppedEvent == NULL)
    {
        Err = GetLastError();
        goto cleanup;
    }

    UpdateServiceStatus(SvcStatusHandle,
                        SERVICE_START_PENDING,
                        ERROR_SUCCESS);

    //
    // Queue the main service function for execution in a worker thread.
    //
    QueueUserWorkItem(&ServiceRunningWorkerThread,
                      NULL,
                      WT_EXECUTELONGFUNCTION);

    UpdateServiceStatus(SvcStatusHandle,
                        SERVICE_RUNNING,
                        ERROR_SUCCESS);

cleanup:

    if (Err != ERROR_SUCCESS)
    {
        ServiceStop(Err);
    }
}

VOID
WINAPI
ServiceCtrlHandler(
    DWORD Ctrl
    )
{
    switch (Ctrl)
    {
    case SERVICE_CONTROL_STOP:
        //
        // Set service stop event
        //
        UpdateServiceStatus(SvcStatusHandle, SERVICE_STOP_PENDING, ERROR_SUCCESS);
        SetEvent(SvcStopRequestEvent);
        break;

    default:
        break;
    }
}

#define SERVICE_WAIT_HINT_TIME 30000 // 30 seconds

BOOL
UpdateServiceStatus(
    __in_opt SERVICE_STATUS_HANDLE hSvcHandle,
    __in  DWORD  dwCurrentState,
    __in  DWORD  dwWin32ExitCode
    )
{
    SERVICE_STATUS SvcStatus;

    static DWORD dwCheckPoint = 1;

    SvcStatus.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
    SvcStatus.dwCurrentState            = dwCurrentState;
    SvcStatus.dwWin32ExitCode           = dwWin32ExitCode;
    SvcStatus.dwServiceSpecificExitCode = ERROR_SUCCESS;

    if (dwCurrentState == SERVICE_START_PENDING)
    {
        SvcStatus.dwControlsAccepted = 0;
    }
    else
    {
        SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    }

    if ((dwCurrentState == SERVICE_RUNNING) ||
        (dwCurrentState == SERVICE_STOPPED))
    {
        SvcStatus.dwCheckPoint = 0;
        SvcStatus.dwWaitHint = 0;
    }
    else
    {
        SvcStatus.dwCheckPoint = dwCheckPoint++;
        SvcStatus.dwWaitHint = SERVICE_WAIT_HINT_TIME;
    }

    return SetServiceStatus(hSvcHandle, &SvcStatus);
}

DWORD
WINAPI
ServiceRunningWorkerThread(
    _In_ PVOID lpThreadParameter
    )
{
    PDEVICE_CONTEXT    DeviceContext = NULL;
    PDEVICE_LIST_ENTRY Link          = NULL;

    UNREFERENCED_PARAMETER(lpThreadParameter);

    InterlockedOr(&SvcControlFlags, SERVICE_FLAGS_RUNNING);

    //
    // Periodically check if the service is stopping.
    //
    while ((InterlockedOr(&SvcControlFlags, 0) & SERVICE_FLAGS_RUNNING) != 0)
    {
        AcquireSRWLockShared(&DeviceListLock);

        for (Link = DeviceList.Flink; Link != &DeviceList; Link = Link->Flink)
        {
            DeviceContext = CONTAINING_DEVICE_RECORD(Link);

            OsrFx2ControlDevice(DeviceContext);
        }

        ReleaseSRWLockShared(&DeviceListLock);

        Sleep(2000);  // Simulate some lengthy operations.
    }

    //
    // Signal the stopped event.
    //
    SetEvent(SvcStoppedEvent);

    return 0;
}

VOID
CALLBACK
ServiceStopCallback(
    _In_ PVOID   lpParameter,
    _In_ BOOLEAN TimerOrWaitFired
    )
{
    UNREFERENCED_PARAMETER(TimerOrWaitFired);

    //
    // Since wait object can not be unregistered in callback function, queue
    // another thread
    //
    QueueUserWorkItem(ServiceStopWorkerThread,
                      lpParameter,
                      WT_EXECUTEDEFAULT);
}

DWORD
WINAPI
ServiceStopWorkerThread(
    _In_ PVOID lpThreadParameter
    )
{
    UNREFERENCED_PARAMETER(lpThreadParameter);

    ServiceStop(ERROR_SUCCESS);

    return 0;
}

VOID
ServiceStop(
    _In_ DWORD ExitCode
    )
{
    if (SvcStatusHandle == NULL)
    {
        return;
    }

    UpdateServiceStatus(SvcStatusHandle, SERVICE_STOP_PENDING, ExitCode);

    //
    // Notify the working thread to stop
    //
    if ((InterlockedOr(&SvcControlFlags, 0) & SERVICE_FLAGS_RUNNING) != 0)
    {
        InterlockedAnd(&SvcControlFlags, ~SERVICE_FLAGS_RUNNING);
        WaitForSingleObject(SvcStoppedEvent, INFINITE);
    }

    //
    // Clean up device context after the worker thread has finished.
    //
    CleanupDeviceInterfaceContext();

    //
    // cleanup work
    //
    if (SvcStopWaitObject != NULL)
    {
        UnregisterWait(SvcStopWaitObject);
    }

    if (SvcStopRequestEvent != NULL)
    {
        CloseHandle(SvcStopRequestEvent);
    }

    if (SvcStoppedEvent != NULL)
    {
        CloseHandle(SvcStoppedEvent);
    }

    UpdateServiceStatus(SvcStatusHandle, SERVICE_STOPPED, ExitCode);
}


<|start_filename|>network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.h<|end_filename|>
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   Copyright (c) 2014 Microsoft Corporation.  All Rights Reserved.
//
//   Module Name:
//      Framework_WFPSamplerCalloutDriver.h
//
//   Abstract:
//
//   Author:
//      <NAME>      (DHarper)
//
//   Revision History:
//
//      [ Month ][Day] [Year] - [Revision]-[ Comments ]
//      May       01,   2010  -     1.0   -  Creation
//      December  13,   2013  -     1.1   -  Add support for multiple injectors and redirectors, and 
//                                              add structure for serializing asynchronous 
//                                              FWPM_LAYER_STREAM injections
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H
#define FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H

extern "C"
{
#pragma warning(push)
#pragma warning(disable: 4201) /// NAMELESS_STRUCT_UNION
#pragma warning(disable: 4324) /// STRUCTURE_PADDED

   #include <ntifs.h>                    /// IfsKit\Inc
   #include <ntddk.h>                    /// Inc
   #include <wdf.h>                      /// Inc\WDF\KMDF\1.9
   #include <ndis.h>                     /// Inc
   #include <fwpmk.h>                    /// Inc
   #include <fwpsk.h>                    /// Inc
   #include <netioddk.h>                 /// Inc
   #include <ntintsafe.h>                /// Inc
   #include <ntstrsafe.h>                /// Inc
   #include <stdlib.h>                   /// SDK\Inc\CRT

#pragma warning(pop)
}

#include "Identifiers.h"                     /// ..\Inc
#include "ProviderContexts.h"                /// ..\Inc
#include "HelperFunctions_Include.h"         /// ..\SysLib
#include "HelperFunctions_ExposedCallouts.h" /// .
#include "ClassifyFunctions_Include.h"       /// .
#include "CompletionFunctions_Include.h"     /// .
#include "NotifyFunctions_Include.h"         /// .
#include "SubscriptionFunctions_Include.h"   /// .

#define WFPSAMPLER_CALLOUT_DRIVER_TAG (UINT32)'DCSW'

#define WPP_COMPID_LEVEL_LOGGER(COMPID,LEVEL)  (WPP_CONTROL(WPP_BIT_Error).Logger),
#define WPP_COMPID_LEVEL_ENABLED(COMPID,LEVEL) (WPP_CONTROL(WPP_BIT_Error).Level >= LEVEL)

typedef struct WFPSAMPLER_DEVICE_DATA_
{
   HANDLE*                  pEngineHandle;
   INJECTION_HANDLE_DATA*   ppIPv4InboundInjectionHandles[2];
   INJECTION_HANDLE_DATA*   ppIPv4OutboundInjectionHandles[2];
   INJECTION_HANDLE_DATA*   ppIPv6InboundInjectionHandles[2];
   INJECTION_HANDLE_DATA*   ppIPv6OutboundInjectionHandles[2];
   INJECTION_HANDLE_DATA*   ppInboundInjectionHandles[2];
   INJECTION_HANDLE_DATA*   ppOutboundInjectionHandles[2];
   HANDLE*                  ppRedirectionHandles[2];
}WFPSAMPLER_DEVICE_DATA, *PWFPSAMPLER_DEVICE_DATA;

typedef struct DEVICE_EXTENSION_
{
   VOID*            pRegistration;
   PCALLBACK_OBJECT pCallbackObject;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

extern PDEVICE_OBJECT g_pWDMDevice;

extern PIO_WORKITEM g_pPowerStateEnterIOWorkItem;
extern PIO_WORKITEM g_pPowerStateExitIOWorkItem;

extern WDFDRIVER g_WDFDriver;
extern WDFDEVICE g_WDFDevice;

extern HANDLE g_bfeSubscriptionHandle;

extern WFPSAMPLER_DEVICE_DATA g_WFPSamplerDeviceData;

extern DEVICE_EXTENSION g_deviceExtension;

extern BOOLEAN g_calloutsRegistered;

extern SERIALIZATION_LIST g_bsiSerializationList;

extern KSPIN_LOCK g_bpeSpinLock;

#endif /// FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/APOOSUpgradeTest.h<|end_filename|>
#include <stdafx.h>

class CAPOUpgradeTest
{
    CAPOUpgradeTest(){};
    ~CAPOUpgradeTest(){};

    BEGIN_TEST_CLASS(CAPOUpgradeTest)
        START_OSUPGRADE
        START_APPVERIFIFER_UPGRADE
        TEST_CLASS_PROPERTY(L"Owner", L"auddev")
        TEST_CLASS_PROPERTY(L"TestClassification", L"Feature")
        TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"audiodg.exe")
        TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"audioenginebaseapop.h")
        TEST_CLASS_PROPERTY(L"RunAs", L"Elevated")
        TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
        TEST_CLASS_PROPERTY(L"Ignore", L"false")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfileFile", L"Audio-Tests.wprp")
        TEST_CLASS_PROPERTY(L"EtwLogger:WPRProfile", L"MultimediaCategory.Verbose.File")
        END_APPVERIFIER
    END_TEST_CLASS()

public:
    TEST_METHOD_SETUP(setUpMethod);
    TEST_METHOD_CLEANUP(tearDownMethod);

protected:
    BEGIN_TEST_METHOD(TestUpgrade)
        COMMON_ONECORE_TEST_PROPERTIES
        TEST_METHOD_PROPERTY(L"Kits.TestId", L"B98FD3CB-2E6E-4152-BA85-3021149B8E86")
        TEST_METHOD_PROPERTY(L"Kits.TestId2", L"C467E942-EB43-477F-8E2A-01D07FEF90C4")
        TEST_METHOD_PROPERTY(L"Kits.TestName", L"Audio APO - Verify APOs Work After an OS Upgrade - TestUpgrade")
        TEST_METHOD_PROPERTY(L"Kits.ExpectedRuntime", L"60")
        TEST_METHOD_PROPERTY(L"Kits.TimeoutInMinutes", L"240")
        TEST_METHOD_PROPERTY(L"Kits.Description", L"Third Party APO Test: TestUpgrade")
        APO_TEST_PROPERTIES
    END_TEST_METHOD()
};

<|start_filename|>gnss/gnssUmdf/Driver.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    driver.cpp

Abstract:

    This file contains the driver entry points and callbacks.

Environment:

    Windows User-Mode Driver Framework

--*/

#include "precomp.h"
#include "Trace.h"
#include "Defaults.h"
#include "Driver.h"
#include "Device.h"

#include "Driver.tmh"


extern "C"
NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    NTSTATUS status = STATUS_SUCCESS;

    // Initialize WPP Tracing
    WPP_INIT_TRACING(DriverObject, RegistryPath);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");

    // Register a cleanup callback so that we can call WPP_CLEANUP when
    // the framework driver object is deleted during driver unload.
    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.EvtCleanupCallback = GnssUmdfEvtDriverContextCleanup;

    WDF_DRIVER_CONFIG config;
    WDF_DRIVER_CONFIG_INIT(&config,
                           CDevice::OnDeviceAdd);

    config.EvtDriverUnload = OnDriverUnload;

    status = WdfDriverCreate(DriverObject,
                             RegistryPath,
                             &attributes,
                             &config,
                             WDF_NO_HANDLE);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);

        WPP_CLEANUP(DriverObject);

        return status;
    }

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");

    return status;
}

void
GnssUmdfEvtDriverContextCleanup(
    _In_ WDFOBJECT DriverObject
)
/*++
Routine Description:

    Free all the resources allocated in DriverEntry.

Arguments:

    DriverObject - handle to a WDF Driver object.

Return Value:

    void.

--*/
{
    UNREFERENCED_PARAMETER(DriverObject);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");

    // Stop WPP Tracing
    WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)DriverObject));
}

void
OnDriverUnload(
    _In_ WDFDRIVER /* Driver */
)
{
    WPP_CLEANUP(Driver);
}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/stdafx.h<|end_filename|>
//
// stdafx.h -- Copyright (c) Microsoft Corporation
//
// Author: zaneh
//
// Description:
//
//   Include file for well-known headers
//
#pragma once

#define _USE_MATH_DEFINES
#include <cmath>
#include <vector>

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#define NO_MOCK_VALIDATION
#include <mock.h>
#include <Windows.Media.Devices.h>
#include <windows.h>
#include <versionhelpers.h>
#include <WexTestClass.h>

#include "wil\resource.h"
#include "wil\com.h"


// System Effect Specifiers. These help determine which type of system
// effect the device is attempting to use in order to test it properly
#define DT_GFX          0x00000002
#define DT_LFX          0x00000004
#define DT_LGFX         (DT_LFX | DT_GFX)
#define DT_SFX          0x00000008
#define DT_MFX          0x00000010
#define DT_EFX          0x00000020
#define DT_SMEFX        (DT_SFX | DT_MFX | DT_EFX)

// Reg Key definitions for disabling AudioDG crashing on an APO exception
#define PREVENT_APOTEST_CRASH_OR_REPORT_ON_APO_EXCEPTION L"PreventAPOTestCrashOrReportOnAPOException"
#define REGKEY_AUDIO_SOFTWARE  L"Software\\Microsoft\\Windows\\CurrentVersion\\Audio"
#define REGKEY_DEFAULT L"(default)"

// Test logging macros
#ifndef LOG_ERROR
#define LOG_ERROR(fmt, ...) WEX::Logging::Log::Error(WEX::Common::String().Format(fmt L"\n\t\tFunction: " \
                            __FUNCTION__ L" Line: %d", __VA_ARGS__, __LINE__))
#endif

#ifndef LOG_OUTPUT
#define LOG_OUTPUT(fmt, ...)  WEX::Logging::Log::Comment(WEX::Common::String().Format(fmt, __VA_ARGS__))
#endif

#define LOG_RETURN_IF_FAILED(hr, fmt, ...) if (FAILED(hr)) { LOG_ERROR(fmt, __VA_ARGS__); \
                            WEX::Logging::Log::Error(WEX::Common::String().Format(L"\t\tError Code: (0x%08lx)" \
                            , hr)); return hr; }
#define LOG_RETURN_VOID_IF_FAILED(hr, fmt, ...) if(FAILED(hr)) { LOG_ERROR(fmt, __VA_ARGS__); \
                            WEX::Logging::Log::Error(WEX::Common::String().Format(L"\t\tError Code: (0x%08lx)" \
                            , hr)); return; }
#define LOG_RETURN_HR_IF(condition, hr, fmt, ...) if (condition) { LOG_ERROR(fmt, __VA_ARGS__); \
                            WEX::Logging::Log::Error(WEX::Common::String().Format(L"\t\tError Code: (0x%08lx)" \
                            , hr)); return hr; }
// End Test logging macros

// Begin Metadata defines
#define COMMON_ONECORE_TEST_PROPERTIES \
    COMMON_TEST_PROPERTIES \
    TEST_METHOD_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86") \
    TEST_METHOD_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64") \
    TEST_METHOD_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM") \
    TEST_METHOD_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64") \
    TEST_METHOD_PROPERTY(L"Kits.MinRelease", L"RS5") \
    TEST_METHOD_PROPERTY(L"Kits.CorePackageComposition", L"OneCoreUAP") \
    TEST_METHOD_PROPERTY(L"Kits.CorePackageComposition", L"OneCore") \
    TEST_METHOD_PROPERTY(L"Kits.CorePackageComposition", L"Full") \
    
#define COMMON_TEST_PROPERTIES \
    TEST_METHOD_PROPERTY(L"Kits.TestType", L"Development") \
    TEST_METHOD_PROPERTY(L"Kits.RunElevated", L"True") \
    TEST_METHOD_PROPERTY(L"Kits.RequiresReboot", L"False") \
    TEST_METHOD_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation") \
    TEST_METHOD_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration") \
    TEST_METHOD_PROPERTY(L"Kits.HasSupplementalContent", L"False") \

#define APO_TEST_PROPERTIES \
    TEST_METHOD_PROPERTY(L"Kits.Specification", L"Device.Audio.APO.ThirdParty") \

#define START_APPVERIFIFER \
    TEST_CLASS_PROPERTY(L"Kits.TestTask", L"AppVerifierStart") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.Stage", L"Setup") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.RunElevated", L"True") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.CommandLine", L"cmd.exe /c appverif -enable exceptions handles heaps leak locks memory srwlock threadpool tls -for te.processhost.exe te.exe audiodg.exe") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.Order", L"1") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.ExpectedExitCode", L"0") \

#define END_APPVERIFIER \
    TEST_CLASS_PROPERTY(L"Kits.TestTask", L"AppVerifierEnd") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierEnd.Stage", L"Cleanup") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierEnd.RunElevated", L"True") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierEnd.CommandLine", L"cmd.exe /c appverif -disable exceptions handles heaps leak locks memory srwlock threadpool tls -for te.processhost.exe te.exe audiodg.exe") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierEnd.Order", L"1") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierEnd.ExpectedExitCode", L"0") \

#define START_OSUPGRADE \
    TEST_CLASS_PROPERTY(L"Kits.TestTask", L"OSUpgrade") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.Stage", L"Setup") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.RunElevated", L"True") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.CommandLine", L"cmd.exe /c c:\\HLK\\ISO\\setup.exe /auto:upgrade /compat IgnoreWarning /noreboot") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.Order", L"1") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.ExpectedExitCode", L"0") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.OSUpgrade.TimeoutInMinutes", L"240") \

#define START_APPVERIFIFER_UPGRADE \
    TEST_CLASS_PROPERTY(L"Kits.TestTask", L"AppVerifierStart") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.Stage", L"Setup") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.RunElevated", L"True") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.CommandLine", L"cmd.exe /c appverif -enable exceptions handles heaps leak locks memory srwlock threadpool tls -for te.processhost.exe te.exe audiodg.exe") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.Order", L"2") \
    TEST_CLASS_PROPERTY(L"Kits.TestTask.AppVerifierStart.ExpectedExitCode", L"0") \

// End Metadata defines


<|start_filename|>audio/sysvad/EndpointsCommon/usbhsminipairs.h<|end_filename|>
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    usbhsminipairs.h

Abstract:

    USB Sideband Audio endpoint filter definitions. 

--*/

#ifndef _SYSVAD_USBHSMINIPAIRS_H_
#define _SYSVAD_USBHSMINIPAIRS_H_

#include "usbhsspeakertopo.h"
#include "usbhsspeakertoptable.h"
#include "usbhsspeakerwavtable.h"

#include "usbhsmictopo.h"
#include "usbhsmictoptable.h"
#include "usbhsmicwavtable.h"

NTSTATUS
CreateMiniportWaveRTSYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

NTSTATUS
CreateMiniportTopologySYSVAD
( 
    _Out_       PUNKNOWN *,
    _In_        REFCLSID,
    _In_opt_    PUNKNOWN,
    _In_        POOL_FLAGS,
    _In_        PUNKNOWN,
    _In_opt_    PVOID,
    _In_        PENDPOINT_MINIPAIR
);

/*
* USB Headset speaker miniports.
*
**********************************************************************************************
* Topology/Wave bridge connection for USB Headset Speaker                                    *
*                                                                                            *
*              +---------------------------------+                +-------+                  *
*              | Wave                            |                | Topo  |                  *
*              |                                 |                |       |                  *
*              |      +---------------------+    |                |       |                  *
*              |      |                     |    |                |       |                  *
* System   --->|0-----|1  HW Audio Engine  3|---2|---> Loopback   |       |                  *
*              |      |        Node         |    |                |       |                  *
* Offload  --->|1-----|2                   0|---3|===============>|0---->1|---> Line Out     *
*              |      +---------------------+    |                |       |                  *
*              |                                 |                |       |                  *
*              +---------------------------------+                +-------+                  *
*                                                                                            *
**********************************************************************************************/

static
PHYSICALCONNECTIONTABLE UsbHsSpeakerTopologyPhysicalConnections[] =
{
    {
        KSPIN_TOPO_WAVEOUT_SOURCE,  // TopologyIn
        KSPIN_WAVE_RENDER_SOURCE,   // WaveOut
        CONNECTIONTYPE_WAVE_OUTPUT
    }
};

// A subdevice is created for each HFP device using the reference string in the form of:
// <Base_Reference_String>-<Hashcode>
// where Base_Reference_String is one of the following template names
#define USBHS_SPEAKER_TOPO_NAME    L"TopologyUsbHsSpeaker"
#define USBHS_SPEAKER_WAVE_NAME    L"WaveUsbHsSpeaker"
#define USBHS_MIC_TOPO_NAME        L"TopologyUsbHsMic"
#define USBHS_MIC_WAVE_NAME        L"WaveUsbHsMic"
// And Hashcode is 8-character long hexadecimal value

// A reference string max length is being set to a value to accommodate them both
#define USBHS_INTERFACE_REFSTRING_MAX_LENGTH 100

static
ENDPOINT_MINIPAIR UsbHsSpeakerMiniports =
{
    eUsbHsSpeakerDevice,
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_TOPO_NAME and made unique for each side band interface
    USBHS_SPEAKER_TOPO_NAME,            // template name matches with KSNAME_TopologyBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &UsbHsSpeakerTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    USBHS_SPEAKER_WAVE_NAME,            // template name matches with KSNAME_WaveBthHfpSpeaker in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &UsbHsSpeakerWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    UsbHsSpeakerPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(UsbHsSpeakerPinDeviceFormatsAndModes),
    UsbHsSpeakerTopologyPhysicalConnections,
    SIZEOF_ARRAY(UsbHsSpeakerTopologyPhysicalConnections),
    ENDPOINT_OFFLOAD_SUPPORTED
};


/*
* Headset Microphone miniports.
*
*
************************************************************************************************************************************************
* Topology/Wave bridge connection for USB Headset Microphone                                                                                   *
*              +---------------------------------+                +---------------------------------------------------------+                  *
*              |         +-------------+         |                |      +-------------+     +-------+     +--------+       |                  *
* Capture  <---|1<-------|0    ADC    1|<-------0|<===============|1<----| peak meter  |<----|  mute |<----| volume |<---- 0|<---- Mic in      *
*              |         +-------------+         |                |      +-------------+     +-------+     +--------+       |                  *
*              +---------------------------------+                +---------------------------------------------------------+                  *
*                                                                                                                                              *
************************************************************************************************************************************************/
static
PHYSICALCONNECTIONTABLE UsbHsMicTopologyPhysicalConnections[] =
{
    {
        KSPIN_TOPO_BRIDGE,          // TopologyOut
        KSPIN_WAVE_BRIDGE,          // WaveIn
        CONNECTIONTYPE_TOPOLOGY_OUTPUT
    }
};

static
ENDPOINT_MINIPAIR UsbHsMicMiniports =
{
    eUsbHsMicDevice,
    NULL,                               // Will be generated based on BTHHFP_MIC_TOPO_NAME and made unique for each side band interface
    USBHS_MIC_TOPO_NAME,                // template name matches with KSNAME_TopologyBthHfpMic in the inf's [Strings] section
    CreateMiniportTopologySYSVAD,
    &UsbHsMicTopoMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    NULL,                               // Will be generated based on BTHHFP_SPEAKER_WAVE_NAME and made unique for each side band interface
    USBHS_MIC_WAVE_NAME,                // template name matches with KSNAME_WaveBthHfpMic in the inf's [Strings] section
    CreateMiniportWaveRTSYSVAD,
    &UsbHsMicWaveMiniportFilterDescriptor,
    0, NULL,                            // Interface properties
    1,
    UsbHsMicPinDeviceFormatsAndModes,
    SIZEOF_ARRAY(UsbHsMicPinDeviceFormatsAndModes),
    UsbHsMicTopologyPhysicalConnections,
    SIZEOF_ARRAY(UsbHsMicTopologyPhysicalConnections),
    ENDPOINT_NO_FLAGS
};

//=============================================================================
//
// Render miniport pairs.
//
static
PENDPOINT_MINIPAIR  g_UsbHsRenderEndpoints[] = 
{
    &UsbHsSpeakerMiniports
};

#define g_cUsbHsRenderEndpoints  (SIZEOF_ARRAY(g_UsbHsRenderEndpoints))

// If the count of Render endpoints changes, update formula below
C_ASSERT(g_cUsbHsRenderEndpoints == 1);

//=============================================================================
//
// Capture miniport pairs.
//
static
PENDPOINT_MINIPAIR  g_UsbHsCaptureEndpoints[] = 
{
    &UsbHsMicMiniports
};

#define g_cUsbHsCaptureEndpoints (SIZEOF_ARRAY(g_UsbHsCaptureEndpoints))

// If the count of Capture endpoints changes, update formula below
C_ASSERT(g_cUsbHsCaptureEndpoints == 1);

//=============================================================================
//
// Total miniports = # endpoints * 2 (topology + wave)
//
// 
// Allow 5 USB Headset Endpoints.
//
#define MAX_USBHS_DEVICES       (5)
#define g_MaxUsbHsMiniports     ((g_cUsbHsRenderEndpoints + g_cUsbHsCaptureEndpoints) * 2 * MAX_USBHS_DEVICES)

// g_MaxUsbHsMiniports is used when calculating the MaxObjects inside AddDevice.
C_ASSERT(g_MaxUsbHsMiniports == 20);

#endif // _SYSVAD_USBHSMINIPAIRS_H_



<|start_filename|>nfc/NfcCxSample/windows-drivertemplate-nfc/Device.cpp<|end_filename|>
#include <new>

#include "Device.h"

// [Required]
// Called when the device is provisioned during system boot or when a new device is plugged-in while the
// system is running.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfdriver/nc-wdfdriver-evt_wdf_driver_device_add
NTSTATUS DeviceContext::AddDevice(
    _In_ WDFDRIVER Driver,
    _Inout_ PWDFDEVICE_INIT DeviceInit)
{
    (void)Driver;

    NTSTATUS status;

    // Configure the NFC Class Extension.
    // Note: The NFC_CX_TRANSPORT_TYPE is only used for telemetry purposes. It does not affect the behavior of
    // the driver.
    NFC_CX_CLIENT_CONFIG nfcCxConfig;
    NFC_CX_CLIENT_CONFIG_INIT(&nfcCxConfig, NFC_CX_TRANSPORT_CUSTOM);

#ifdef ENABLE_IF_IMPLEMENTING_CUSTOM_IOCTLS
    nfcCxConfig.EvtNfcCxDeviceIoControl = IoControl;
#endif

    // Provide the NCI Write callback, which is called by the NFC CX when it has an NCI packet that needs to
    // be sent to the NFC Controller.
    nfcCxConfig.EvtNfcCxWriteNciPacket = WriteNciPacket;

    // Instruct the NFC CX to read the existing NCI configuration values and only update them if necessary.
    nfcCxConfig.DriverFlags = NFC_CX_DRIVER_ENABLE_EEPROM_WRITE_PROTECTION;

    status = NfcCxDeviceInitConfig(DeviceInit, &nfcCxConfig);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    // Create the PnP power callbacks configuration.
    WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
    pnpCallbacks.EvtDevicePrepareHardware = PrepareHardware;
    pnpCallbacks.EvtDeviceReleaseHardware = ReleaseHardware;
    pnpCallbacks.EvtDeviceD0Entry = D0Entry;
    pnpCallbacks.EvtDeviceD0Exit = D0Exit;

    // Set the PnP power callbacks.
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks);

    // Create WDF object attributes for the device.
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DeviceContext);
    deviceAttributes.EvtDestroyCallback = Destroy;

    // Create the device.
    WDFDEVICE device;
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    // Get a pointer to the raw memory that WDF allocated for the Device class.
    DeviceContext* context = DeviceGetContext(device);

    // Initialize the raw memory using C++'s placement new operator.
    // https://en.cppreference.com/w/cpp/language/new
    new (context) DeviceContext(device);

    // Let the NFC Class Extension initialize the device.
    status = NfcCxDeviceInitialize(device);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    // Create the RF config. (Enable everything.)
    NFC_CX_RF_DISCOVERY_CONFIG discoveryConfig;
    NFC_CX_RF_DISCOVERY_CONFIG_INIT(&discoveryConfig);
    discoveryConfig.PollConfig = NFC_CX_POLL_NFC_A | NFC_CX_POLL_NFC_B | NFC_CX_POLL_NFC_F_212 | NFC_CX_POLL_NFC_F_424 | NFC_CX_POLL_NFC_15693 | NFC_CX_POLL_NFC_ACTIVE | NFC_CX_POLL_NFC_A_KOVIO;
    discoveryConfig.NfcIPMode = NFC_CX_NFCIP_NFC_A | NFC_CX_NFCIP_NFC_F_212 | NFC_CX_NFCIP_NFC_F_424 | NFC_CX_NFCIP_NFC_ACTIVE | NFC_CX_NFCIP_NFC_ACTIVE_A | NFC_CX_NFCIP_NFC_ACTIVE_F_212 | NFC_CX_NFCIP_NFC_ACTIVE_F_424;
    discoveryConfig.NfcIPTgtMode = NFC_CX_NFCIP_TGT_NFC_A | NFC_CX_NFCIP_TGT_NFC_F | NFC_CX_NFCIP_TGT_NFC_ACTIVE_A | NFC_CX_NFCIP_TGT_NFC_ACTIVE_F;
    discoveryConfig.NfcCEMode = NFC_CX_CE_NFC_A | NFC_CX_CE_NFC_B | NFC_CX_CE_NFC_F;

    // Set the RF config.
    status = NfcCxSetRfDiscoveryConfig(device, &discoveryConfig);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

#ifdef ENABLE_IF_USING_SEQUENCE_HANDLER_CALLBACKS
    // Set the NFC Class Extension sequence handlers.
    for (int sequenceType = 0; sequenceType != SequenceMaximum; ++sequenceType)
    {
        status = NfcCxRegisterSequenceHandler(device, NFC_CX_SEQUENCE(sequenceType), SequenceHandler);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    }
#endif

    return STATUS_SUCCESS;
}

DeviceContext::DeviceContext(_In_ WDFDEVICE Device) :
    _Device(Device)
{
}

// [Required]
// Called when the memory for the class is about to be freed by WDF.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfobject/nc-wdfobject-evt_wdf_object_context_destroy
void DeviceContext::Destroy(
    _In_ WDFOBJECT Object)
{
    // Manually call the destructor for the class.
    // This mirrors the use of the placement new operator in `DeviceContext::AddDevice`.
    DeviceContext* context = DeviceGetContext(Object);
    context->~DeviceContext();
}

// [Likely required]
// Called when the device's hardware resources are ready to be initialized.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfdevice/nc-wdfdevice-evt_wdf_device_prepare_hardware
NTSTATUS DeviceContext::PrepareHardware(
    _In_ WDFDEVICE Device,
    _In_ WDFCMRESLIST ResourcesRaw,
    _In_ WDFCMRESLIST ResourcesTranslated)
{
    (void)Device;
    (void)ResourcesRaw;
    (void)ResourcesTranslated;

    // FIX ME:
    // Initialize the hardware so that it is ready to accept NCI packets.

    return STATUS_SUCCESS;
}

// [Likely required]
// Called when the device's hardware resources are no longer accessible.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfdevice/nc-wdfdevice-evt_wdf_device_release_hardware
NTSTATUS DeviceContext::ReleaseHardware(
    _In_ WDFDEVICE Device,
    _In_ WDFCMRESLIST ResourcesTranslated)
{
    (void)Device;
    (void)ResourcesTranslated;

    // FIX ME:
    // If neccessary, free any resources created by PrepareHardware.

    return STATUS_SUCCESS;
}

// [Likely required]
// Called when the NFC Controller is entering the fully powered-on state.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfdevice/nc-wdfdevice-evt_wdf_device_d0_entry
NTSTATUS DeviceContext::D0Entry(
    _In_ WDFDEVICE Device,
    _In_ WDF_POWER_DEVICE_STATE PreviousState)
{
    (void)PreviousState;

    NTSTATUS status;

    // Invoke the HostActionStart event, so that the NFC Class Extension initializes the NFC Controller (by
    // sending the relevant NCI packets).
    NFC_CX_HARDWARE_EVENT eventArgs = {};
    eventArgs.HostAction = HostActionStart;

    status = NfcCxHardwareEvent(Device, &eventArgs);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    return STATUS_SUCCESS;
}

// [Likely required]
// Called when the NFC Controller is entering a low power state.
//
// By default, the NFC Class Extension will prevent this callback from being invoked if there is an client
// process that may be using the NFC Controller. So it is okay for the NFC Controller to be fully
// uninitialized here.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/wdfdevice/nc-wdfdevice-evt_wdf_device_d0_exit
NTSTATUS DeviceContext::D0Exit(
    _In_ WDFDEVICE Device,
    _In_ WDF_POWER_DEVICE_STATE TargetState)
{
    (void)TargetState;

    NTSTATUS status;

    // Invoke the HostActionStop event, so that the NFC Class Extension uninitializes the NFC Controller (by sending
    // the relevant NCI packets).
    NFC_CX_HARDWARE_EVENT eventArgs = {};
    eventArgs.HostAction = HostActionStop;

    status = NfcCxHardwareEvent(Device, &eventArgs);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    return STATUS_SUCCESS;
}

// [Optional]
// Called when certain state transitions occur within the NFC Class Extension state machine. This can be used to send
// custom commands to the NFC Controller if neccessary.
//
// https://docs.microsoft.com/windows-hardware/drivers/nfc/sequence-handling
void DeviceContext::SequenceHandler(
    _In_ WDFDEVICE Device,
    _In_ NFC_CX_SEQUENCE Sequence,
    _In_ PFN_NFC_CX_SEQUENCE_COMPLETION_ROUTINE CompletionRoutine,
    _In_opt_ WDFCONTEXT CompletionContext)
{
    (void)Sequence;

    // Nothing to do. So complete the sequence handler immediately.
    // Note: CompletionRoutine may be called asynchronously.
    CompletionRoutine(Device, STATUS_SUCCESS, 0, CompletionContext);
}

// [Optional]
// Can be used to implement custom IOCTLs.
//
// The NFC Class Extension registers for the default I/O queue and so it gets the first crack at handling all I/O requests.
// If this callback is enabled, any IOCTL the NFC Class Extension doesn't recognize will be forwarded here.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/nfccx/nc-nfccx-evt_nfc_cx_device_io_control
void DeviceContext::IoControl(
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG  IoControlCode)
{
    (void)Device;
    (void)OutputBufferLength;
    (void)InputBufferLength;
    (void)IoControlCode;

    // No custom IOCTLs are currently supported. So complete all I/O requests with a standard error.
    WdfRequestComplete(Request, STATUS_INVALID_DEVICE_STATE);
}

// [Required]
// Called by the NFC Class Extension when an NCI packet must be sent to the NFC Controller.
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/nfccx/nc-nfccx-evt_nfc_cx_write_nci_packet
void DeviceContext::WriteNciPacket(
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request)
{
    (void)Device;

    NTSTATUS status;

    // Get the NCI packet.
    void* nciPacket;
    size_t nciPacketLength;
    status = WdfRequestRetrieveInputBuffer(Request, 0, &nciPacket, &nciPacketLength);
    if (!NT_SUCCESS(status))
    {
        WdfRequestComplete(Request, status);
        return;
    }

    // FIX ME:
    // Send NCI packet to NFC Controller hardware using the relevant bus API.
    // For example,
    //   - I2C or SPI: https://docs.microsoft.com/windows-hardware/drivers/spb/spb-peripheral-device-drivers
    //   - USB: https://docs.microsoft.com/windows-hardware/drivers/usbcon/usb-driver-development-guide

    // FIX ME:
    // Complete I/O request with STATUS_SUCCESS if NCI packet is succesfully sent to the NFC Controller.
    WdfRequestComplete(Request, STATUS_NOT_IMPLEMENTED);
}

// FIX ME:
// Ensure NfcCxNciReadNotification is called when the NFC Controller needs to send an NCI packet to the driver.
// This is usually done in response to a hardware notification. For example,
//   - GPIO interrupt (I2C or SPI): https://docs.microsoft.com/windows-hardware/drivers/gpio/gpio-interrupts
//   - USB continuous reader: https://docs.microsoft.com/windows-hardware/drivers/usbcon/how-to-use-the-continous-reader-for-getting-data-from-a-usb-endpoint--umdf-/
//
// https://docs.microsoft.com/windows-hardware/drivers/ddi/content/nfccx/nf-nfccx-nfccxncireadnotification

// A helper function that forwards NCI packets from the NFC Controller to the NFC Class Extension driver.
//
// NOTE: If the NCI packet is already packaged within an existing WDFMEMORY, then the NfcCxNciReadNotification
// function can be called directly.
NTSTATUS DeviceContext::ReadNciPacket(
    _In_reads_bytes_(nciPacketLength) void* nciPacket,
    _In_ size_t nciPacketLength)
{
    NTSTATUS status;

    if (!_NciReadMemory)
    {
        // Create the WDFMEMORY object that will be used for all NCI reads.
        WDF_OBJECT_ATTRIBUTES memoryAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes);
        memoryAttributes.ParentObject = _Device;

        status = WdfMemoryCreatePreallocated(&memoryAttributes, nciPacket, nciPacketLength, &_NciReadMemory);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    }
    else
    {
        // Re-assign the WDFMEMORY to point to the new NCI packet.
        status = WdfMemoryAssignBuffer(_NciReadMemory, nciPacket, nciPacketLength);
        if (!NT_SUCCESS(status))
        {
            return status;
        }
    }

    // Note: NfcCxNciReadNotification does not store a reference to the WDFMEMORY object passed to it. So it
    // is safe to free the NCI packet's memory after the call has completed.
    status = NfcCxNciReadNotification(_Device, _NciReadMemory);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    return STATUS_SUCCESS;
}


<|start_filename|>general/registry/regfltr/sys/txr.c<|end_filename|>
/*++
Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    TxR.c

Abstract: 

    Samples that show how to deal with transactional registry operations.

Environment:

    Kernel mode only

--*/

#include "regfltr.h"


BOOLEAN
TransactionEnlistSample(
    )
/*++

Routine Description:

    This sample shows how to enlist to a transaction that a registry operation
    is part of inorder to get notifications when it commits or aborts. 

Return Value:

    TRUE if the sample completed successfully.

--*/

{
    PCALLBACK_CONTEXT CallbackCtx = NULL;
    PRMCALLBACK_CONTEXT RMCallbackCtx = NULL;
    NTSTATUS Status;
    UNICODE_STRING Name;
    OBJECT_ATTRIBUTES KeyAttributes;
    OBJECT_ATTRIBUTES TxAttributes;
    HANDLE Key = NULL;
    HANDLE Transaction = NULL;
    BOOLEAN Success = FALSE;
    
    InfoPrint("");
    InfoPrint("=== Transaction Enlist Sample ====");

    if (!g_RMCreated) {
        ErrorPrint("Sample can't run because KTM data structures were not successfully created.");
        goto Exit;
    }

    //
    // Create the registry callback context and the transaction callback context.
    //

    CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_ENLIST,
                                         CALLBACK_ALTITUDE);
    if (CallbackCtx == NULL) {
        goto Exit;
    }

    RMCallbackCtx = (PRMCALLBACK_CONTEXT) ExAllocatePoolZero (
                        PagedPool,
                        sizeof(RMCALLBACK_CONTEXT),
                        REGFLTR_CONTEXT_POOL_TAG);
    if (RMCallbackCtx == NULL) {
        goto Exit;
    }
    CallbackCtx->RMCallbackCtx = RMCallbackCtx;

    //
    // Create a transaction
    //

    InitializeObjectAttributes(&TxAttributes,
                               NULL,
                               OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);
    
    Status = ZwCreateTransaction(&Transaction,
                                 TRANSACTION_ALL_ACCESS,
                                 &TxAttributes,
                                 NULL,
                                 NULL,
                                 0,
                                 0,
                                 0,
                                 NULL,
                                 NULL);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CreateTransaction failed. Status 0x%x", Status);
        goto Exit;
    }

    //
    // Register the callback
    //

    Status = CmRegisterCallbackEx(Callback,
                                  &CallbackCtx->Altitude,
                                  g_DeviceObj->DriverObject,
                                  (PVOID) CallbackCtx,
                                  &CallbackCtx->Cookie, 
                                  NULL);
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status);
        Success = FALSE;
    }

    Success = TRUE;

    //
    // Create a key
    //

    RtlInitUnicodeString(&Name, KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               g_RootKey,
                               NULL);

    Status = ZwCreateKeyTransacted(&Key,
                                   KEY_ALL_ACCESS,
                                   &KeyAttributes,
                                   0,
                                   NULL,
                                   0,
                                   Transaction,
                                   NULL);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x", Status);
        Success = FALSE;
    }

    //
    // Commit the transaction
    //

    Status = ZwCommitTransaction(Transaction, TRUE);
    
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwCommitTransaction failed. Status 0x%x", Status);
        Success = FALSE;
    }
    
    //
    // Unregister the callback
    //

    Status = CmUnRegisterCallback(CallbackCtx->Cookie);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status);
        Success = FALSE;
    }

    //
    // Check that the transaction callback context records a commit notification
    //

    if (RMCallbackCtx->Notification != TRANSACTION_NOTIFY_COMMIT) {
        ErrorPrint("RMContext notification mask is 0x%x instead of 0x%x.",
                   RMCallbackCtx->Notification,
                   TRANSACTION_NOTIFY_COMMIT);
        Success = FALSE;
    }

  Exit:
    
    //
    // Clean up
    //

    if (Transaction != NULL) {
        ZwClose(Transaction);
    }

    //
    // Need to reopen the key to delete it because the previous 
    // handle was part of a transaction that is now gone.
    //

    if (Key != NULL) {
        ZwClose(Key);
        Key = NULL;
    }

    RtlInitUnicodeString(&Name, KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               g_RootKey,
                               NULL);
    
    ZwOpenKey(&Key, KEY_ALL_ACCESS, &KeyAttributes); 
    if (Key != NULL) {
        ZwDeleteKey(Key);
        ZwClose(Key);
    }

    if (CallbackCtx != NULL) {
        ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG);
    }

    if (RMCallbackCtx != NULL) {
        ExFreePoolWithTag(RMCallbackCtx, REGFLTR_CONTEXT_POOL_TAG);
    }

    if (Success) {
        InfoPrint("Transaction Enlist Demo succeeded.");
    } else {
        ErrorPrint("Transaction Enlist Demo FAILED.");
    }

    return Success;
}


NTSTATUS 
CallbackTransactionEnlist(
    _In_ PCALLBACK_CONTEXT CallbackCtx,
    _In_ REG_NOTIFY_CLASS NotifyClass,
    _Inout_ PVOID Argument2
)
/*++

Routine Description:

    This helper callback routine shows hot to enlist on a transaction.

Arguments:

    CallbackContext - The value that the driver passed to the Context parameter
        of CmRegisterCallbackEx when it registers this callback routine.

    NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of 
        registry operation that is being performed and whether the callback
        is being called in the pre or post phase of processing.

    Argument2 - A pointer to a structure that contains information specific
        to the type of the registry operation. The structure type depends
        on the REG_NOTIFY_CLASS value of Argument1. 

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PREG_CREATE_KEY_INFORMATION PreCreateInfo;
    PVOID Transaction = NULL;
    
    switch(NotifyClass) {

        case RegNtPreCreateKeyEx:

            PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2;

            //
            // Get the transaction object
            //
            
            Transaction = PreCreateInfo->Transaction;
            if (Transaction == NULL) {

                //
                // Even if the transaction is not provided in the 
                // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction 
                // on the RootObject to check that it isn't associated with
                // a transaction.
                //

                Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie,
                                                    PreCreateInfo->RootObject);

                if (Transaction == NULL) {
                    ErrorPrint("CreateKey is unexpectedly not transacted.");
                    break;
                }
            }

            //
            // Use the volatile RM created in CreateKTMResourceManager() 
            // to enlist in the transaction. We want notifications for
            // when the transaction commits or rolls back. 
            // 
            // Note: Make sure the callback routine handles all the 
            // notifications requested here. Look at RMCallback() to see
            // how to handle notifications.
            //

            Status = EnlistInTransaction(&CallbackCtx->RMCallbackCtx->Enlistment,
                                        ENLISTMENT_SUBORDINATE_RIGHTS,
                                        Transaction,
                                        TRANSACTION_NOTIFY_COMMIT |
                                        TRANSACTION_NOTIFY_ROLLBACK,
                                        CallbackCtx->RMCallbackCtx);
            
            if (!NT_SUCCESS(Status)) {
                ErrorPrint("EnlistInTransaction failed. Status 0x%x.", Status);
            }

            break;

        default:
            //
            // Do nothing for other notifications
            //
            break;
    }

    return Status;
}



BOOLEAN 
TransactionReplaySample(
    )
/*++

Routine Description:

    This sample shows how to copy a transactional create key operation.

Return Value:

    TRUE if the sample completed successfully.

--*/

{
    PCALLBACK_CONTEXT CallbackCtx = NULL;
    NTSTATUS Status;
    UNICODE_STRING Name;
    OBJECT_ATTRIBUTES KeyAttributes;
    OBJECT_ATTRIBUTES TxAttributes;
    HANDLE Key = NULL;
    HANDLE Transaction = NULL;
    HANDLE TransactedRoot = NULL;
    BOOLEAN bSuccess = FALSE;
    
    InfoPrint("");
    InfoPrint("=== Transaction Replay Sample ====");

    if (!g_RMCreated) {
        ErrorPrint("Sample can't run because KTM data structures were not successfully created.");
        goto Exit;
    }

    //
    // Create the callback context
    //

    CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_REPLAY,
                                         CALLBACK_ALTITUDE);
    if (CallbackCtx == NULL) {
        goto Exit;
    }
 
    //
    // Create a transaction
    //

    InitializeObjectAttributes(&TxAttributes,
                               NULL,
                               OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    Status = ZwCreateTransaction(&Transaction,
                                 TRANSACTION_ALL_ACCESS,
                                 &TxAttributes,
                                 NULL,
                                 NULL,
                                 0,
                                 0,
                                 0,
                                 NULL,
                                 NULL);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CreateTransaction failed. Status 0x%x", Status);
        goto Exit;
    }

    //
    // Open a transacted handle to the root key
    //
    
    RtlInitUnicodeString(&Name, ROOT_KEY_ABS_PATH);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);
    
    Status = ZwOpenKeyTransacted(&TransactedRoot,
                                 KEY_ALL_ACCESS,
                                 &KeyAttributes,
                                 Transaction);
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwOpenKeyTransacted failed. Status 0x%x",Status);
        goto Exit;
    }
  
    //
    // Register callback
    //

    Status = CmRegisterCallbackEx(Callback,
                                  &CallbackCtx->Altitude,
                                  g_DeviceObj->DriverObject,
                                  (PVOID) CallbackCtx,
                                  &CallbackCtx->Cookie, 
                                  NULL);
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status);
        goto Exit;
    }

    bSuccess = TRUE;
    
    //
    // Create a key using the transacted root key handle.
    //

    RtlInitUnicodeString(&Name, KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               TransactedRoot,
                               NULL);
    Status = ZwCreateKey(&Key,
                         KEY_ALL_ACCESS,
                         &KeyAttributes,
                         0,
                         NULL,
                         0,
                         NULL);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwCreateKey failed. Status 0x%x", Status);
        bSuccess = FALSE;
    } 

    //
    // Unregister the callback
    //

    Status = CmUnRegisterCallback(CallbackCtx->Cookie);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status);
        bSuccess = FALSE;
    }


    //
    // Verify that the key created exists and that a key with the
    // "modified" name is also exists.
    //
    
    if (Key != NULL) {
        ZwClose(Key);
        Key = NULL;
    }

    Status = ZwOpenKey(&Key,
                       KEY_ALL_ACCESS,
                       &KeyAttributes);
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwCreateKey failed. Status 0x%x", Status);
        bSuccess = FALSE;
    } else {
        ZwClose(Key);
        Key = NULL;
    }
    
    RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               TransactedRoot,
                               NULL);

    Status = ZwOpenKey(&Key,
                       KEY_ALL_ACCESS,
                       &KeyAttributes);

    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwCreateKey failed. Status 0x%x", Status);
        bSuccess = FALSE;
    } else {
        ZwClose(Key);
        Key = NULL;
    }

    //
    // Roll back transaction
    //

    Status = ZwRollbackTransaction(Transaction, TRUE);
    
    if (!NT_SUCCESS(Status)) {
        ErrorPrint("ZwRollbackTransaction failed. Status 0x%x", Status);
        bSuccess = FALSE;
        goto Exit;
    }
    
    //
    // Check that both keys no longer exist. 
    //

    RtlInitUnicodeString(&Name, KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               g_RootKey,
                               NULL);
    Status = ZwOpenKey(&Key,
                       KEY_ALL_ACCESS,
                       &KeyAttributes);
    if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
        ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", 
                   Status,
                   STATUS_OBJECT_NAME_NOT_FOUND);
        bSuccess = FALSE;
    }

    if (Key != NULL) {
        ZwDeleteKey(Key);
        ZwClose(Key);
        Key = NULL;
    }

    RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME);
    InitializeObjectAttributes(&KeyAttributes,
                               &Name,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               g_RootKey,
                               NULL);
    Status = ZwOpenKey(&Key,
                       KEY_ALL_ACCESS,
                       &KeyAttributes);
    if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
        ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", 
                   Status,
                   STATUS_OBJECT_NAME_NOT_FOUND);
        bSuccess = FALSE;
    }

  Exit:
    
    //
    // Clean up 
    //

    if (Key != NULL) {
        ZwDeleteKey(Key);
        ZwClose(Key);
    }

    if (TransactedRoot!= NULL) {
        ZwDeleteKey(TransactedRoot);
        ZwClose(TransactedRoot);
    }

    if (Transaction != NULL) {
        ZwClose(Transaction);
    }

    if (CallbackCtx != NULL) {
        ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG);
    }

    if (bSuccess) {
        InfoPrint("Transaction Replay Sample succeeded.");
    } else {
        ErrorPrint("Transaction Replay Sample FAILED.");
    }

    return bSuccess;
    
}



NTSTATUS 
CallbackTransactionReplay(
    _In_ PCALLBACK_CONTEXT CallbackCtx,
    _In_ REG_NOTIFY_CLASS NotifyClass,
    _Inout_ PVOID Argument2
    )
/*++

Routine Description:

    This helper callback routine shows how to get the transaction associated
    with a registry operation and shows how to do another operation in the 
    same transaction.
    
Arguments:

    CallbackContext - The value that the driver passed to the Context parameter
        of CmRegisterCallbackEx when it registers this callback routine.

    NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of 
        registry operation that is being performed and whether the callback
        is being called in the pre or post phase of processing.

    Argument2 - A pointer to a structure that contains information specific
        to the type of the registry operation. The structure type depends
        on the REG_NOTIFY_CLASS value of Argument1. 

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PREG_CREATE_KEY_INFORMATION PreCreateInfo;
    HANDLE TransactionHandle = NULL;
    PVOID Transaction = NULL;
    OBJECT_ATTRIBUTES KeyAttributes;
    UNICODE_STRING Name;
    UNICODE_STRING LocalClass = {0};
    PUNICODE_STRING Class = NULL;
    HANDLE Key = NULL;
    HANDLE RootKey = NULL;
    KPROCESSOR_MODE Mode = KernelMode;

    switch(NotifyClass) {

        case RegNtPreCreateKeyEx:

            PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2;

            //
            // Get the transaction object
            //

            Transaction = PreCreateInfo->Transaction;
            if (Transaction == NULL) {

                //
                // Even if the transaction is not provided in the 
                // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction 
                // on the RootObject to check that it isn't associated with
                // a transaction.
                //

                Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie,
                                                    PreCreateInfo->RootObject);
                if (Transaction == NULL) {
                    ErrorPrint("CreateKey is unexpectedly not transacted.");
                    break;
                }
            }

            //
            // Get a handle to the transaction object
            //

            Status = ObOpenObjectByPointer(Transaction,
                                           OBJ_KERNEL_HANDLE,
                                           NULL,
                                           TRANSACTION_ALL_ACCESS,
                                           *TmTransactionObjectType,
                                           KernelMode,
                                           &TransactionHandle);

            if (!NT_SUCCESS (Status)) {
                ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status);
                break;
            }

            // 
            // Next replay the create key using the transacted version of the
            // API and the transaction handle.
            //

            Status = ObOpenObjectByPointer(PreCreateInfo->RootObject,
                                           OBJ_KERNEL_HANDLE,
                                           NULL,
                                           KEY_ALL_ACCESS,  // Getting handle with all access
                                           PreCreateInfo->ObjectType,
                                           KernelMode,
                                           &RootKey);
            if (!NT_SUCCESS (Status)) {
                ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status);
                break;
            }

    
            //
            // REG_CREATE_KEY_INFORMATION is a partially structure. The class
            // field's buffer is not captured. Since it is passed to 
            // ZwCreateKeyTransacted, it needs to be captured.
            //
            // *Note: in Windows 8 all fields are captured. See capture.c
            // for more details.
            //

            Mode = ExGetPreviousMode();

            if (!g_IsWin8OrGreater && Mode == UserMode) {
                Status = CaptureUnicodeString(&LocalClass, 
                                              PreCreateInfo->Class, 
                                              REGFLTR_CAPTURE_POOL_TAG);
                if (!NT_SUCCESS(Status)) {
                    break;
                }
                Class = &LocalClass;
                
            } else {
                Class = PreCreateInfo->Class;
            }

            
            RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME);
            InitializeObjectAttributes(&KeyAttributes,
                                       &Name,
                                       OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                       RootKey,
                                       PreCreateInfo->SecurityDescriptor);

            Status = ZwCreateKeyTransacted(&Key,
                                           KEY_ALL_ACCESS,
                                           &KeyAttributes,
                                           0,
                                           Class,
                                           PreCreateInfo->CreateOptions,
                                           TransactionHandle,
                                           PreCreateInfo->Disposition);

            ZwClose(RootKey);
            ZwClose(TransactionHandle);

            if (!NT_SUCCESS(Status)) {
                ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x.", Status);
                break;
            }

            ZwClose(Key);
            InfoPrint("\tCallback: Create key %wZ replayed in same transaction context.",
                      PreCreateInfo->CompleteName);
            Status = STATUS_SUCCESS;
            break;
            
        default:
            //
            // Do nothing for other notifications
            //
            break;
    }

    //
    // Free buffers used for capturing user mode values.
    //

    if (LocalClass.Buffer != NULL) {
        FreeCapturedUnicodeString(&LocalClass, REGFLTR_CAPTURE_POOL_TAG);
    }

    return Status;   
}



<|start_filename|>wmi/wmisamp/wmisamp.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
    EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

Module Name:

    WmiSamp.c

Abstract:

    --

Environment:

    Kernel mode

--*/


#include "WmiSamp.h"


#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, WmiSampEvtDeviceAdd)
#endif


//
// Define a tag for use by the memory allocation routines.
//
#define WMI_SAMPLE_TAG (ULONG)'SimW'


//
// Private methods.
//

NTSTATUS
PriStartNewPeriodicTimer(
    _In_ WDFDEVICE DeviceObject,
    _In_ ULONG TimeInMilliSeconds,
    _In_ PFN_WDF_TIMER CallbackFunction
    );


EVT_WDF_TIMER PriTimerCallback;


NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    WDF_DRIVER_CONFIG config;
 
    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Initialize the Driver Config structure.
    //
    WDF_DRIVER_CONFIG_INIT(&config,
                           WmiSampEvtDeviceAdd);

    //
    // Create the Framework Driver object.
    //
    status = WdfDriverCreate(DriverObject,
                             RegistryPath,
                             WDF_NO_OBJECT_ATTRIBUTES,
                             &config,
                             WDF_NO_HANDLE);
    return status;
}


NTSTATUS
WmiSampEvtDeviceAdd(
    WDFDRIVER DriverObject,
    PWDFDEVICE_INIT DeviceInit
    )
{
    NTSTATUS status;
    WDFDEVICE deviceObject;
    WDF_OBJECT_ATTRIBUTES wdfObjAttributes;
    PWMI_SAMPLE_DEVICE_DATA wmiDeviceData;

    ULONG i;
    ULONG oneMinute = 60*1000;
    EC1 Ec1 = {0};
    EC2 Ec2 = {0};

    UNREFERENCED_PARAMETER(DriverObject);

    PAGED_CODE();

    //
    // Initialize all the properties specific to the device. Default values are
    // set for the ones that are not set explicitly here.
    //
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_UNKNOWN);
    WdfDeviceInitSetExclusive(DeviceInit, TRUE);

    //
    // Initialize attributes structure.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&wdfObjAttributes);

    //
    // Specify the context type for the WDF device object.
    //
    WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&wdfObjAttributes, WMI_SAMPLE_DEVICE_DATA);

    //
    // Specify the callback function for cleaning up the WDF device object specific
    // memory allocations.
    //
    wdfObjAttributes.EvtDestroyCallback = WmiSampDeviceEvtDestroyCallback;

    //
    // Create a Framework Device object.
    //
    status = WdfDeviceCreate(&DeviceInit, &wdfObjAttributes, &deviceObject);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, WmiSampEvtDeviceAdd\n", status));
        return status;
    }

    //
    // Get the Device Object context and initialize the data blocks with specific
    // data.
    //
    wmiDeviceData = GetWmiSampleDeviceData(deviceObject);
    wmiDeviceData->Ec1Count = EC1_COUNT;
    wmiDeviceData->Ec2Count = EC2_COUNT;

    //
    // Create a wdf spin lock object to protect access to the EC1 data and parent
    // the object to the device object.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&wdfObjAttributes);
    wdfObjAttributes.ParentObject = deviceObject;

    status = WdfSpinLockCreate(&wdfObjAttributes, &wmiDeviceData->Ec1Lock);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, WmiSampEvtDeviceAdd\n", status));
        return status;
    }

    //
    // Create a wdf spin lock object to protect access to the EC2 data and parent
    // the object to the device object.
    //
    status = WdfSpinLockCreate(&wdfObjAttributes, &wmiDeviceData->Ec2Lock);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, WmiSampEvtDeviceAdd\n", status));
        return status;
    }

    for (i = 0; i < EC1_COUNT; i++) {
        WmiSampSetEc1(wmiDeviceData,
                      &Ec1,
                      EC1_SIZE,
                      i);
    }

    for (i = 0; i < EC2_COUNT; i++) {
        WmiSampSetEc2(wmiDeviceData,
                      &Ec2,
                      EC2_SIZE,
                      i);
    }

    //
    // Register the WMI providers and create provider instances for this Framework
    // device object.
    //
    status = WmiSampWmiRegistration(deviceObject);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, WmiSampEvtDeviceAdd\n", status));
        return status;
    }

    //
    // Start a periodic timer to dynamically initialize/uninitialize WMI providers
    // for this device object.
    //
    status = PriStartNewPeriodicTimer(deviceObject, oneMinute, PriTimerCallback);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, WmiSampEvtDeviceAdd\n", status));
        return status;
    }

    return status;
}


VOID
WmiSampDeviceEvtDestroyCallback(
    WDFOBJECT DeviceObject
    )

/*++

Routine Description:

    This callback function is called by the framework when the reference count
    on the WDF device object is zero and the framework is ready to release the
    resouces held by this object. All object specific resource allocations that
    were NOT made by the framework must be released here to avoid a resource
    leak.

Arguments:

    DeviceObject - The WDF device object that is about to be destroyed by the
        framework.

Return Value:

    VOID.

--*/

{
    ULONG index;
    PWMI_SAMPLE_DEVICE_DATA wmiDeviceData;

    wmiDeviceData = GetWmiSampleDeviceData((WDFDEVICE)DeviceObject);

    //
    // Release the memory allocated for EC1. The wdfspinlock Ec1Lock memory
    // will be released automatically by the framework as part of the device
    // object cleanup.
    //
    for (index = 0; index < EC1_COUNT; index++) {
        if (wmiDeviceData->Ec1[index] != NULL) {
            ExFreePool(wmiDeviceData->Ec1[index]);
            wmiDeviceData->Ec1[index] = NULL;
        }
    }

    //
    // Release the memory allocated for EC2. The wdfspinlock Ec2Lock memory
    // will be released automatically by the framework as part of the device
    // object cleanup.
    //
    for (index = 0; index < EC2_COUNT; index++) {
        if (wmiDeviceData->Ec2[index] != NULL) {
            ExFreePool(wmiDeviceData->Ec2[index]);
            wmiDeviceData->Ec2[index] = NULL;
        }
    }

    return;
}


_Success_(return > 0)
ULONG
WmiSampGetEc1(
    _In_    PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _Out_ PVOID Buffer,
    _In_    ULONG Index
    )
{
    ULONG ec1Length = 0;
    if (Index >= EC1_COUNT) {
        return ec1Length;
    }

    //
    // Acquire the lock to protect access to the EC1 data since multiple
    // threads could be trying to access the common data concurrently.
    //
    WdfSpinLockAcquire(WmiDeviceData->Ec1Lock);

    RtlCopyMemory(Buffer,
                  WmiDeviceData->Ec1[Index],
                  WmiDeviceData->Ec1Length[Index]);

    ec1Length = WmiDeviceData->Ec1Length[Index];

    //
    // Release the lock.
    //
    WdfSpinLockRelease(WmiDeviceData->Ec1Lock);

    return ec1Length;
}


VOID
WmiSampSetEc1(
    _In_ PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _In_ PVOID Buffer,
    _In_ ULONG Length,
    _In_ ULONG Index
    )
{
    PEC1 ec1;
    ULONG ec1Length = ALIGN_UP(Length, PVOID);
    PVOID oldBuffer = NULL;

    if (Index >= EC1_COUNT) {
        return;
    }

    ec1 = ExAllocatePoolZero(NonPagedPoolNx, ec1Length, WMI_SAMPLE_TAG);
    if (ec1 != NULL) {

        RtlCopyMemory(ec1, Buffer, Length);

        //
        // Acquire the lock to protect access to the EC1 data since multiple
        // threads could be trying to access the common data concurrently.
        //
        WdfSpinLockAcquire(WmiDeviceData->Ec1Lock);

        oldBuffer = WmiDeviceData->Ec1[Index];
        WmiDeviceData->Ec1[Index] = ec1;
        WmiDeviceData->Ec1Length[Index] = ec1Length;
        WmiDeviceData->Ec1ActualLength[Index] = Length;

        //
        // Release the lock.
        //
        WdfSpinLockRelease(WmiDeviceData->Ec1Lock);

        if (oldBuffer != NULL) {
            ExFreePool(oldBuffer);
        }
    }

    return;
}


_Success_(return > 0)
ULONG
WmiSampGetEc2(
    _In_    PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _Out_ PVOID Buffer,
    _In_    ULONG Index
    )
{
    ULONG ec2Length = 0;
    if (Index >= EC2_COUNT) {
        return ec2Length;
    }

    //
    // Acquire the lock to protect access to the EC2 data since multiple
    // threads could be trying to access the common data concurrently.
    //
    WdfSpinLockAcquire(WmiDeviceData->Ec2Lock);

    RtlCopyMemory(Buffer,
                  WmiDeviceData->Ec2[Index],
                  WmiDeviceData->Ec2Length[Index]);

    ec2Length = WmiDeviceData->Ec2Length[Index];

    //
    // Release the lock.
    //
    WdfSpinLockRelease(WmiDeviceData->Ec2Lock);

    return ec2Length;
}


VOID
WmiSampSetEc2(
    _In_ PWMI_SAMPLE_DEVICE_DATA WmiDeviceData,
    _In_ PVOID Buffer,
    _In_ ULONG Length,
    _In_ ULONG Index
    )
{
    PEC2 ec2;
    ULONG ec2Length = ALIGN_UP(Length, PVOID);
    PVOID oldBuffer = NULL;

    if (Index >= EC2_COUNT) {
        return;
    }

    ec2 = ExAllocatePoolZero(NonPagedPoolNx, ec2Length, WMI_SAMPLE_TAG);
    if (ec2 != NULL) {

        RtlCopyMemory(ec2, Buffer, Length);

        //
        // Acquire the lock to protect access to the EC2 data since multiple
        // threads could be trying to access the common data concurrently.
        //
        WdfSpinLockAcquire(WmiDeviceData->Ec2Lock);

        oldBuffer = WmiDeviceData->Ec2[Index];
        WmiDeviceData->Ec2[Index] = ec2;
        WmiDeviceData->Ec2Length[Index] = ec2Length;
        WmiDeviceData->Ec2ActualLength[Index] = Length;

        //
        // Release the lock.
        //
        WdfSpinLockRelease(WmiDeviceData->Ec2Lock);

        if (oldBuffer != NULL) {
            ExFreePool(oldBuffer);
        }
    }

    return;
}


NTSTATUS
PriStartNewPeriodicTimer(
    _In_ WDFDEVICE DeviceObject,
    _In_ ULONG TimeInMilliSeconds,
    _In_ PFN_WDF_TIMER CallbackFunction
    )

/*++

Routine Description:

    This function creates a new Framework timer object and sets it to fire
    periodically at that specifed time interval.

Arguments:

    DeviceObject - The Framework device object for which the new timer is to be
        created. This device object will be the parent object of the new timer
        object.

    TimeInMilliSeconds - The intervals at which the timer should fire.

    CallbackFunction - The function that needs to get invoked each time the timer
        is fired.

Return Value:

    NT Status code.

--*/

{
    NTSTATUS status;
    WDFTIMER timerObject;
    WDF_TIMER_CONFIG config;
    WDF_OBJECT_ATTRIBUTES attributes;

    //
    // Configure the timer to periodically fire.
    //
    WDF_TIMER_CONFIG_INIT_PERIODIC(&config, CallbackFunction, TimeInMilliSeconds);
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

    //
    // Set the parent object of the timer as the given device object. This causes
    // the timer object to get cleaned up (destroyed) whenever the device object
    // gets destroyed.
    //
    attributes.ParentObject = DeviceObject;

    //
    // Create and start a new Framework timer object.
    //
    status = WdfTimerCreate(&config, &attributes, &timerObject);
    if (!NT_SUCCESS(status)) {

        DebugPrint(("[WmiSamp] Status = 0x%08x, PriStartNewPeriodicTimer\n", status));
        return status;
    }

    WdfTimerStart(timerObject, WDF_REL_TIMEOUT_IN_MS(TimeInMilliSeconds));

    return status;
}


VOID
PriTimerCallback(
    _In_ WDFTIMER Timer
    )

/*++

Routine Description:

    This is the callback function that gets invoked whenever the timer fires.
    The Dynamic WMI registration function is called from this callback function.

Arguments:

    Timer - The Framework timer object that was fired.

Return Value:

    None.

--*/

{
    NTSTATUS status;
    WDFDEVICE deviceObject;

    //
    // Get the parent object for this timer. While creating the timer object, the
    // device object was specified as the parent object for the timer.
    //
    deviceObject = WdfTimerGetParentObject(Timer);

    //
    // Perform the dynamic WMI registration.
    //
    status = WmiSampDynamicWmiRegistration(deviceObject);
    if (!NT_SUCCESS(status)) {
        DebugPrint(("[WmiSamp] Status = 0x%08x, PriTimerCallback\n", status));
    }

}


<|start_filename|>serial/serial/openclos.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation

Module Name:

    openclos.c

Abstract:

    This module contains the code that is very specific to
    opening, closing, and cleaning up in the serial driver.

Environment:

    Kernel mode

--*/

#include "precomp.h"

#if defined(EVENT_TRACING)
#include "openclos.tmh"
#endif


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,SerialGetCharTime)
#pragma alloc_text(PAGESER,SerialEvtFileClose)
#pragma alloc_text(PAGESER,SerialDrainUART)
#pragma alloc_text(PAGESRP0,SerialEvtDeviceFileCreate)
#pragma alloc_text(PAGESRP0,SerialCreateTimersAndDpcs)
#endif // ALLOC_PRAGMA



VOID
SerialEvtDeviceFileCreate (
    IN WDFDEVICE     Device,
    IN WDFREQUEST    Request,
    IN WDFFILEOBJECT FileObject
    )
/*++

Routine Description:

    The framework calls a driver's EvtDeviceFileCreate callback
    when the framework receives an IRP_MJ_CREATE request.
    The system sends this request when a user application opens the
    device to perform an I/O operation, such as reading or writing a file.
    This callback is called synchronously, in the context of the thread
    that created the IRP_MJ_CREATE request.

Arguments:

    Device - Handle to a framework device object.
    FileObject - Pointer to fileobject that represents the open handle.
    CreateParams - Copy of the Create IO_STACK_LOCATION

Return Value:

   VOID.

--*/
{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);

    UNREFERENCED_PARAMETER(FileObject);

    PAGED_CODE();

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
                     "SerialEvtDeviceFileCreate %wZ\n", &extension->DeviceName);

    status = SerialDeviceFileCreateWorker(Device);

    //
    // Complete the WDF request.
    //
    WdfRequestComplete(Request, status);

    return;

}


NTSTATUS
SerialWdmDeviceFileCreate (
    IN WDFDEVICE Device,
    IN PIRP Irp
    )
/*++

Routine Description:

    This is the dispatch routine for IRP_MJ_CREATE. The system sends this
    request when a user application opens the device to perform an I/O
    operation, such as reading or writing a file.

Arguments:

    DeviceObject - Pointer to the device object for this device
    Irp - Pointer to the IRP for the current request

Return Value:

   NT status code

--*/
{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
                     "SerialWdmDeviceFileCreate %wZ\n", &extension->DeviceName);

    status = SerialDeviceFileCreateWorker(Device);

    //
    // Complete the WDM request.
    //
    Irp->IoStatus.Information = 0L;
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}


NTSTATUS
SerialDeviceFileCreateWorker (
    IN WDFDEVICE Device
    )
{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);

    //
    // Create a buffer for the RX data when no reads are outstanding.
    //

    extension->InterruptReadBuffer = NULL;
    extension->BufferSize = 0;

    switch (MmQuerySystemSize()) {

        case MmLargeSystem: {

            extension->BufferSize = 4096;
            extension->InterruptReadBuffer = ExAllocatePool2(
                                                 POOL_FLAG_NON_PAGED,
                                                 extension->BufferSize,
                                                 POOL_TAG
                                                 );

            if (extension->InterruptReadBuffer) {
                break;
            }

        }

        case MmMediumSystem: {

            extension->BufferSize = 1024;
            extension->InterruptReadBuffer = ExAllocatePool2(
                                                 POOL_FLAG_NON_PAGED,
                                                 extension->BufferSize,
                                                 POOL_TAG
                                                 );

            if (extension->InterruptReadBuffer) {
                break;
            }

        }

        case MmSmallSystem: {

            extension->BufferSize = 128;
            extension->InterruptReadBuffer = ExAllocatePool2(
                                                 POOL_FLAG_NON_PAGED,
                                                 extension->BufferSize,
                                                 POOL_TAG
                                                 );

        }

    }

    if (!extension->InterruptReadBuffer) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // By taking a power reference by calling WdfDeviceStopIdle, we prevent the
    // framework from powering down our device due to idle timeout when there
    // is an open handle.  Power reference also moves the device to D0 if we are
    // idled out. If you fail create anywhere later in this routine, do make sure
    // drop the reference.
    //
    status = WdfDeviceStopIdle(Device, TRUE);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // wakeup is not currently enabled
    //

    extension->IsWakeEnabled = FALSE;

    //
    // On a new open we "flush" the read queue by initializing the
    // count of characters.
    //

    extension->CharsInInterruptBuffer = 0;
    extension->LastCharSlot = extension->InterruptReadBuffer +
                              (extension->BufferSize - 1);

    extension->ReadBufferBase = extension->InterruptReadBuffer;
    extension->CurrentCharSlot = extension->InterruptReadBuffer;
    extension->FirstReadableChar = extension->InterruptReadBuffer;

    extension->TotalCharsQueued = 0;

    //
    // We set up the default xon/xoff limits.
    //

    extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
    extension->HandFlow.XonLimit = extension->BufferSize >> 1;

    extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
    extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;

    extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
                                   (extension->BufferSize>>4));

    //
    // Mark the device as busy for WMI
    //

    extension->WmiCommData.IsBusy = TRUE;

    extension->IrpMaskLocation = NULL;
    extension->HistoryMask = 0;
    extension->IsrWaitMask = 0;

    extension->SendXonChar = FALSE;
    extension->SendXoffChar = FALSE;

#if !DBG
    //
    // Clear out the statistics.
    //

    WdfInterruptSynchronize(
        extension->WdfInterrupt,
        SerialClearStats,
        extension
        );
#endif

    //
    // The escape char replacement must be reset upon every open.
    //

    extension->EscapeChar = 0;

    //
    // We don't want the device to be removed or stopped when there is an handle
    //
    // Note to anyone copying this sample as a starting point:
    //
    // This works in this driver simply because this driver supports exactly
    // one open handle at a time.  If it supported more, then it would need
    // counting logic to determine when all the reasons for failing Stop/Remove
    // were gone.
    //
    WdfDeviceSetStaticStopRemove(Device, FALSE);

    //
    // Synchronize with the ISR and let it know that the device
    // has been successfully opened.
    //

    WdfInterruptSynchronize(
        extension->WdfInterrupt,
        SerialMarkOpen,
        extension
        );

    return STATUS_SUCCESS;

}


VOID
SerialEvtFileClose(
    IN WDFFILEOBJECT FileObject
    )

/*++

   EvtFileClose is called when all the handles represented by the FileObject
   is closed and all the references to FileObject is removed. This callback
   may get called in an arbitrary thread context instead of the thread that
   called CloseHandle. If you want to delete any per FileObject context that
   must be done in the context of the user thread that made the Create call,
   you should do that in the EvtDeviceCleanp callback.

Arguments:

    FileObject - Pointer to fileobject that represents the open handle.

Return Value:

   VOID

--*/

{
    PAGED_CODE();

    SerialFileCloseWorker(WdfFileObjectGetDevice(FileObject));
    return;
}


NTSTATUS
SerialWdmFileClose (
    IN WDFDEVICE Device,
    IN PIRP Irp
    )
/*++

Routine Description:

    This is the dispatch routine for IRP_MJ_CLOSE. This is called when all the
    handles represented by the FileObject is closed and all the references to
    the FileObject is removed.

Arguments:

    DeviceObject - Pointer to the device object for this device
    Irp - Pointer to the IRP for the current request

Return Value:

   NT status code

--*/
{
    SerialFileCloseWorker(Device);

    Irp->IoStatus.Information = 0L;
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}


VOID
SerialFileCloseWorker(
    IN WDFDEVICE Device
    )
{
    ULONG flushCount;

    //
    // This "timer value" is used to wait 10 character times
    // after the hardware is empty before we actually "run down"
    // all of the flow control/break junk.
    //
    LARGE_INTEGER tenCharDelay;

    //
    // Holds a character time.
    //
    LARGE_INTEGER charTime;

    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
    PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "In SerialEvtFileClose %wZ\n",
                                                &extension->DeviceName);

    //
    // Acquire the interrupt state lock.
    //
    WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);

    //
    // If the Interrupts are connected, then the hardware state has to be
    // cleaned up now. Note that the EvtFileClose callback gets called for
    // an open file object even though the interrupts have been disabled
    // possibly  due to a Surprise Remove PNP event. In such a case, the
    // Interrupt  object should not be used.
    //
    if (interruptContext->IsInterruptConnected) {

        charTime.QuadPart = -SerialGetCharTime(extension).QuadPart;

        //
        // Do this now so that if the isr gets called it won't do anything
        // to cause more chars to get sent.  We want to run down the hardware.
        //

        SetDeviceIsOpened(extension, FALSE, FALSE);

        //
        // Synchronize with the isr to turn off break if it
        // is already on.
        //

        WdfInterruptSynchronize(
            extension->WdfInterrupt,
            SerialTurnOffBreak,
            extension
            );

        //
        // Wait a reasonable amount of time (20 * fifodepth) until all characters
        // have been emptied out of the hardware.
        //

        for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
           if ((READ_LINE_STATUS(extension, extension->Controller) &
                (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
               (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {

              KeDelayExecutionThread(KernelMode, FALSE, &charTime);
          } else {
             break;
          }
        }

        if (flushCount == 0) {
           SerialMarkHardwareBroken(extension);
        }

        //
        // Synchronize with the ISR to let it know that interrupts are
        // no longer important.
        //

        WdfInterruptSynchronize(
            extension->WdfInterrupt,
            SerialMarkClose,
            extension
            );


        //
        // If the driver has automatically transmitted an Xoff in
        // the context of automatic receive flow control then we
        // should transmit an Xon.
        //

        if (extension->RXHolding & SERIAL_RX_XOFF) {

            //
            // Loop until the holding register is empty.
            //
            while (!(READ_LINE_STATUS(extension, extension->Controller) &
                     SERIAL_LSR_THRE)) {
                KeDelayExecutionThread(
                    KernelMode,
                    FALSE,
                    &charTime
                    );

            }

            WRITE_TRANSMIT_HOLDING(extension,
                extension->Controller,
                extension->SpecialChars.XonChar
                );

            //
            // Wait a reasonable amount of time for the characters
            // to be emptied out of the hardware.
            //

             for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
                if ((READ_LINE_STATUS(extension, extension->Controller) &
                     (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
                    (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
                   KeDelayExecutionThread(KernelMode, FALSE, &charTime);
                } else {
                   break;
                }
             }

             if (flushCount == 0) {
                SerialMarkHardwareBroken(extension);
             }
        }


        //
        // The hardware is empty.  Delay 10 character times before
        // shut down all the flow control.
        //

        tenCharDelay.QuadPart = charTime.QuadPart * 10;

        KeDelayExecutionThread(
            KernelMode,
            TRUE,
            &tenCharDelay
            );

#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.")
        SerialClrDTR(extension->WdfInterrupt, extension);

        //
        // We have to be very careful how we clear the RTS line.
        // Transmit toggling might have been on at some point.
        //
        // We know that there is nothing left that could start
        // out the "polling"  execution path.  We need to
        // check the counter that indicates that the execution
        // path is active.  If it is then we loop delaying one
        // character time.  After each delay we check to see if
        // the counter has gone to zero.  When it has we know that
        // the execution path should be just about finished.  We
        // make sure that we still aren't in the routine that
        // synchronized execution with the ISR by synchronizing
        // ourselve with the ISR.
        //

        if (extension->CountOfTryingToLowerRTS) {

            do {
#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_HIGH, "This warning is due to suppressing the previous one.")
                KeDelayExecutionThread(
                    KernelMode,
                    FALSE,
                    &charTime
                    );

            } while (extension->CountOfTryingToLowerRTS);

            //
            // The execution path should no longer exist that
            // is trying to push down the RTS.  Well just
            // make sure it's down by falling through to
            // code that forces it down.
            //

        }

#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.")
        SerialClrRTS(extension->WdfInterrupt, extension);

        //
        // Clean out the holding reasons (since we are closed).
        //

        extension->RXHolding = 0;
        extension->TXHolding = 0;

        //
        // Mark device as not busy for WMI
        //

        extension->WmiCommData.IsBusy = FALSE;

    }

    //
    // Release the Interrupt state lock.
    //
    WdfWaitLockRelease(interruptContext->InterruptStateLock);

    //
    // All is done.  The port has been disabled from interrupting
    // so there is no point in keeping the memory around.
    //

    extension->BufferSize = 0;
    if (extension->InterruptReadBuffer != NULL) {
       ExFreePool(extension->InterruptReadBuffer);
    }
    extension->InterruptReadBuffer = NULL;

    //
    // Make sure the wake is disabled.
    //
    ASSERT(!extension->IsWakeEnabled);

    SerialDrainTimersAndDpcs(extension);

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_CREATE_CLOSE, "DPC's drained:\n");

    //
    // It's fine for the device to be powered off if there are no open handles.
    //
    WdfDeviceResumeIdle(Device);

    //
    // It's okay to allow the device to be stopped or removed.
    //
    // Note to anyone copying this sample as a starting point:
    //
    // This works in this driver simply because this driver supports exactly
    // one open handle at a time.  If it supported more, then it would need
    // counting logic to determine when all the reasons for failing Stop/Remove
    // were gone.
    //
    WdfDeviceSetStaticStopRemove(Device, TRUE);

    return;

}

BOOLEAN
SerialMarkOpen(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID Context
    )

/*++

Routine Description:

    This routine merely sets a boolean to true to mark the fact that
    somebody opened the device and its worthwhile to pay attention
    to interrupts.

Arguments:

    Context - Really a pointer to the device extension.

Return Value:

    This routine always returns FALSE.

--*/

{

    PSERIAL_DEVICE_EXTENSION extension = Context;

    UNREFERENCED_PARAMETER(Interrupt);

    SerialReset(extension->WdfInterrupt, extension);

    //
    // Prepare for the opening by re-enabling interrupts.
    //
    // We do this my modifying the OUT2 line in the modem control.
    // In PC's this bit is "anded" with the interrupt line.
    //

    WRITE_MODEM_CONTROL(extension,
        extension->Controller,
        (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller) | SERIAL_MCR_OUT2)
        );

    extension->DeviceIsOpened = TRUE;
    extension->ErrorWord = 0;

    return FALSE;

}

VOID
SerialDrainUART(IN PSERIAL_DEVICE_EXTENSION PDevExt,
                IN PLARGE_INTEGER PDrainTime)
{
   PAGED_CODE();

   //
   // Wait until all characters have been emptied out of the hardware.
   //

   while ((READ_LINE_STATUS(PDevExt, PDevExt->Controller) &
           (SERIAL_LSR_THRE | SERIAL_LSR_TEMT))
           != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
        KeDelayExecutionThread(KernelMode, FALSE, PDrainTime);
    }
}

VOID
SerialDisableUART(IN PVOID Context)

/*++

Routine Description:

    This routine disables the UART and puts it in a "safe" state when
    not in use (like a close or powerdown).

Arguments:

    Context - Really a pointer to the device extension.

Return Value:

    This routine always returns FALSE.

--*/

{
   PSERIAL_DEVICE_EXTENSION extension = Context;

   //
   // Prepare for the closing by stopping interrupts.
   //
   // We do this by adjusting the OUT2 line in the modem control.
   // In PC's this bit is "anded" with the interrupt line.
   //

   WRITE_MODEM_CONTROL(extension, extension->Controller,
                       (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller)
                               & ~SERIAL_MCR_OUT2));

   if (extension->FifoPresent) {
      WRITE_FIFO_CONTROL(extension, extension->Controller, (UCHAR)0);
    }
}



BOOLEAN
SerialMarkClose(
    IN WDFINTERRUPT  Interrupt,
    IN PVOID Context
    )

/*++

Routine Description:

    This routine merely sets a boolean to false to mark the fact that
    somebody closed the device and it's no longer worthwhile to pay attention
    to interrupts.  It also disables the UART.

Arguments:

    Context - Really a pointer to the device extension.

Return Value:

    This routine always returns FALSE.

--*/

{

    PSERIAL_DEVICE_EXTENSION extension = Context;

    UNREFERENCED_PARAMETER(Interrupt);

    SerialDisableUART(Context);
    extension->DeviceIsOpened = FALSE;
    extension->DeviceState.Reopen   = FALSE;

    return FALSE;

}

LARGE_INTEGER
SerialGetCharTime(
    IN PSERIAL_DEVICE_EXTENSION Extension
    )

/*++

Routine Description:

    This function will return the number of 100 nanosecond intervals
    there are in one character time (based on the present form
    of flow control.

Arguments:

    Extension - Just what it says.

Return Value:

    100 nanosecond intervals in a character time.

--*/

{
    ULONG dataSize = 0;
    ULONG paritySize;
    ULONG stopSize;
    ULONG charTime;
    ULONG bitTime;
    LARGE_INTEGER tmp;

    PAGED_CODE();

    if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
        dataSize = 5;
    } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                == SERIAL_6_DATA) {
        dataSize = 6;
    } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                == SERIAL_7_DATA) {
        dataSize = 7;
    } else if ((Extension->LineControl & SERIAL_DATA_MASK)
                == SERIAL_8_DATA) {
        dataSize = 8;
    }

    paritySize = 1;
    if ((Extension->LineControl & SERIAL_PARITY_MASK)
            == SERIAL_NONE_PARITY) {

        paritySize = 0;

    }

    if (Extension->LineControl & SERIAL_2_STOP) {

        //
        // Even if it is 1.5, for sanities sake were going
        // to say 2.
        //

        stopSize = 2;

    } else {

        stopSize = 1;

    }

    //
    // First we calculate the number of 100 nanosecond intervals
    // are in a single bit time (Approximately).
    //

    bitTime = (10000000+(Extension->CurrentBaud-1))/Extension->CurrentBaud;
    charTime = bitTime + ((dataSize+paritySize+stopSize)*bitTime);

    tmp.QuadPart = charTime;
    return tmp;

}




<|start_filename|>avstream/avscamera/sys/VideoHwSim.cpp<|end_filename|>
/**************************************************************************

    A/V Stream Camera Sample

    Copyright (c) 2014, Microsoft Corporation.

    File:

        VideoHwSim.cpp

    Abstract:

        A fake h/w simulation for the video pin.  Provides Video pin specific 
        metadata.

    History:

        created 5/28/2014

**************************************************************************/

#include "Common.h"


CVideoHardwareSimulation::CVideoHardwareSimulation(
    _Inout_ CSensor *Sensor,
    _In_    LONG    PinID
)
    : CHardwareSimulation( Sensor, PinID )
    , m_Illuminated(FALSE)
{}


CVideoHardwareSimulation::~CVideoHardwareSimulation()
{}

//
//  Emit metadata here for video pin.
//
void
CVideoHardwareSimulation::
EmitMetadata(
    _Inout_ PKSSTREAM_HEADER    pStreamHeader
)
{
    NT_ASSERT(pStreamHeader);

    //  Add the normal frame info to the metadata
    //  Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized.
    CHardwareSimulation::EmitMetadata( pStreamHeader );

    if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA))
    {
        PKS_FRAME_INFO          pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1);
        PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO)(pFrameInfo + 1);
        ULONG                   BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize;

        //  TODO: This metadata should only be exposed on a sensor category preview pin.
        //        It's possible I should derive a new preview sim to handle this; but for now
        //        we just populate the IR illumination state on every preview pin.  Hopefully 
        //        the app is smart enough to realize that this is meaningless if they have not 
        //        selected an IR mediatype.
        if (BytesLeft >= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION))
        {
            PKSCAMERA_METADATA_FRAMEILLUMINATION pPreviewIllumination =
                (PKSCAMERA_METADATA_FRAMEILLUMINATION)(((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize);
            RtlZeroMemory(pPreviewIllumination, sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION));

            pPreviewIllumination->Header.MetadataId = (ULONG)MetadataId_FrameIllumination;
            pPreviewIllumination->Header.Size = sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);

            //  Toggle the Illumination state for an IR frame.
            CExtendedVidProcSetting State;
            (void)m_Sensor->GetIRTorch(&State);

            if (State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALWAYS_ON)
            {
                m_Illuminated = TRUE;
                pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON;
            }
            else if ((State.Flags & KSCAMERA_EXTENDEDPROP_IRTORCHMODE_ALTERNATING_FRAME_ILLUMINATION))
            {
                m_Illuminated = !m_Illuminated;
                if (m_Illuminated)
                {
                    pPreviewIllumination->Flags = KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON;
                }
            }
            else
            {
                m_Illuminated = FALSE;
            }

            DBG_TRACE("Frame Illumination: Flags=0x%016llX, State=%s",
                State.Flags,
                (pPreviewIllumination->Flags == KSCAMERA_METADATA_FRAMEILLUMINATION_FLAG_ON ? "ON" : "OFF"));

            pMetadata->UsedSize += sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);
            BytesLeft -= sizeof(KSCAMERA_METADATA_FRAMEILLUMINATION);
        }

        CExtendedVidProcSetting FaceDetect;
        m_Sensor->GetFaceDetection(&FaceDetect);

        if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_VIDEO )
        {
            DBG_TRACE("VIDEO");
            EmitFaceMetadata(
                pStreamHeader,
                FaceDetect.GetULONG(),
                FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK);
        }
    }
}



<|start_filename|>avstream/sampledevicemft/multipinmfthelpers.h<|end_filename|>
//*@@@+++@@@@******************************************************************
//
// Microsoft Windows Media Foundation
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//*@@@---@@@@******************************************************************
//
#pragma once
#include "stdafx.h"
#include "common.h"
#include <deque>
using namespace std;
#include <functional>

typedef void(*DMFT_IMAGE_TRANSFORM_FN)(
    const RECT&             rcDest,          // Destination rectangle for the transformation.
    BYTE*                   pDest,           // Destination buffer.
    LONG                    lDestStride,     // Destination stride.
    const BYTE*             pSrc,            // Source buffer.
    LONG                    lSrcStride,      // Source stride.
    DWORD                   dwWidthInPixels, // Image width in pixels.
    DWORD                   dwHeightInPixels // Image height in pixels.
    );
//
//Queue class!!!
//
class Ctee;
//typedef CMFAttributesTrace CMediaTypeTrace; /* Only used for debug. take this out*/


template< typename T ,  HRESULT ( __stdcall T::*Func) ( IMFAsyncResult* ) >
class CDMFTAsyncCallback : public IMFAsyncCallback
{
public:
    CDMFTAsyncCallback( T* parent , DWORD dwWorkQueueId = MFASYNC_CALLBACK_QUEUE_STANDARD) :
        m_Parent(parent),
        m_dwQueueId(dwWorkQueueId)
    { }
    virtual ~CDMFTAsyncCallback() { }

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        HRESULT hr = S_OK;
        if (ppv != nullptr)
        {
            *ppv = nullptr;
            if (riid == __uuidof(IMFAsyncCallback) || riid == __uuidof(IUnknown))
            {
                AddRef();
                *ppv = static_cast<IUnknown*>(this);
            }
            else
            {
                hr = E_NOINTERFACE;
            }
        }
        else
        {
            hr = E_POINTER;
        }
        return hr;
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }
    STDMETHODIMP_(ULONG) Release()
    {
        long cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        *pdwFlags = 0;
        *pdwQueue = m_dwQueueId;
        return E_NOTIMPL;
    }

    STDMETHODIMP Invoke( IMFAsyncResult* pAsyncResult )
    {
        ComPtr<T> spParent;
        {
            // Take a reference on the parent so that
            // shutdown may not yank it from us
            CAutoLock Lock(&m_Lock);
            spParent = m_Parent;
        }
        if (spParent.Get())
        {
            return ((spParent.Get())->*Func)(pAsyncResult);
        }
        return MF_E_SHUTDOWN;
    }
    VOID Shutdown()
    {
        CAutoLock Lock(&m_Lock);
        m_Parent = nullptr; //Break the reference
    }
    T GetParent()
    {
        return m_Parent;
    }
protected:
    CCritSec    m_Lock;
    ComPtr<T>   m_Parent; 
    long        m_cRef = 0;
    DWORD       m_dwQueueId = MFASYNC_CALLBACK_QUEUE_STANDARD;
};


class CPinQueue: public IUnknown{
public:
    CPinQueue(_In_ DWORD _inPinId, _In_ IMFDeviceTransform* pTransform=nullptr);
    ~CPinQueue();

    STDMETHODIMP_(VOID) InsertInternal  ( _In_  IMFSample *pSample = nullptr );
    STDMETHODIMP Insert                 ( _In_ IMFSample *pSample );
    STDMETHODIMP Remove                 (_Outptr_result_maybenull_ IMFSample **pSample);
    virtual STDMETHODIMP RecreateTee    (
        _In_  IMFMediaType *inMediatype,
        _In_ IMFMediaType *outMediatype,
        _In_opt_ IUnknown* punkManager);
#if ((defined NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB))
    STDMETHODIMP RecreateTeeByAllocatorMode(
        _In_  IMFMediaType* inMediatype,
        _In_ IMFMediaType* outMediatype,
        _In_opt_ IUnknown* punkManager,
        _In_ MFSampleAllocatorUsage allocatorUsage,
        _In_opt_ IMFVideoSampleAllocator* pAllcoator);
#endif
    STDMETHODIMP_(VOID) Clear();
  
    //
    //Inline functions
    //
    __inline BOOL Empty()
    {
        return (!m_sampleList.size());
    }
    __inline DWORD pinStreamId()
    {
        return m_dwInPinId;
    }
    __inline GUID pinCategory()
    {
        if (IsEqualCLSID(m_streamCategory, GUID_NULL))
        {
            ComPtr<IMFAttributes> spAttributes;
            if (SUCCEEDED(m_pTransform->GetOutputStreamAttributes(pinStreamId(), spAttributes.ReleaseAndGetAddressOf())))
            {
                (VOID)spAttributes->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &m_streamCategory);

            }
        }
        return m_streamCategory;
    }

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        HRESULT hr = S_OK;
        if (ppv != nullptr)
        {
            *ppv = nullptr;
            if (riid == __uuidof(IUnknown))
            {
                AddRef();
                *ppv = static_cast<IUnknown*>(this);
            }
            else
            {
                hr = E_NOINTERFACE;
            }
        }
        else
        {
            hr = E_POINTER;
        }
        return hr;
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }
    STDMETHODIMP_(ULONG) Release()
    {
        long cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }

private:
    DWORD                m_dwInPinId;           /* This is the input pin       */
    IMFSampleList        m_sampleList;          /* List storing the samples    */
    IMFDeviceTransform*  m_pTransform;         /* Weak reference to the the device MFT */
    GUID                 m_streamCategory;
    ULONG                m_cRef;
protected:
    ComPtr<Ctee>         m_spTeer;                /*Tee that acts as a passthrough or an XVP  */
 };


#ifdef MF_DEVICEMFT_ADD_GRAYSCALER_
class CPinQueueWithGrayScale:public  CPinQueue{
public:
    CPinQueueWithGrayScale(_In_ DWORD dwPinId, _In_ IMFDeviceTransform* pParent) :CPinQueue(dwPinId,pParent)
    {
    }
    STDMETHODIMP RecreateTee(_In_  IMFMediaType *inMediatype, _In_ IMFMediaType *outMediatype, _In_opt_ IUnknown* punkManager);
};
#endif
//
// Define these in different components
// The below classes are used to add the
// XVP and the Decoder components.
//
class Ctee: public IUnknown{
public:
    // This is a helper class to release the interface
    // It will first call shutdowntee to break any circular
    // references any components might have with their composed
    // objects
    static VOID ReleaseTee( _In_ ComPtr<Ctee> &tee)
    {
        if (tee)
        {
            tee->ShutdownTee();
            tee = nullptr;
        }
    }
    STDMETHOD(Start)()
    {
        return S_OK;
    }
    STDMETHOD(Stop)()
    {
        return S_OK;
    }
   virtual STDMETHODIMP PassThrough( _In_ IMFSample * ) = 0;

   STDMETHOD_(VOID, ShutdownTee)()
   {
       return; // NOOP
   }
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
   {
       HRESULT hr = S_OK;
       if (ppv != nullptr)
       {
           *ppv = nullptr;
           if (riid == __uuidof(IUnknown))
           {
               AddRef();
               *ppv = static_cast<IUnknown*>(this);
           }
           else
           {
               hr = E_NOINTERFACE;
           }
       }
       else
       {
           hr = E_POINTER;
       }
       return hr;
   }
   Ctee()
   {
   }
   virtual ~Ctee()
   {}
   STDMETHODIMP_(ULONG) AddRef()
   {
       return InterlockedIncrement(&m_cRef);
   }
   STDMETHODIMP_(ULONG) Release()
   {
       long cRef = InterlockedDecrement(&m_cRef);
       if (cRef == 0)
       {
           delete this;
       }
       return cRef;
   }

protected:
    ULONG m_cRef = 0;
};


class CNullTee:public Ctee{
public:
    CNullTee(_In_ CPinQueue* q)
        : m_Queue(q)
    {
    }
    STDMETHODIMP PassThrough( _In_ IMFSample* );

protected:
    // Store the queue here for simplicity
    ComPtr<CPinQueue> m_Queue;
};


class CWrapTee : public Ctee
{
public:
    CWrapTee( _In_ Ctee *tee=nullptr ) 
    : m_spObjectWrapped(tee)
    , m_pInputMediaType(nullptr)
    , m_pOutputMediaType(nullptr)
    {

    }
    virtual ~CWrapTee()
    {
    }

    STDMETHODIMP PassThrough        ( _In_ IMFSample* );
    virtual STDMETHODIMP Do         ( _In_ IMFSample* pSample, _Out_ IMFSample ** , _Inout_ bool &newSample) = 0;
    STDMETHODIMP SetMediaTypes(_In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType);
    //
    // Inline functions
    //
protected:
    
    __inline IMFMediaType* getInMediaType()
    {
        IMFMediaType* pmediaType = nullptr;
        m_pInputMediaType.CopyTo(&pmediaType);
        return pmediaType;
    }
    __inline IMFMediaType* getOutMediaType()
    {
        IMFMediaType* pmediaType = nullptr;
        m_pOutputMediaType.CopyTo(&pmediaType);
        return pmediaType;
    }

protected:
    
    ComPtr< IMFMediaType > m_pInputMediaType;
    ComPtr< IMFMediaType > m_pOutputMediaType;
    ComPtr<Ctee> m_spObjectWrapped;
};

//
// wrapper class for encoder and decoder
//
class CVideoProcTee: public CWrapTee
{
public:

    CVideoProcTee( _In_ Ctee* p,  _In_ GUID category = PINNAME_PREVIEW
        , _In_ IMFVideoSampleAllocator* sampleAllocator=nullptr
    )
        :CWrapTee(p)
        , m_bProducesSamples(FALSE)
        , m_asyncHresult(S_OK)
        , m_streamCategory(category)
        , m_fSetD3DManager(FALSE)
        , m_spDefaultAllocator(sampleAllocator)
    {}
    __inline IMFTransform* Transform()
    {
        return m_spVideoProcessor.Get();
    }

    VOID SetD3DManager( _In_opt_ IUnknown* pUnk )
    {
        m_spDeviceManagerUnk = pUnk;
    }

    STDMETHODIMP SetMediaTypes(_In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType);
    virtual STDMETHODIMP Configure(_In_ IMFMediaType *, _In_ IMFMediaType *, _Inout_ IMFTransform**) = 0;
    STDMETHOD(CreateAllocator)();
    STDMETHOD(Stop)()
    {
        HRESULT hr = S_OK;
        if (SUCCEEDED(hr = StopStreaming()))
        {
            if (m_spObjectWrapped)
            {
                hr = m_spObjectWrapped->Stop();
            }
        }
        return hr;
    }
    virtual ~CVideoProcTee()
    {}
protected:
    CCritSec m_Lock;
    __inline VOID SetAsyncStatus(_In_ HRESULT hrStatus)
    {
        InterlockedCompareExchange(&m_asyncHresult, hrStatus, S_OK);
    }
    HRESULT GetAsyncStatus()
    {
        return InterlockedCompareExchange(&m_asyncHresult, S_OK, S_OK);
    }
    STDMETHOD(StartStreaming)() = 0;
    STDMETHOD(StopStreaming)()  = 0;
    HRESULT              m_asyncHresult;
    ComPtr< IMFTransform > m_spVideoProcessor;
    ComPtr<IUnknown>       m_spDeviceManagerUnk;
    ComPtr<IMFVideoSampleAllocatorEx> m_spPrivateAllocator;
    ComPtr<IMFVideoSampleAllocator> m_spDefaultAllocator;

    BOOL                   m_bProducesSamples;
    GUID                   m_streamCategory;
    BOOL                   m_fSetD3DManager;
};

class CXvptee :public CVideoProcTee{
public:
    CXvptee( _In_ Ctee *, _In_ GUID category = PINNAME_PREVIEW );
    virtual ~CXvptee();
    STDMETHOD(StartStreaming)();
    STDMETHOD(StopStreaming)();
    STDMETHODIMP Do             (   _In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample);
    STDMETHODIMP Configure      (   _In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform** );

};

class CDecoderTee : public CVideoProcTee {
public:

    CDecoderTee(_In_ Ctee *t,_In_ DWORD dwQueueId,GUID category=PINNAME_PREVIEW) :
        CVideoProcTee(t)
        , m_fAsyncMFT(FALSE)
        , m_D3daware(FALSE)
        , m_hwMFT(FALSE)
        , m_asyncCallback(nullptr)
        , m_asyncHresult(S_OK)
        , m_lNeedInputRequest(0)
        , m_streamCategory(category)
        , m_bXvpAdded(FALSE)
        , m_dwMFTInputId(0)
        , m_dwMFTOutputId(0)
        , m_dwQueueId(dwQueueId)
        , m_dwCameraStreamWorkQueueId(0)
    {
        m_streamCategory = category;
    }
    virtual ~CDecoderTee();

    STDMETHODIMP Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample);
    STDMETHODIMP Configure(_In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform**);
    STDMETHODIMP Invoke(_In_ IMFAsyncResult*);
protected:
    STDMETHODIMP StartStreaming();
    STDMETHODIMP StopStreaming();
    HRESULT GetSample( _Outptr_result_maybenull_ IMFSample**);
    HRESULT ConfigDecoder( _In_ IMFTransform* ,_In_ GUID guidSubType = GUID_NULL);
    HRESULT ConfigRealTimeMFT(_In_ IMFTransform* );
    HRESULT ProcessOutputSync( _COM_Outptr_opt_ IMFSample** );
    HRESULT ProcessFormatChange();
    STDMETHOD_(VOID, ShutdownTee)();

    BOOL                 m_fAsyncMFT;
    BOOL                 m_D3daware;
    BOOL                 m_hwMFT;
    ComPtr<CDMFTAsyncCallback<CDecoderTee, &CDecoderTee::Invoke> >     m_asyncCallback;
    HRESULT              m_asyncHresult;
    DWORD                m_lNeedInputRequest;
    GUID                 m_streamCategory; // Needed for bind flags
    BOOL                 m_bXvpAdded;
    DWORD                m_dwMFTInputId;
    DWORD                m_dwMFTOutputId;
    ComPtr<IMFSample>    m_spUnprocessedSample;
    DWORD                m_dwQueueId;
    DWORD                m_dwCameraStreamWorkQueueId;
    std::deque<ComPtr<IMFSample> > m_InputSampleList;

};

class CSampleCopytee :public CVideoProcTee {
public:
    CSampleCopytee(_In_ Ctee*, _In_ GUID category = PINNAME_PREVIEW
        , _In_ IMFVideoSampleAllocator* sampleAllocator = nullptr
    );
    ~CSampleCopytee();
    STDMETHOD(StartStreaming)();
    STDMETHOD(StopStreaming)();
    STDMETHODIMP Do(_In_ IMFSample* pSample, _Outptr_ IMFSample **, _Inout_ bool &newSample);
    STDMETHODIMP Configure(_In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform**);
};

#ifdef MF_DEVICEMFT_ADD_GRAYSCALER_
class CGrayTee : public CWrapTee {
public:
    CGrayTee(Ctee*);
    ~CGrayTee() {
    }

    STDMETHODIMP Do(_In_ IMFSample* pSample, _Out_ IMFSample **, , _Inout_ bool &newSample);
    STDMETHODIMP Configure(_In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform**);
private:
    // Function pointer for the function that transforms the image.
    DMFT_IMAGE_TRANSFORM_FN m_transformfn;
    RECT m_rect;
};
#endif
/*
################## EVENT HANDLING #############################################
Events are usually divided into two categories by the Capture Pipeline
1) Regular Event / Manual Reset Event
2) One Shot Event

Regular events are set and cleared by the pipeline
One shot Events will only be set by the pipeline and it should be cleared
by the Component managing the Event Store i.e. KS or DMFT. For Redstone
The pipeline should not send any Non One shot events.This sample however
does show the handling of Regular/ Manual Reset events

This clearing of One shot events is done when the event is fired..
E.g before sending the warm start command The pipeline will send a one shot event
KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART and when the operation completes the
event should be fired.
The Device MFT should usually keep a watch for one shot events sent by the Pipeline
for Async Extended controls..The list of Asynchronous controls are as follows..

KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE
KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE
KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE
KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID
KSPROPERTY_CAMERACONTROL_EXTENDED_ISO
KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED
KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION
KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE
KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE
KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE
KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL
KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART
KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL
KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE
The complete list can be found from the Camera DDI spec
###############################################################################
*/

typedef struct _DMFTEventEntry{
    ULONG   m_ulEventId;        // KSEVENT->Id
    PVOID   m_pEventData;       // Lookup for events in the data structure
    HANDLE  m_hHandle;          // The duplicate handle stored from the event
    //
    // Constructor. We simply cache the handles, the property id and the KSEVENTDATA buffer sent from the user mode
    //
    _DMFTEventEntry( _In_ ULONG ulEventId, _In_ PVOID pEventData, _In_ HANDLE pHandle):m_pEventData(pEventData)
        , m_ulEventId(ulEventId)
        , m_hHandle(pHandle)
    {
    }
    ~_DMFTEventEntry()
    {
        if ( m_hHandle != nullptr )
        {
            CloseHandle(m_hHandle);
            m_hHandle = nullptr;
        }
    }

}DMFTEventEntry, *PDMFTEventEntry;

//
// Handler for one shot events and Normal events
//
class CDMFTEventHandler{
public:
    //
    // Handle the events here
    //
    HRESULT KSEvent( _In_reads_bytes_(ulEventLength) PKSEVENT pEvent,
        _In_ ULONG ulEventLength,
        _Inout_updates_bytes_opt_(ulDataLength) LPVOID pEventData,
        _In_ ULONG ulDataLength,
        _Inout_ ULONG* pBytesReturned);
    HRESULT SetOneShot(ULONG);
    HRESULT SetRegularEvent(ULONG);
    HRESULT Clear();
protected:
    HRESULT Dupe (_In_ HANDLE hEventHandle, _Outptr_ LPHANDLE lpTargetHandle);
private:
    map< ULONG, HANDLE >        m_OneShotEventMap;
    vector< PDMFTEventEntry >   m_RegularEventList;
};

class CMultipinMft;
class CInPin;
class COutPin;

class CPinCreationFactory {
protected:
    ComPtr<CMultipinMft> m_spDeviceTransform;
public:
    typedef enum _type_pin {
        DMFT_PIN_INPUT,
        DMFT_PIN_OUTPUT,
        DMFT_PIN_CUSTOM,
        DMFT_PIN_ALLOCATOR_PIN,
        DMFT_MAX
    }type_pin;
    HRESULT CreatePin( _In_ ULONG ulInputStreamId, _In_ ULONG ulOutStreamId, _In_ type_pin type,_Outptr_ CBasePin** ppPin, _In_ BOOL& isCustom);
    CPinCreationFactory(_In_ CMultipinMft* pDeviceTransform):m_spDeviceTransform(pDeviceTransform){
    }
};
HRESULT CheckPinType(_In_ IMFAttributes* pAttributes, _In_ GUID pinType, _Out_ PBOOL pbIsImagePin);
BOOL    CheckImagePin(_In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsImagePin);
HRESULT CheckPreviewPin(_In_ IMFAttributes* pAttributes, _Out_ PBOOL pbIsPreviewPin);




<|start_filename|>storage/miniports/storahci/src/io.h<|end_filename|>
/*++

Copyright (C) Microsoft Corporation, 2009

Module Name:

    io.h

Abstract:



Notes:

Revision History:

--*/

#pragma once

ULONG
GetSlotToActivate(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ ULONG                   TargetSlots
    );

UCHAR
GetSingleIo(
    PAHCI_CHANNEL_EXTENSION ChannelExtension
    );

BOOLEAN
ActivateQueue(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ BOOLEAN AtDIRQL
    );

VOID
AddQueue (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Inout_ PSTORAHCI_QUEUE     Queue,
    _In_ PSTORAGE_REQUEST_BLOCK Srb,
    _In_ ULONG Signature,
    _In_ UCHAR Tag
    );

PSTORAGE_REQUEST_BLOCK
RemoveQueue (
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _Inout_ PSTORAHCI_QUEUE Queue,
    _In_ ULONG Signature,
    _In_ UCHAR Tag
    );

VOID
AhciCompleteIssuedSRBs(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR SrbStatus,
    _In_ BOOLEAN AtDIRQL
  );

VOID
SRBtoATA_CFIS(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PSLOT_CONTENT SlotContent
  );

VOID
SRBtoATAPI_CFIS(
    PAHCI_CHANNEL_EXTENSION ChannelExtension,
    PSLOT_CONTENT SlotContent
  );

VOID
CfistoATA_CFIS(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent
  );

ULONG
SRBtoPRDT(
    _In_ PVOID ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent
  );

VOID
SRBtoCmdHeader(
    _In_ PVOID ChannelExtension,
    _In_ PSLOT_CONTENT SlotContent,
    _In_ ULONG Length,
    _In_ BOOLEAN Reset
  );

BOOLEAN
AhciProcessIo(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    );

BOOLEAN
AhciFormIo(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    );

PSCSI_REQUEST_BLOCK
BuildRequestSenseSrb(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  FailingSrb
    );

VOID
AhciPortFailAllIos(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ UCHAR   SrbStatus,
    _In_ BOOLEAN AtDIRQL
    );

HW_DPC_ROUTINE AhciPortSrbCompletionDpcRoutine;

HW_DPC_ROUTINE AhciPortBusChangeDpcRoutine;



<|start_filename|>audio/sysvad/EndpointsCommon/UsbHsDeviceFormats.h<|end_filename|>
//----------------------------------------------------------------------------
//
// Formats
// This reflects the Data formats of the hardware mixer output
// Hardware vendor should retrieve these formats from the hardware and use to figure
// out an intersection with the sideband data-range
//
// Defines a data structure containing the KSDATAFORMAT_WAVEFORMATEXTENSIBLE
// structure for Audio Driver to build data range intersection
//
// NOTE: This is defined here because of the dependencies on the ks headers
//----------------------------------------------------------------------------

static
KSDATAFORMAT_WAVEFORMATEXTENSIBLE UsbHsSpeakerSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                44100,
                88200,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                48000,
                96000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                288000,
                6,
                24,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            24,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};

static
KSDATAFORMAT_WAVEFORMATEXTENSIBLE UsbHsMicSupportedDeviceFormats[] =
{
    { // 0
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                44100,
                176400,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 1
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                44100,
                88200,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 2
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                192000,
                4,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 3
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                1,
                48000,
                96000,
                2,
                16,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            16,
            KSAUDIO_SPEAKER_MONO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    },
    { // 4
        {
            sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
            0,
            0,
            0,
            STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
            STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
        },
        {
            {
                WAVE_FORMAT_EXTENSIBLE,
                2,
                48000,
                288000,
                6,
                24,
                sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
            },
            24,
            KSAUDIO_SPEAKER_STEREO,
            STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM)
        }
    }
};


<|start_filename|>audio/tests/KSPosTst/timetest.cpp<|end_filename|>
// ------------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Module Name:
//
//  timetest.cpp
//
// Abstract:
//
//  Functions to precisely measure time.
//
// -------------------------------------------------------------------------------

#include "PreComp.h"
#include <math.h>

// ------------------------------------------------------------------------------
// returns millisecs
double tpQPC (void)
{
    static LARGE_INTEGER liFrequency = { 0 };
    static LARGE_INTEGER liStart = { 0 };
    static bool bInitialized = false;

    if (!bInitialized)
    {
        if (!QueryPerformanceFrequency(&liFrequency))
        {
            throw ("QueryPerformanceFrequency failed");
        }
        
        if (!QueryPerformanceCounter(&liStart))
        {
            throw ("QueryPerformanceCounter failed");
        }
        
        bInitialized = true;
    }

    LARGE_INTEGER liNow = { 0 };

    if (!QueryPerformanceCounter(&liNow))
    {
        throw ("QueryPerformanceCounter failed");
    }

    // milliseconds since test start
    return 1000.0 * (liNow.QuadPart - liStart.QuadPart) / liFrequency.QuadPart;
}


<|start_filename|>audio/sysvad/APO/AecApo/AecApoMfx.cpp<|end_filename|>
//
// AecApoMFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description:
//
//  Implementation of CAecApoMFX
//

#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlsync.h>
#include <mmreg.h>

#include <initguid.h>
#include <audioenginebaseapo.h>
#include <baseaudioprocessingobject.h>
#include <resource.h>

#include <float.h>

#include "AecApo.h"
#include <devicetopology.h>
#include <CustomPropKeys.h>

#define SUPPORTED_AEC_SAMPLINGRATE (16000)

// Static declaration of the APO_REG_PROPERTIES structure
// associated with this APO.  The number in <> brackets is the
// number of IIDs supported by this APO.  If more than one, then additional
// IIDs are added at the end
#pragma warning (disable : 4815)
const AVRT_DATA CRegAPOProperties<1> CAecApoMFX::sm_RegProperties(
    __uuidof(AecApoMFX),                           // clsid of this APO
    L"Sample MFX Aec APO",                         // friendly name of this APO
    L"Copyright (c) Microsoft Corporation",         // copyright info
    1,                                              // major version #
    0,                                              // minor version #
    __uuidof(IAudioProcessingObject),               // iid of primary interface
    (APO_FLAG) (APO_FLAG_BITSPERSAMPLE_MUST_MATCH | APO_FLAG_FRAMESPERSECOND_MUST_MATCH), // kak check this
    DEFAULT_APOREG_MININPUTCONNECTIONS,
    DEFAULT_APOREG_MAXINPUTCONNECTIONS,
    DEFAULT_APOREG_MINOUTPUTCONNECTIONS,
    DEFAULT_APOREG_MAXOUTPUTCONNECTIONS,
    DEFAULT_APOREG_MAXINSTANCES
    );


#pragma AVRT_CODE_BEGIN
//-------------------------------------------------------------------------
// Description:
//
//  Do the actual processing of data.
//
// Parameters:
//
//      u32NumInputConnections      - [in] number of input connections
//      ppInputConnections          - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers
//      u32NumOutputConnections      - [in] number of output connections
//      ppOutputConnections         - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers
//
// Return values:
//
//      void
//
// Remarks:
//
//  This function processes data in a manner dependent on the implementing
//  object.  This routine can not fail and can not block, or call any other
//  routine that blocks, or touch pagable memory.
//
STDMETHODIMP_(void) CAecApoMFX::APOProcess(
    UINT32 u32NumInputConnections,
    APO_CONNECTION_PROPERTY** ppInputConnections,
    UINT32 u32NumOutputConnections,
    APO_CONNECTION_PROPERTY** ppOutputConnections)
{
    UNREFERENCED_PARAMETER(u32NumInputConnections);
    UNREFERENCED_PARAMETER(u32NumOutputConnections);

    FLOAT32 *pf32InputFrames, *pf32OutputFrames;

    ATLASSERT(m_bIsLocked);

    // assert that the number of input and output connectins fits our registration properties
    ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections);
    ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections);
    ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections);
    ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections);

    ATLASSERT(ppInputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE);
    ATLASSERT(ppOutputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE);

    APO_CONNECTION_PROPERTY_V2* inConnection = reinterpret_cast<APO_CONNECTION_PROPERTY_V2*>(ppInputConnections[0]);
    APO_CONNECTION_PROPERTY_V2* outConnection = reinterpret_cast<APO_CONNECTION_PROPERTY_V2*>(ppOutputConnections[0]);

    // check APO_BUFFER_FLAGS.
    switch( ppInputConnections[0]->u32BufferFlags )
    {
        case BUFFER_INVALID:
        {
            ATLASSERT(false);  // invalid flag - should never occur.  don't do anything.
            break;
        }
        case BUFFER_VALID:
        case BUFFER_SILENT:
        {
            // get input pointer to connection buffer
            pf32InputFrames = reinterpret_cast<FLOAT32*>(ppInputConnections[0]->pBuffer);
            ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) );

            // get output pointer to connection buffer
            pf32OutputFrames = reinterpret_cast<FLOAT32*>(ppOutputConnections[0]->pBuffer);
            ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) );

            //
            // Provide microphone buffer and timestamps to AEC algorithm
            //
            UNREFERENCED_PARAMETER(inConnection);
            UNREFERENCED_PARAMETER(outConnection);

            // Set the valid frame count.
            ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount;
            ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags;

            break;
        }
        default:
        {
            ATLASSERT(false);  // invalid flag - should never occur
            break;
        }
    } // switch

} // APOProcess
#pragma AVRT_CODE_END

//-------------------------------------------------------------------------
// Description:
//
// Parameters:
//
//      pTime                       - [out] hundreds-of-nanoseconds
//
// Return values:
//
//      S_OK on success, a failure code on failure
STDMETHODIMP CAecApoMFX::GetLatency(HNSTIME* pTime)  
{  
    ASSERT_NONREALTIME();  
    HRESULT hr = S_OK;  
  
    IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit);  
  
    *pTime = 0;

Exit:  
    return hr;  
}


//-------------------------------------------------------------------------
// Description:
//
//  Verifies that the APO is ready to process and locks its state if so.
//
// Parameters:
//
//      u32NumInputConnections - [in] number of input connections attached to this APO
//      ppInputConnections - [in] connection descriptor of each input connection attached to this APO
//      u32NumOutputConnections - [in] number of output connections attached to this APO
//      ppOutputConnections - [in] connection descriptor of each output connection attached to this APO
//
// Return values:
//
//      S_OK                                Object is locked and ready to process.
//      E_POINTER                           Invalid pointer passed to function.
//      APOERR_INVALID_CONNECTION_FORMAT    Invalid connection format.
//      APOERR_NUM_CONNECTIONS_INVALID      Number of input or output connections is not valid on
//                                          this APO.
STDMETHODIMP CAecApoMFX::LockForProcess(UINT32 u32NumInputConnections,
    APO_CONNECTION_DESCRIPTOR** ppInputConnections,  
    UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
{
    ASSERT_NONREALTIME();
    HRESULT hr = S_OK;

    UNCOMPRESSEDAUDIOFORMAT  uncompAudioFormat;

    // fill in the samples per frame for the output (since APO_FLAG_SAMPLESPERFRAME_MUST_MATCH is not selected)
    // There are two potentially different samples per frame values here. The input, which will be interleaved + primary. 
    // And the output, which is just the primary. Because this is used for clearing the zeroing the output buffer, we're going
    // to fill it in with the output samples per frame. ProcessBuffer has both.
    hr = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&uncompAudioFormat);
    IF_FAILED_JUMP(hr, Exit);

    m_u32SamplesPerFrame = uncompAudioFormat.dwSamplesPerFrame;

    hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections,
        ppInputConnections, u32NumOutputConnections, ppOutputConnections);
    IF_FAILED_JUMP(hr, Exit);
    
Exit:
    return hr;
}

// The method that this long comment refers to is "Initialize()"
//-------------------------------------------------------------------------
// Description:
//
//  Generic initialization routine for APOs.
//
// Parameters:
//
//     cbDataSize - [in] the size in bytes of the initialization data.
//     pbyData - [in] initialization data specific to this APO
//
// Return values:
//
//     S_OK                         Successful completion.
//     E_POINTER                    Invalid pointer passed to this function.
//     E_INVALIDARG                 Invalid argument
//     AEERR_ALREADY_INITIALIZED    APO is already initialized
//
// Remarks:
//
//  This method initializes the APO.  The data is variable length and
//  should have the form of:
//
//    struct MyAPOInitializationData
//    {
//        APOInitBaseStruct APOInit;
//        ... // add additional fields here
//    };
//
//  If the APO needs no initialization or needs no data to initialize
//  itself, it is valid to pass NULL as the pbyData parameter and 0 as
//  the cbDataSize parameter.
//
//  As part of designing an APO, decide which parameters should be
//  immutable (set once during initialization) and which mutable (changeable
//  during the lifetime of the APO instance).  Immutable parameters must
//  only be specifiable in the Initialize call; mutable parameters must be
//  settable via methods on whichever parameter control interface(s) your
//  APO provides. Mutable values should either be set in the initialize
//  method (if they are required for proper operation of the APO prior to
//  LockForProcess) or default to reasonable values upon initialize and not
//  be required to be set before LockForProcess.
//
//  Within the mutable parameters, you must also decide which can be changed
//  while the APO is locked for processing and which cannot.
//
//  All parameters should be considered immutable as a first choice, unless
//  there is a specific scenario which requires them to be mutable; similarly,
//  no mutable parameters should be changeable while the APO is locked, unless
//  a specific scenario requires them to be.  Following this guideline will
//  simplify the APO's state diagram and implementation and prevent certain
//  types of bug.
//
//  If a parameter changes the APOs latency or MaxXXXFrames values, it must be
//  immutable.
//
//  The default version of this function uses no initialization data, but does verify
//  the passed parameters and set the m_bIsInitialized member to true.
//
//  Note: This method may not be called from a real-time processing thread.
//

HRESULT CAecApoMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
    HRESULT                     hr = S_OK;

    IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit);
    IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit);

    if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
        //
        // pbyData contains APOInitSystemEffects3 structure describing the microphone endpoint
        //
        APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;
        m_initializedForEffectsDiscovery = papoSysFxInit3->InitializeForDiscoveryOnly;

        // Support for COMMUNICATIONS mode only when streaming
        IF_TRUE_ACTION_JUMP(
            !m_initializedForEffectsDiscovery && (papoSysFxInit3->AudioProcessingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS),
            hr = E_INVALIDARG,
            Exit);
        m_audioSignalProcessingMode = papoSysFxInit3->AudioProcessingMode;

        // Register for notification of endpoint volume change in GetApoNotificationRegistrationInfo      
        // Keep a reference to the device that will be registering for endpoint volume notifcations. 

        IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit);
        // Get the endpoint on which this APO has been created. It is the last device in the device collection.
        UINT32 numDevices;
        hr = papoSysFxInit3->pDeviceCollection->GetCount(&numDevices);
        IF_FAILED_JUMP(hr, Exit);
        IF_TRUE_ACTION_JUMP(numDevices <= 0, hr = E_INVALIDARG, Exit);

        hr = papoSysFxInit3->pDeviceCollection->Item(numDevices - 1, &m_spCaptureDevice);
        IF_FAILED_JUMP(hr, Exit);

        m_bIsInitialized = true;

        // Try to get the logging service, but ignore errors as failure to do logging it is not fatal.
        if(SUCCEEDED(papoSysFxInit3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, IID_PPV_ARGS(&m_apoLoggingService))))
        {
            m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"CAecApoMFX::Initialize called with APOInitSystemEffects3.");
        }        
    }
    else if (cbDataSize == sizeof(APOInitSystemEffects2))
    {
        //
        // pbyData contains APOInitSystemEffects2 structure describing the microphone endpoint
        //
        APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData;
        m_initializedForEffectsDiscovery = papoSysFxInit2->InitializeForDiscoveryOnly;

        // Support for COMMUNICATIONS mode only when streaming
        IF_TRUE_ACTION_JUMP(
            !m_initializedForEffectsDiscovery && (papoSysFxInit2->AudioProcessingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS),
            hr = E_INVALIDARG,
            Exit);
        m_audioSignalProcessingMode = papoSysFxInit2->AudioProcessingMode;

        m_bIsInitialized = true;
    }
    else
    {
        hr = E_INVALIDARG;
    }

Exit:
    return hr;
}

//-------------------------------------------------------------------------
// Description:
//
//
//
// Parameters:
//
//
//
// Return values:
//
//
//
// Remarks:
//
//
STDMETHODIMP CAecApoMFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event)
{
    UNREFERENCED_PARAMETER(Event);

    *ppEffectsIds = NULL;
    *pcEffects = 0;

    if (m_audioSignalProcessingMode == AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS)
    {
        // Return the list of effects implemented by this APO for COMMUNICATIONS processing mode  
        static const GUID effectsList[] = { AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION };

        *ppEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(effectsList));
        if (!*ppEffectsIds)
        {
            return E_OUTOFMEMORY;
        }
        *pcEffects = ARRAYSIZE(effectsList);
        CopyMemory(*ppEffectsIds, effectsList, sizeof(effectsList));
    }

    return S_OK;
}

HRESULT IsInputFormatSupportedForAec(IAudioMediaType* pMediaType, BOOL * pSupported)
{
    ASSERT_NONREALTIME();

    HRESULT hr = S_OK;
    UNCOMPRESSEDAUDIOFORMAT format;

    IF_TRUE_ACTION_JUMP((pMediaType == nullptr || pSupported == nullptr), hr = E_INVALIDARG, exit);
    hr = pMediaType->GetUncompressedAudioFormat(&format);
    IF_FAILED_JUMP(hr, exit);

    *pSupported = format.dwBytesPerSampleContainer == 4 &&
                  format.dwValidBitsPerSample == 32 &&
                  format.fFramesPerSecond == SUPPORTED_AEC_SAMPLINGRATE && // We only support one sampling rate at the input
                  format.dwSamplesPerFrame <= 16 && // We only support <= 16 channels at the input
                  format.guidFormatType == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;

exit:
    return hr;
}

HRESULT IsOutputFormatSupportedForAec(IAudioMediaType* pMediaType, BOOL * pSupported)
{
    ASSERT_NONREALTIME();

    HRESULT hr = S_OK;
    UNCOMPRESSEDAUDIOFORMAT format;

    IF_TRUE_ACTION_JUMP((pMediaType == nullptr || pSupported == nullptr), hr = E_INVALIDARG, exit);
    hr = pMediaType->GetUncompressedAudioFormat(&format);
    IF_FAILED_JUMP(hr, exit);

    *pSupported = format.dwBytesPerSampleContainer == 4 &&
                  format.dwValidBitsPerSample == 32 &&
                  format.fFramesPerSecond == SUPPORTED_AEC_SAMPLINGRATE &&
                  format.dwSamplesPerFrame == 1 && // We only mono output
                  format.guidFormatType == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;

exit:
    return hr;
}

HRESULT
CreatePreferredInputMediaType(IAudioMediaType** ppMediaType, UINT32 requestedInputChannelCount)
{
    ASSERT_NONREALTIME();

    // Default to mono format
    // We will adjust the channel count based on the requested format later on
    UNCOMPRESSEDAUDIOFORMAT format =
    {
        KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
        1, 4, 32, SUPPORTED_AEC_SAMPLINGRATE, KSAUDIO_SPEAKER_DIRECTOUT
    };

    // Match the channel count of the input if it is less than 16
    if (requestedInputChannelCount <= 16)
    {
        format.dwSamplesPerFrame = requestedInputChannelCount;
        format.dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
    }
    
    return CreateAudioMediaTypeFromUncompressedAudioFormat(&format, ppMediaType);
}

HRESULT
CreatePreferredOutputMediaType(IAudioMediaType** ppMediaType)
{
    ASSERT_NONREALTIME();

    // Output is mono @ the same sampling rate as the input
    UNCOMPRESSEDAUDIOFORMAT format =
    {
        KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
        1, 4, 32, SUPPORTED_AEC_SAMPLINGRATE, KSAUDIO_SPEAKER_DIRECTOUT
    };

    return CreateAudioMediaTypeFromUncompressedAudioFormat(&format, ppMediaType);
}

//-------------------------------------------------------------------------
// Description:
//
//
//
// Parameters:
//
//
//
// Return values:
//
//
//
// Remarks:
//
//
STDMETHODIMP CAecApoMFX::IsInputFormatSupported(IAudioMediaType *pOutputFormat, IAudioMediaType *pRequestedInputFormat, IAudioMediaType **ppSupportedInputFormat)
{
    ASSERT_NONREALTIME();
    HRESULT hResult;

    IF_TRUE_ACTION_JUMP((NULL == pRequestedInputFormat) || (NULL == ppSupportedInputFormat), hResult = E_POINTER, Exit);
    *ppSupportedInputFormat = NULL;

    // This method here is called in the context of the MIC endpoint
    // There are 2 supported scenarios
    // - The AEC APO can handle any mic format
    // - The AEC APO can support exactly 1 input format
    //
    // For the purposes of this sample AEC APO, we pretend that the AEC APO supports exactly 1 format 
    // - a sampling rate of SUPPORTED_AEC_SAMPLINGRATE. The APO can accept upto 16 channels at the input
    //   and will output mono audio.
    //

    if (pOutputFormat)
    {
        // Is this a valid format that we support at the output?
        BOOL bSupportedOut = FALSE;
        hResult = IsOutputFormatSupportedForAec(pOutputFormat, &bSupportedOut);
        IF_FAILED_JUMP(hResult, Exit);
        if (!bSupportedOut)
        {
            return APOERR_FORMAT_NOT_SUPPORTED;
        }
    } 
    
    BOOL bSupported = FALSE;
    hResult = IsInputFormatSupportedForAec(pRequestedInputFormat, &bSupported);
    IF_FAILED_JUMP(hResult, Exit);

    if (!bSupported)
    {
        hResult = CreatePreferredInputMediaType(ppSupportedInputFormat, pRequestedInputFormat->GetAudioFormat()->nChannels);
        IF_FAILED_JUMP(hResult, Exit);
        return S_FALSE;
    }
   
    pRequestedInputFormat->AddRef();
    *ppSupportedInputFormat = pRequestedInputFormat;

Exit:

    return hResult;
}

//-------------------------------------------------------------------------
// Description:
//
//
//
// Parameters:
//
//
//
// Return values:
//
//
//
// Remarks:
//
//
STDMETHODIMP CAecApoMFX::IsOutputFormatSupported(IAudioMediaType *pInputFormat, IAudioMediaType *pRequestedOutputFormat, IAudioMediaType **ppSupportedOutputFormat)
{
    ASSERT_NONREALTIME();
    HRESULT hResult;

    IF_TRUE_ACTION_JUMP((NULL == pRequestedOutputFormat) || (NULL == ppSupportedOutputFormat), hResult = E_POINTER, Exit);
    *ppSupportedOutputFormat = NULL;

    if (pInputFormat != nullptr)
    {
        BOOL bSupportedIn = FALSE;
        hResult = IsInputFormatSupportedForAec(pInputFormat, &bSupportedIn);
        IF_FAILED_JUMP(hResult, Exit);
        if (!bSupportedIn)
        {
            return APOERR_FORMAT_NOT_SUPPORTED;
        }
    }

    BOOL bSupported = FALSE;
    hResult = IsOutputFormatSupportedForAec(pRequestedOutputFormat, &bSupported);
    IF_FAILED_JUMP(hResult, Exit);

    if (!bSupported)
    {
        hResult = CreatePreferredOutputMediaType(ppSupportedOutputFormat);
        IF_FAILED_JUMP(hResult, Exit);
        return S_FALSE;
    }

    pRequestedOutputFormat->AddRef();
    *ppSupportedOutputFormat = pRequestedOutputFormat;

Exit:

    return hResult;
}

STDMETHODIMP
CAecApoMFX::AddAuxiliaryInput(
    DWORD dwInputId,
    UINT32 cbDataSize,
    BYTE *pbyData,
    APO_CONNECTION_DESCRIPTOR * pInputConnection
)
{
    HRESULT hResult = S_OK;

    CComPtr<IAudioMediaType> spSupportedType;
    ASSERT_NONREALTIME();

    IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit);
    IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit);

    BOOL bSupported = FALSE;
    hResult = IsInputFormatSupportedForAec(pInputConnection->pFormat, &bSupported);
    IF_FAILED_JUMP(hResult, Exit);
    IF_TRUE_ACTION_JUMP(!bSupported, hResult = APOERR_FORMAT_NOT_SUPPORTED, Exit);

    // This APO can only handle 1 auxiliary input
    IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != 0, hResult = APOERR_NUM_CONNECTIONS_INVALID, Exit);

    m_auxiliaryInputId = dwInputId;

    IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hResult = E_INVALIDARG, Exit);
    IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hResult = E_INVALIDARG, Exit);
    if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
        /*
        //
        // pbyData contains APOInitSystemEffects3 structure describing the loopback endpoint
        //
        APOInitSystemEffects3* papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;

        // Register for notification in GetApoNotificationRegistrationInfo

        // Keep a reference to the loopback device that will be registering for endpoint volume notifcations.

        IF_TRUE_ACTION_JUMP(papoSysFxInit3->pDeviceCollection == nullptr, hResult = E_INVALIDARG, Exit);
        UINT32 numDevices;
        hResult = papoSysFxInit3->pDeviceCollection->GetCount(&numDevices);
        IF_FAILED_JUMP(hResult, Exit);
        IF_TRUE_ACTION_JUMP(numDevices <= 0, hResult = E_INVALIDARG, Exit);

        hResult = papoSysFxInit3->pDeviceCollection->Item(numDevices - 1, &m_spLoopbackDevice);
        IF_FAILED_JUMP(hResult, Exit);
        */
    }
    else
    {
        //
        // pbyData contains APOInitSystemEffects2 structure describing the render endpoint
        //
    }

    // Signal to AEC algorithm that there is a reference audio stream

Exit:
    return hResult;
}

STDMETHODIMP
CAecApoMFX::RemoveAuxiliaryInput(DWORD dwInputId)
{
    HRESULT hResult = S_OK;
    ASSERT_NONREALTIME();

    IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit);
    IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit);

    // This APO can only handle 1 auxiliary input
    IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != dwInputId, hResult = APOERR_INVALID_INPUTID, Exit);

    m_auxiliaryInputId = 0;

    // Signal to AEC algorithm that there is no longer any reference audio stream

Exit:
    return hResult;
}

STDMETHODIMP
CAecApoMFX::IsInputFormatSupported(IAudioMediaType* pRequestedInputFormat,
                                   IAudioMediaType** ppSupportedInputFormat)
{
    ASSERT_NONREALTIME();
    HRESULT hResult = S_OK;

    IF_TRUE_ACTION_JUMP((NULL == pRequestedInputFormat) || (NULL == ppSupportedInputFormat), hResult = E_POINTER, Exit);

    BOOL bSupported = FALSE;
    hResult = IsInputFormatSupportedForAec(pRequestedInputFormat, &bSupported);
    IF_FAILED_JUMP(hResult, Exit);

    if (!bSupported)
    {
        hResult = CreatePreferredInputMediaType(ppSupportedInputFormat, pRequestedInputFormat->GetAudioFormat()->nChannels);
        IF_FAILED_JUMP(hResult, Exit);
        return S_FALSE;
    }

    pRequestedInputFormat->AddRef();
    *ppSupportedInputFormat = pRequestedInputFormat;

Exit:
    return hResult;
}

// IAPOAuxiliaryInputRT
STDMETHODIMP_(void)
CAecApoMFX::AcceptInput(DWORD dwInputId,
                        const APO_CONNECTION_PROPERTY * pInputConnection)
{
    ASSERT_REALTIME();
    ATLASSERT(m_bIsInitialized);
    ATLASSERT(m_bIsLocked);

    ATLASSERT(pInputConnection->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE);
    ATLASSERT(inputId == m_auxiliaryInputId);
    UNREFERENCED_PARAMETER(dwInputId);

    const APO_CONNECTION_PROPERTY_V2* connectionV2 = reinterpret_cast<const APO_CONNECTION_PROPERTY_V2*>(pInputConnection);

    // Check connectionV2->property.u32BufferFlags to see whether loopback buffer is silent
    // Provide loopback buffer and timestamp to AEC algorithm
    UNREFERENCED_PARAMETER(connectionV2);
}

STDMETHODIMP CAecApoMFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count)
{
    *apoNotifications = nullptr;
    *count = 0;

    /*
    RETURN_HR_IF_NULL(E_FAIL, m_spCaptureDevice);
    RETURN_HR_IF_NULL(E_FAIL, m_spLoopbackDevice);

    // Let the OS know what notifications we are interested in by returning an array of
    // APO_NOTIFICATION_DESCRIPTORs.
    constexpr DWORD numDescriptors = 2;
    wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors;

    apoNotificationDescriptors.reset(
        static_cast<APO_NOTIFICATION_DESCRIPTOR*>(CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors)));
    RETURN_IF_NULL_ALLOC(apoNotificationDescriptors);

    // Our APO wants to get notified when a endpoint volume changes on the capture endpoint.
    apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME;
    (void)m_spCaptureDevice->QueryInterface(&apoNotificationDescriptors[0].audioEndpointVolume.device);

    // Our APO wants to get notified when a endpoint volume changes on the auxiliary input endpoint.
    apoNotificationDescriptors[1].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME;
    (void)m_spLoopbackDevice->QueryInterface(&apoNotificationDescriptors[1].audioEndpointVolume.device);

    *apoNotifications = apoNotificationDescriptors.release();
    *count = numDescriptors;
    */

    return S_OK;
}

static bool IsSameEndpointId(IMMDevice* device1, IMMDevice* device2)
{
    bool isSameEndpointId = false;

    wil::unique_cotaskmem_string deviceId1;
    if (SUCCEEDED(device1->GetId(&deviceId1)))
    {
        wil::unique_cotaskmem_string deviceId2;
        if (SUCCEEDED(device2->GetId(&deviceId2)))
        {
            isSameEndpointId = (CompareStringOrdinal(deviceId1.get(), -1, deviceId2.get(), -1, TRUE) == CSTR_EQUAL);
        }
    }
    return isSameEndpointId;
}

// HandleNotification is called whenever there is a change that matches any of the
// APO_NOTIFICATION_DESCRIPTOR elements in the array that was returned by GetApoNotificationRegistrationInfo.
// Note that the APO will have to query each property once to get its initial value because this method is
// only invoked when any of the properties have changed.
STDMETHODIMP_(void) CAecApoMFX::HandleNotification(_In_ APO_NOTIFICATION* /* apoNotification */)
{
    // Handle endpoint volume change
    /* 
    if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE)
    {
        if (IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_spCaptureDevice))
        {            
            m_captureEndpointMasterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume;
        }
        else if (IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_spLoopbackDevice))
        {
            m_loopbackEndpointMasterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume;
        }
    }
    else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE)
    {

    }
    else if(apoNotification->type == APO_NOTIFICATION_TYPE_SYSTEM_EFFECTS_PROPERTY_CHANGE)
    {

    } 
    */
}



<|start_filename|>gnss/gnssUmdf/FixSession.cpp<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    fixsession.cpp

Abstract:

    This file contains the Fix sessions and contains logic to provide position.

Environment:

    Windows User-Mode Driver Framework

--*/

#include "precomp.h"
#include "Trace.h"
#include "Defaults.h"
#include "FixSession.h"
#include "FixHandler.h"

#include "FixSession.tmh"


static void CALLBACK
s_FixThreadStub(
    _Inout_ PTP_CALLBACK_INSTANCE Instance,
    _Inout_opt_ PVOID Context,
    _Inout_ PTP_WORK Work
);

CFixSession::CFixSession() :
    _Id(INVALID_SESSION_ID),
    _State(STOPPED),
    _HasCachedFix(false),
    _StopFixEvent(nullptr),
    _ThreadpoolWork(nullptr)
{
    InitializeCriticalSection(&_Lock);
    _GnssPosition = g_DefaultGnssFixData;
}

CFixSession::~CFixSession()
{
    if (_StopFixEvent != nullptr && _ThreadpoolWork != nullptr)
    {
        SetEvent(_StopFixEvent);
        WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, TRUE);
    }

    if (_StopFixEvent != nullptr)
    {
        CloseHandle(_StopFixEvent);
        _StopFixEvent = nullptr;
    }

    if (_ThreadpoolWork != nullptr)
    {
        CloseThreadpoolWork(_ThreadpoolWork);
        _ThreadpoolWork = nullptr;
    }

    DeleteCriticalSection(&_Lock);
}

NTSTATUS
CFixSession::Initialize(
    _In_ WDFQUEUE Queue
)
{
    EnterCriticalSection(&_Lock);

    NTSTATUS status = STATUS_SUCCESS;

    // set the state
    _State = STOPPED;

    // Create the TP work
    _ThreadpoolWork = CreateThreadpoolWork(
        (PTP_WORK_CALLBACK)s_FixThreadStub,
        (PVOID)this,
        nullptr
    );

    if (_ThreadpoolWork == nullptr)
    {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    // Create the stop fix Event
    _StopFixEvent = CreateEvent(/* lpEventAttributes */ nullptr, /* bManualReset */ FALSE, /* bInitialState */ FALSE, /* lpName */ nullptr);
    if (_StopFixEvent == nullptr)
    {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    // Create the manual dispatch queue
    WDF_IO_QUEUE_CONFIG queueConfiguration;
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfiguration, WdfIoQueueDispatchManual);

    // Initialize the attributes of the queue. Set the execution level to
    // passive to force all IRPs to be sent to the driver at passive IRQL.
    WDF_OBJECT_ATTRIBUTES ioQueueAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT(&ioQueueAttributes);
    ioQueueAttributes.SynchronizationScope = WdfSynchronizationScopeQueue;
    ioQueueAttributes.ExecutionLevel = WdfExecutionLevelPassive;
    status = WdfIoQueueCreate(WdfIoQueueGetDevice(Queue),
                              &queueConfiguration,
                              &ioQueueAttributes,
                              &_FixAcquisitionRequestQueue);

Exit:
    LeaveCriticalSection(&_Lock);
    return status;
}

NTSTATUS
CFixSession::StartAcquiringFix(
    _In_ PGNSS_FIXSESSION_PARAM FixSessionParameters
)
{
    NTSTATUS status = STATUS_SUCCESS;

    EnterCriticalSection(&_Lock);

    // Check the state of the thread.
    // If already running we should not get this request
    // as multiplexing is not supported for Singleshot
    if (_State == ACQUIRING)
    {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    // Create PTP_WORK only when first start fix is called.
    if (_ThreadpoolWork == nullptr)
    {
        // Not initialized. Error out
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    _Id = FixSessionParameters->FixSessionID;
    _State = ACQUIRING;
    _FixParameter = *FixSessionParameters;

    ResetEvent(_StopFixEvent);

    // Post a work object to the thread
    SubmitThreadpoolWork(_ThreadpoolWork);

    // Initialize the GetFix Queue
    WdfIoQueueStart(_FixAcquisitionRequestQueue);

Exit:
    LeaveCriticalSection(&_Lock);

    return status;
}

NTSTATUS
CFixSession::StopAcquiringFix(
    _In_ PGNSS_STOPFIXSESSION_PARAM StopFixSessionParam
)
{
    NTSTATUS status = STATUS_SUCCESS;

    {
        EnterCriticalSection(&_Lock);

        // If Ids don't Match return Error
        if (StopFixSessionParam->FixSessionID != _Id)
        {
            status = STATUS_UNSUCCESSFUL;
            TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Id mismatch in Stop Fix Session call");
            goto Exit;
        }

        // Check the state of the session
        if (_State != ACQUIRING)
        {
            TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Can't stop an inactive session.");
            goto Exit;
        }

        // Stop the current processing of the fix session
        if (_StopFixEvent != nullptr)
        {
            SetEvent(_StopFixEvent);
        }

        LeaveCriticalSection(&_Lock);
    }

    // Wait for any threadpool work to finish.
    if (_ThreadpoolWork != nullptr)
    {
        WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, TRUE);
    }

    EnterCriticalSection(&_Lock);
    _State = STOPPED;

    // Drain the GetFix Queue
    WdfIoQueuePurgeSynchronously(_FixAcquisitionRequestQueue);

Exit:
    LeaveCriticalSection(&_Lock);

    return status;
}

NTSTATUS
CFixSession::ModifyAcquiringFix(
    _In_ PGNSS_FIXSESSION_PARAM ModifyFixSessionParam
)
{
    NTSTATUS status = STATUS_SUCCESS;

    EnterCriticalSection(&_Lock);

    memcpy_s(&_FixParameter, sizeof(GNSS_FIXSESSION_PARAM), ModifyFixSessionParam, sizeof(*ModifyFixSessionParam));

    LeaveCriticalSection(&_Lock);

    return status;
}

NTSTATUS
CFixSession::ReportGnssFix(
    _In_ PGNSS_FIXDATA GnssFix
)
{
    EnterCriticalSection(&_Lock);

    NTSTATUS status = STATUS_SUCCESS;
    WDFREQUEST Request;
    WDF_IO_QUEUE_STATE QueueStatus;
    ULONG uRequest;

    QueueStatus = WdfIoQueueGetState(_FixAcquisitionRequestQueue, &uRequest, nullptr);
    if (!WDF_IO_QUEUE_READY(QueueStatus) || (uRequest == 0))
    {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    if (STATUS_SUCCESS == WdfIoQueueRetrieveNextRequest(_FixAcquisitionRequestQueue, &Request))
    {
        status = CompleteRequestWithGnssFix(Request, GnssFix);
        if (!NT_SUCCESS(status))
        {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Could not respond with fix data with %!STATUS!", status);
            goto Exit;
        }
    }
    else
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Couldn't retrieve the next request with %!STATUS!", status);
        goto Exit;
    }

Exit:
    LeaveCriticalSection(&_Lock);

    return status;
}

NTSTATUS
CFixSession::CompleteRequestWithGnssFix(
    _In_ WDFREQUEST Request,
    PGNSS_FIXDATA GnssFix
)
{
    NTSTATUS status = STATUS_SUCCESS;
    PVOID outputBuffer;
    size_t outputBufferLength;
    PGNSS_EVENT gnssEvent;
    size_t size = FIELD_OFFSET(GNSS_EVENT, FixData) + sizeof(GNSS_FIXDATA);

    status = WdfRequestRetrieveOutputBuffer(Request,
                                            size,
                                            &outputBuffer,
                                            &outputBufferLength);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "WdfRequestRetrieveOutputBuffer() failed with %!STATUS!", status);
        goto Exit;
    }

    gnssEvent = ((GNSS_EVENT *)outputBuffer);
    memset(outputBuffer, 0, size);
    gnssEvent->Version = GNSS_DRIVER_VERSION_2;
    gnssEvent->EventType = GNSS_Event_FixAvailable;
    gnssEvent->EventDataSize = sizeof(GNSS_FIXDATA);
    gnssEvent->Size = (ULONG)size;
    memcpy_s(&gnssEvent->FixData, sizeof(GNSS_FIXDATA), GnssFix, sizeof(GNSS_FIXDATA));
    gnssEvent->FixData.FixSessionID = _Id;

    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, outputBufferLength);
    status = STATUS_PENDING;
Exit:
    return status;
}

NTSTATUS
CFixSession::UpdateCurrentPosition(
    _In_ PGNSS_FIXDATA GnssFix
)
{
    NTSTATUS status = STATUS_SUCCESS;

    EnterCriticalSection(&_Lock);

    memcpy_s(&_GnssPosition, sizeof(GNSS_FIXDATA), GnssFix, sizeof(*GnssFix));

    LeaveCriticalSection(&_Lock);

    return status;
}

NTSTATUS
CFixSession::GetFix(
    _In_ WDFREQUEST Request
)
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG sessionId = INVALID_SESSION_ID;
    PGNSS_EVENT outputEvent = nullptr;
    ULONG *pSessionId = nullptr;

    EnterCriticalSection(&_Lock);

    // Verify the size of where output goes eventually, when this IRP is serviced
    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(GNSS_EVENT), (void**)&outputEvent, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Could not retrieve output buffer for request with %!STATUS!", status);
        goto Exit;
    }

    // Get the session ID this get fix is for
    status = WdfRequestRetrieveInputBuffer(Request, sizeof(sessionId), (void**)&pSessionId, nullptr);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Could not retrieve input sessionID for request");
        goto Exit;
    }

    sessionId = *pSessionId;

    // Check if the Ids match
    if (_Id != sessionId)
    {
        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }
    // If we have a cached fix around because we tried to respond while there
    // weren't any getfixes around, answer immediately with it
    if (_HasCachedFix)
    {
        status = CompleteRequestWithGnssFix(Request, &_GnssPosition);

        _HasCachedFix = !(NT_SUCCESS(status));
    }
    // Otherwise, we push the request into a queue
    else
    {
        status = WdfRequestForwardToIoQueue(Request, _FixAcquisitionRequestQueue);
        if (!NT_SUCCESS(status))
        {
            TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "WdfRequestForwardToIoQueue failed with %!STATUS!", status);
            goto Exit;
        }

        status = STATUS_PENDING;
    }

Exit:
    LeaveCriticalSection(&_Lock);

    return status;
}

static void CALLBACK
s_FixThreadStub(
    _Inout_ PTP_CALLBACK_INSTANCE /*Instance*/,
    _Inout_opt_ PVOID Context,
    _Inout_ PTP_WORK /*Work*/
)
{
    CFixSession *pFixSession = static_cast<CFixSession *>(Context);

    pFixSession->AcquireSingleFix();
}

void
CFixSession::AcquireSingleFix()
{
    NTSTATUS status = STATUS_SUCCESS;

    while (true)
    {
        // Wait stop fix event for 1 second and then retrieve position
        DWORD WaitCode = WaitForSingleObject(_StopFixEvent, DEFAULT_FIX_INTERVAL_SECONDS * 1000);
        if (WaitCode == WAIT_TIMEOUT)
        {
            status = RetrievePosition(&_GnssPosition);
            if (!NT_SUCCESS(status))
            {
                TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Fail to retrieve fix data from position source");
            }

            // Send the default fix to the session
            status = ReportGnssFix(&_GnssPosition);
            if (!NT_SUCCESS(status))
            {
                // We were not able to send the fix so cache it
                _HasCachedFix = true;
            }
            else
            {
                if (_GnssPosition.IsFinalFix != FALSE)
                {
                    break;
                }
            }
        }
        else if (WaitCode == WAIT_OBJECT_0)
        {
            // Stop session signalled
            break;
        }
    }

    EnterCriticalSection(&_Lock);

    // Reset the StopFix Event if it is set
    ResetEvent(_StopFixEvent);

    // Update the state of the session
    _State = STOPPED;

    LeaveCriticalSection(&_Lock);
}


NTSTATUS
CFixSession::RetrievePosition(
    _Inout_ PGNSS_FIXDATA GnssFixData
)
{
    NTSTATUS status = STATUS_SUCCESS;

    //
    // FIX ME:
    // You can retrieve a position from a predefined position or GPS HW.
    // This sample code calls RetrievePositionFromPredefinedValue to give fake location.
    // To give a real location from GPS HW, replace this function with new function that defines
    // (1) establishing and enumerating HW interface such as Serial or Bluetooth, and
    // (2) reading and parsing NMEA messages from the interface.
    // You can also define a custom IOCTL instead, updating _GnssPosition directly in CQueue and skip the call below as no-op.
    //
    status = RetrievePositionFromPredefinedValue(GnssFixData);

    return status;
}

NTSTATUS
CFixSession::RetrievePositionFromPredefinedValue(
    _Out_ PGNSS_FIXDATA GnssFixData
)
{
    NTSTATUS status = STATUS_SUCCESS;

    //
    // FIX ME:
    // Specify user-defined GNSS_FIXDATA output based off the DDI format. The following is just an example.
    //
    *GnssFixData =
    {
        sizeof(GNSS_FIXDATA),
        GNSS_DRIVER_DDK_VERSION,
        INVALID_SESSION_ID,
        { 0xCD700000, 0x1D48908 }, // an example date/time, Dec/01/2018
        TRUE,
        STATUS_SUCCESS,
        GNSS_FIXDETAIL_BASIC | GNSS_FIXDETAIL_ACCURACY,
        {
            sizeof(GNSS_FIXDATA_BASIC),
            GNSS_DRIVER_DDK_VERSION,
            // Location: Mount Rainier National Park
            46.852273,
            -121.757468,
            0.0,
            0.0,
            0.0
        },
        {
            sizeof(GNSS_FIXDATA_ACCURACY),
            GNSS_DRIVER_DDK_VERSION,
            10 // Meters accuracy
        }
    };
    return status;
}


<|start_filename|>audio/tests/HLK/ThirdPartyApoTests/CAPXLoggingFrameworkTests.cpp<|end_filename|>
#include "CAPXLoggingFrameworkTests.h"


bool CAPOLoggingFrameworkTest::setUpMethod()
{
    SLGetWindowsInformationDWORD(POLICY_KERNEL_ONECORE_DEVICE_FAMILY_ID, &m_dwPlatform);

    EndpointsWithCapx(m_capxEndpoints);

    return SetupSkipRTHeap();
}

bool CAPOLoggingFrameworkTest::tearDownMethod()
{
    return CleanupSkipRTHeap();
}

using malloca_deleter = wil::function_deleter<decltype(&::_freea), ::_freea>;
template <typename T>
using unique_malloca_ptr = wistd::unique_ptr<T, malloca_deleter>;
enum QueryState
{
    None = 0,
    ApoLog = 1,
    PumpYield = 2
};

class CEventParser : public IEtwEventHandler
{
public:
    int GetCountOfAPOLogging() { return m_countOfAudioProcessingNotifications; }
    bool WasRTTThreadDetected() { return m_bRTThreadDetected; }
    DWORD GetCountOfCreateFiles() { return m_dCreateFilesCalled; }

private:

    int m_countOfAudioProcessingNotifications = 0;
    DWORD m_dLastPid = 0;
    DWORD m_dLastThreadId = 0;
    DWORD m_dCreateFilesCalled = 0;
    bool m_bRTThreadDetected = false;
    QueryState m_currentState = QueryState::None;

    std::wstring QueryProcessName(const DWORD pid)
    {
        wil::unique_handle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
        if(NULL == hProcess)
        {
            return L"err " + std::to_wstring(GetLastError());
        }
        else
        {
            WCHAR szProcessName[1024];
            DWORD cchSize = ARRAYSIZE(szProcessName);
            if (0 == QueryFullProcessImageNameW(hProcess.get(), 0, szProcessName, &cchSize))
            {
                return L"err " + std::to_wstring(GetLastError());
            }

            PWSTR fileName = szProcessName + cchSize;
            while (fileName != szProcessName && *fileName != L'\\') fileName--;
            
            return (*fileName == L'\\') ? fileName + 1 : fileName;
        }
    }

    std::wstring QueryTraceLevel(PEVENT_RECORD pEventRecord)
    {
        UNREFERENCED_PARAMETER(pEventRecord);
        return L"err";
    }

    void OnEvent(PEVENT_RECORD pEventRecord)
    {
        WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
        [&]() {
            ULONG bufferSize = 0;
            if (TdhGetEventInformation(pEventRecord, 0, nullptr, nullptr, &bufferSize) == ERROR_INSUFFICIENT_BUFFER)
            {
                unique_malloca_ptr<TRACE_EVENT_INFO> eventInfo(reinterpret_cast<PTRACE_EVENT_INFO>(_malloca(bufferSize)));
                VERIFY_IS_NOT_NULL(eventInfo);
                RETURN_IF_WIN32_ERROR(TdhGetEventInformation(pEventRecord, 0, nullptr, eventInfo.get(), &bufferSize));

                // ApoLog is the name that the Logging framework uses for each of the logs sent through by the APO
                if (_wcsicmp(L"ApoLog", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    m_countOfAudioProcessingNotifications++;
                    if (m_currentState == QueryState::PumpYield)
                    {
                        if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                            m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                        {
                            m_bRTThreadDetected = true;
                        }
                    }

                    m_dLastPid = pEventRecord->EventHeader.ProcessId;
                    m_dLastThreadId = pEventRecord->EventHeader.ThreadId;
                    m_currentState = QueryState::ApoLog;
                }
                if (_wcsicmp(L"PumpYield", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE *>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    if (m_currentState == QueryState::ApoLog)
                    {
                        if (m_dLastPid == pEventRecord->EventHeader.ProcessId &&
                            m_dLastThreadId == pEventRecord->EventHeader.ThreadId)
                        {
                            m_bRTThreadDetected = true;
                        }
                    }

                    m_dLastPid = pEventRecord->EventHeader.ProcessId;
                    m_dLastThreadId = pEventRecord->EventHeader.ThreadId;
                    m_currentState = QueryState::PumpYield;
                }
                if (_wcsicmp(L"CreateFile", reinterpret_cast<PWSTR>(reinterpret_cast<BYTE*>(eventInfo.get()) + eventInfo->EventNameOffset)) == 0)
                {
                    m_dCreateFilesCalled++;
                }
            }
            return S_OK;
        }();
    }
};

// {6e7b1892-5288-5fe5-8f34-e3b0dc671fd2}
static const GUID AudioSesTelemetryProvider =
{ 0x6e7b1892, 0x5288, 0x5fe5, 0x8f, 0x34, 0xe3, 0xb0, 0xdc, 0x67, 0x1f, 0xd2 };

void CAPOLoggingFrameworkTest::LoggingFrameworkTest()
{
    WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);

    if (m_dwPlatform != DEVICEFAMILYINFOENUM_WINDOWS_CORE && m_capxEndpoints.size() == 0)
    {
        Log::Result(TestResults::Skipped, L"There are no CAPX APOs to test");
        return;
    }

    CEtwListener listener;
    CEventParser parser;

    VERIFY_SUCCEEDED(listener.StartTraceSession(L"LoggingFrameworkTest", static_cast<IEtwEventHandler *>(&parser)));

    // Enable Microsoft.Windows.Audio.Client
    VERIFY_SUCCEEDED(listener.EnableProvider(AudioSesTelemetryProvider, TRACE_LEVEL_INFORMATION, 8));

    auto stopListener = scope_exit([&] {
        listener.StopTraceSession();
    });

    wil::com_ptr_nothrow<IMMDeviceEnumerator> spEnumerator;
    VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&spEnumerator)));

    RunStressTest(FIVE_MIN_IN_SEC, m_capxEndpoints);

    VERIFY_IS_FALSE(parser.WasRTTThreadDetected());
    VERIFY_IS_TRUE(parser.GetCountOfCreateFiles() == 0);
    LOG_OUTPUT(L"Number of APO logs collected: %d", parser.GetCountOfAPOLogging());
}

<|start_filename|>general/ioctl/kmdf/exe/testapp.c<|end_filename|>
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.


Module Name:

    testapp.c

Abstract:

    Purpose of this app to test the NONPNP sample driver. The app
    makes four different ioctl calls to test all the buffer types, write
    some random buffer content to a file created by the driver in \SystemRoot\Temp
    directory, and reads the same file and matches the content.
    If -l option is specified, it does the write and read operation in a loop
    until the app is terminated by pressing ^C.

    Make sure you have the \SystemRoot\Temp directory exists before you run the test.

Environment:

    Win32 console application.

--*/

        
#include <DriverSpecs.h>
_Analysis_mode_(_Analysis_code_type_user_code_)  

#include <windows.h>

#pragma warning(disable:4201)  // nameless struct/union
#include <winioctl.h>
#pragma warning(default:4201)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <strsafe.h>
#include "public.h"


BOOLEAN
ManageDriver(
    IN LPCTSTR  DriverName,
    IN LPCTSTR  ServiceName,
    IN USHORT   Function
    );

HMODULE
LoadWdfCoInstaller(
    VOID
    );

VOID
UnloadWdfCoInstaller(
    HMODULE Library
    );

BOOLEAN
SetupDriverName(
    _Inout_updates_all_(BufferLength) PCHAR DriverLocation,
    _In_ ULONG BufferLength
    );

BOOLEAN
DoFileReadWrite(
    HANDLE HDevice
    );

VOID
DoIoctls(
    HANDLE hDevice
    );

// for example, WDF 1.9 is "01009". the size 6 includes the ending NULL marker
//
#define MAX_VERSION_SIZE 6

CHAR G_coInstallerVersion[MAX_VERSION_SIZE] = {0};
BOOLEAN  G_fLoop = FALSE;
BOOL G_versionSpecified = FALSE;



//-----------------------------------------------------------------------------
// 4127 -- Conditional Expression is Constant warning
//-----------------------------------------------------------------------------
#define WHILE(constant) \
__pragma(warning(disable: 4127)) while(constant); __pragma(warning(default: 4127))


#define USAGE  \
"Usage: nonpnpapp <-V version> <-l> \n" \
       " -V version  {if no version is specified the version specified in the build environment will be used.}\n" \
       "    The version is the version of the KMDF coinstaller to use \n"  \
       "    The format of version  is MMmmm where MM -- major #, mmm - serial# \n" \
       " -l  { option to continuously read & write to the file} \n"

BOOL
ValidateCoinstallerVersion(
    _In_ PSTR Version
    )
{   BOOL ok = FALSE;
    INT i;

    for(i= 0; i<MAX_VERSION_SIZE ;i++){
        if( ! IsCharAlphaNumericA(Version[i])) {
            break;
        }
    }
    if (i == (MAX_VERSION_SIZE -sizeof(CHAR))) {
        ok = TRUE;
    }
    return ok;
}

LONG
Parse(
    _In_ int argc,
    _In_reads_(argc) char *argv[]
    )
/*++
Routine Description:

    Called by main() to parse command line parms

Arguments:

    argc and argv that was passed to main()

Return Value:

    Sets global flags as per user function request

--*/
{
    int i;
    BOOL ok;
    LONG error = ERROR_SUCCESS;

    for (i=0; i<argc; i++) {
        if (argv[i][0] == '-' ||
            argv[i][0] == '/') {
            switch(argv[i][1]) {
            case 'V':
            case 'v':
                if (( (i+1 < argc ) &&
                      ( argv[i+1][0] != '-' && argv[i+1][0] != '/'))) {
                    //
                    // use version in commandline
                    //
                    i++;
                    ok = ValidateCoinstallerVersion(argv[i]);
                    if (!ok) {
                        printf("Not a valid format for coinstaller version\n"
                               "It should be characters between A-Z, a-z , 0-9\n"
                               "The version format is MMmmm where MM -- major #, mmm - serial#");
                        error = ERROR_INVALID_PARAMETER;
                        break;
                    }
                    if (FAILED( StringCchCopy(G_coInstallerVersion,
                                              MAX_VERSION_SIZE,
                                              argv[i]) )) {
                        break;
                    }
                    G_versionSpecified = TRUE;

                }
                else{
                    printf(USAGE);
                    error = ERROR_INVALID_PARAMETER;
                }
                break;
            case 'l':
            case 'L':
                G_fLoop = TRUE;
                break;
            default:
                printf(USAGE);
                error = ERROR_INVALID_PARAMETER;

            }
        }
    }
    return error;
}

PCHAR
GetCoinstallerVersion(
    VOID
    )
{
    if (!G_versionSpecified &&
        FAILED( StringCchPrintf(G_coInstallerVersion,
                                MAX_VERSION_SIZE,
                                "%02d%03d",    // for example, "01009"
                                KMDF_VERSION_MAJOR,
                                KMDF_VERSION_MINOR)))
    {
        printf("StringCchCopy failed with error \n");
    }

    return (PCHAR)&G_coInstallerVersion;
}

VOID __cdecl
main(
    _In_ ULONG argc,
    _In_reads_(argc) PCHAR argv[]
    )
{
    HANDLE   hDevice;
    DWORD    errNum = 0;
    CHAR     driverLocation [MAX_PATH];
    BOOL     ok;
    HMODULE  library = NULL;
    LONG     error;
    PCHAR    coinstallerVersion;

    //
    // Parse command line args
    //   -l     -- loop option
    //
    if ( argc > 1 ) {// give usage if invoked with no parms
        error = Parse(argc, argv);
        if (error != ERROR_SUCCESS) {
            return;
        }
    }

    if (!G_versionSpecified ) {
        coinstallerVersion = GetCoinstallerVersion();

        //
        // if no version is specified or an invalid one is specified use default version
        //
        printf("No version specified. Using default version:%s\n",
               coinstallerVersion);

    } else {
        coinstallerVersion = (PCHAR)&G_coInstallerVersion;
    }

    //
    // open the device
    //
    hDevice = CreateFile(DEVICE_NAME,
                         GENERIC_READ | GENERIC_WRITE,
                         0,
                         NULL,
                         CREATE_ALWAYS,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL);

    if(hDevice == INVALID_HANDLE_VALUE) {

        errNum = GetLastError();

        if (!(errNum == ERROR_FILE_NOT_FOUND ||
                errNum == ERROR_PATH_NOT_FOUND)) {

            printf("CreateFile failed!  ERROR_FILE_NOT_FOUND = %d\n",
                   errNum);
            return ;
        }

        //
        // Load WdfCoInstaller.dll.
        //
        library = LoadWdfCoInstaller();

        if (library == NULL) {
            printf("The WdfCoInstaller%s.dll library needs to be "
                   "in same directory as nonpnpapp.exe\n", coinstallerVersion);
            return;
        }

        //
        // The driver is not started yet so let us install the driver.
        // First setup full path to driver name.
        //
        ok = SetupDriverName( driverLocation, MAX_PATH );

        if (!ok) {
            return ;
        }

        ok = ManageDriver( DRIVER_NAME,
                           driverLocation,
                           DRIVER_FUNC_INSTALL );

        if (!ok) {

            printf("Unable to install driver. \n");

            //
            // Error - remove driver.
            //
            ManageDriver( DRIVER_NAME,
                          driverLocation,
                          DRIVER_FUNC_REMOVE );
            return;
        }

        hDevice = CreateFile( DEVICE_NAME,
                              GENERIC_READ | GENERIC_WRITE,
                              0,
                              NULL,
                              CREATE_ALWAYS,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL );

        if (hDevice == INVALID_HANDLE_VALUE) {
            printf ( "Error: CreatFile Failed : %d\n", GetLastError());
            return;
        }
    }

    DoIoctls(hDevice);

    do {

        if(!DoFileReadWrite(hDevice)) {
            break;
        }

        if(!G_fLoop) {
            break;
        }
        Sleep(1000); // sleep for 1 sec.

    } WHILE (TRUE);

    //
    // Close the handle to the device before unloading the driver.
    //
    CloseHandle ( hDevice );

    //
    // Unload the driver.  Ignore any errors.
    //
    ManageDriver( DRIVER_NAME,
                  driverLocation,
                  DRIVER_FUNC_REMOVE );

    //
    // Unload WdfCoInstaller.dll
    //
    if ( library ) {
        UnloadWdfCoInstaller( library );
    }
    return;
}


VOID
DoIoctls(
    HANDLE hDevice
    )
{
    char OutputBuffer[100];
    char InputBuffer[200];
    BOOL bRc;
    ULONG bytesReturned;

    //
    // Printing Input & Output buffer pointers and size
    //

    printf("InputBuffer Pointer = %p, BufLength = %Id\n", InputBuffer,
                        sizeof(InputBuffer));
    printf("OutputBuffer Pointer = %p BufLength = %Id\n", OutputBuffer,
                                sizeof(OutputBuffer));
    //
    // Performing METHOD_BUFFERED
    //

    if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer),
        "this String is from User Application; using METHOD_BUFFERED"))){
        return;
    }

    printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");

    memset(OutputBuffer, 0, sizeof(OutputBuffer));

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_NONPNP_METHOD_BUFFERED,
                            InputBuffer,
                            (DWORD) strlen( InputBuffer )+1,
                            OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d", GetLastError());
        return;

    }
    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);


    //
    // Performing METHOD_NEITHER
    //

    printf("\nCalling DeviceIoControl METHOD_NEITHER\n");

    if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer),
               "this String is from User Application; using METHOD_NEITHER"))) {
        return;
    }

    memset(OutputBuffer, 0, sizeof(OutputBuffer));

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_NONPNP_METHOD_NEITHER,
                            InputBuffer,
                            (DWORD) strlen( InputBuffer )+1,
                            OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : %d\n", GetLastError());
        return;

    }

    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);

    //
    // Performing METHOD_IN_DIRECT
    //

    printf("\nCalling DeviceIoControl METHOD_IN_DIRECT\n");

    if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer),
               "this String is from User Application; using METHOD_IN_DIRECT"))) {
        return;
    }

    if(FAILED(StringCchCopy(OutputBuffer, sizeof(OutputBuffer),
               "This String is from User Application in OutBuffer; using METHOD_IN_DIRECT"))) {
        return;
    }

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_NONPNP_METHOD_IN_DIRECT,
                            InputBuffer,
                            (DWORD) strlen( InputBuffer )+1,
                            OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : : %d", GetLastError());
        return;
    }

    printf("    Number of bytes transfered from OutBuffer: %d\n",
                                    bytesReturned);

    //
    // Performing METHOD_OUT_DIRECT
    //

    printf("\nCalling DeviceIoControl METHOD_OUT_DIRECT\n");
    if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer),
               "this String is from User Application; using METHOD_OUT_DIRECT"))){
        return;
    }

    memset(OutputBuffer, 0, sizeof(OutputBuffer));

    bRc = DeviceIoControl ( hDevice,
                            (DWORD) IOCTL_NONPNP_METHOD_OUT_DIRECT,
                            InputBuffer,
                            (DWORD) strlen( InputBuffer )+1,
                            OutputBuffer,
                            sizeof( OutputBuffer),
                            &bytesReturned,
                            NULL
                            );

    if ( !bRc )
    {
        printf ( "Error in DeviceIoControl : : %d", GetLastError());
        return;
    }

    printf("    OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);

    return;

}


BOOLEAN
DoFileReadWrite(
    HANDLE HDevice
    )
{
    ULONG bufLength, index;
    PUCHAR readBuf = NULL;
    PUCHAR writeBuf = NULL;
    BOOLEAN ret;
    ULONG   bytesWritten, bytesRead;

    //
    // Seed the random-number generator with current time so that
    // the numbers will be different every time we run.
    //
    srand( (unsigned)time( NULL ) );

    //
    // rand function returns a pseudorandom integer in the range 0 to RAND_MAX
    // (0x7fff)
    //
    bufLength = rand();
    //
    // Try until the bufLength is not zero.
    //
    while(bufLength == 0) {
        bufLength = rand();
    }

    //
    // Allocate a buffer of that size to use for write operation.
    //
    writeBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufLength);
    if(!writeBuf) {
        ret = FALSE;
        goto End;
    }
    //
    // Fill the buffer with randon number less than UCHAR_MAX.
    //
    index = bufLength;
    while(index){
        writeBuf[index-1] = (UCHAR) rand() % UCHAR_MAX;
        index--;
    }

    printf("Write %d bytes to file\n", bufLength);

    //
    // Tell the driver to write the buffer content to the file from the
    // begining of the file.
    //

    if (!WriteFile(HDevice,
                  writeBuf,
                  bufLength,
                  &bytesWritten,
                  NULL)) {

        printf("ReadFile failed with error 0x%x\n", GetLastError());

        ret = FALSE;
        goto End;

    }

    //
    // Allocate another buffer of same size.
    //
    readBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufLength);
    if(!readBuf) {

        ret = FALSE;
        goto End;
    }

    printf("Read %d bytes from the same file\n", bufLength);

    //
    // Tell the driver to read the file from the begining.
    //
    if (!ReadFile(HDevice,
                  readBuf,
                  bufLength,
                  &bytesRead,
                  NULL)) {

        printf("Error: ReadFile failed with error 0x%x\n", GetLastError());

        ret = FALSE;
        goto End;

    }

    //
    // Now compare the readBuf and writeBuf content. They should be the same.
    //

    if(bytesRead != bytesWritten) {
        printf("bytesRead(%d) != bytesWritten(%d)\n", bytesRead, bytesWritten);
        ret = FALSE;
        goto End;
    }

    if(memcmp(readBuf, writeBuf, bufLength) != 0){
        printf("Error: ReadBuf and WriteBuf contents are not the same\n");
        ret = FALSE;
        goto End;
    }

    ret = TRUE;

End:

    if(readBuf){
        HeapFree (GetProcessHeap(), 0, readBuf);
    }

    if(writeBuf){
        HeapFree (GetProcessHeap(), 0, writeBuf);
    }

    return ret;


}


