/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxDeviceInterfaceUM.cpp

Abstract:

    This module implements the device interface object.

Author:



Environment:

    User mode only

Revision History:

--*/

#include "FxSupportPch.hpp"

extern "C" {
#include "FxDeviceInterfaceUM.tmh"
}

FxDeviceInterface::FxDeviceInterface(
    )
/*++

Routine Description:
    Constructor for the object.  Initializes all fields

Arguments:
    None

Return Value:
    None

  --*/
{
    RtlZeroMemory(&m_InterfaceClassGUID, sizeof(m_InterfaceClassGUID));

    RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));
    RtlZeroMemory(&m_ReferenceString, sizeof(m_ReferenceString));

    m_Entry.Next = NULL;

    m_State = FALSE;
}

FxDeviceInterface::~FxDeviceInterface()
/*++

Routine Description:
    Destructor for FxDeviceInterface.  Cleans up any allocations previously
    allocated.

Arguments:
    None

Return Value:
    None

  --*/
{
    // the device interface should be off now
    ASSERT(m_State == FALSE);

    // should no longer be in any list
    ASSERT(m_Entry.Next == NULL);

    if (m_ReferenceString.Buffer != NULL) {
        FxPoolFree(m_ReferenceString.Buffer);
        RtlZeroMemory(&m_ReferenceString, sizeof(m_ReferenceString));
    }

    if (m_SymbolicLinkName.Buffer != NULL) {
        MxMemory::MxFreePool(m_SymbolicLinkName.Buffer);
    }
}

_Must_inspect_result_
NTSTATUS
FxDeviceInterface::Initialize(
    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
    __in CONST GUID* InterfaceGUID,
    __in_opt PCUNICODE_STRING ReferenceString
    )
/*++

Routine Description:
    Initializes the object with the interface GUID and optional reference string

Arguments:
    InterfaceGUID - GUID describing the interface

    ReferenceString - string used to differentiate between 2 interfaces on the
        same PDO

Return Value:
    STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES

  --*/
{
    RtlCopyMemory(&m_InterfaceClassGUID, InterfaceGUID, sizeof(GUID));

    if (ReferenceString != NULL) {
        return FxDuplicateUnicodeString(FxDriverGlobals,
                                        ReferenceString,
                                        &m_ReferenceString);
    }
    else {
        return STATUS_SUCCESS;
    }
}


VOID
FxDeviceInterface::SetState(
    __in BOOLEAN State
    )
/*++

Routine Description:
    Sets the state of the device interface

Arguments:
    State - the state to set


Return Value:
    None.

  --*/
{
    HRESULT hr;
    NTSTATUS status;
    IWudfDeviceStack *pDeviceStack;




    //
    // Get the IWudfDeviceStack interface
    //
    pDeviceStack = m_Device->GetDeviceStackInterface();

    //
    // Enable the interface
    //
    hr = pDeviceStack->SetDeviceInterfaceState(&this->m_InterfaceClassGUID,
                                               this->m_ReferenceString.Buffer,
                                               State);

    if (SUCCEEDED(hr)) {
        m_State = State;
    }
    else {
        status = FxDevice::NtStatusFromHr(pDeviceStack, hr);
        DoTraceLevelMessage(
            FxDevice::GetFxDevice(m_Device)->GetDriverGlobals(),
            TRACE_LEVEL_WARNING, TRACINGPNP,
            "Failed to %s device interface %!STATUS!",
            (State ? "enable" : "disable"), status);





    }
}

_Must_inspect_result_
NTSTATUS
FxDeviceInterface::Register(
    __in MdDeviceObject DeviceObject
    )
/*++

Routine Description:
    Registers the device interface for a given PDO

Arguments:
    DeviceObject - FDO for the device stack in case of UM, and PDO for
                   in case of KM.

Return Value:
    returned by IWudfDeviceStack::CreateDeviceInterface

  --*/
{
    HRESULT hr;
    NTSTATUS status;
    IWudfDeviceStack *pDeviceStack;

    m_Device = DeviceObject;

    //
    // Get the IWudfDeviceStack interface
    //
    pDeviceStack = m_Device->GetDeviceStackInterface();

    hr = pDeviceStack->CreateDeviceInterface(&m_InterfaceClassGUID,
                                             m_ReferenceString.Buffer);

    if (SUCCEEDED(hr)) {
        status = STATUS_SUCCESS;
    }
    else {
        status = FxDevice::NtStatusFromHr(pDeviceStack, hr);
    }

    return status;
}

_Must_inspect_result_
NTSTATUS
FxDeviceInterface::Register(
    _In_ FxDevice* Device
    )
{
    NTSTATUS status;

    //
    // For UMDF, PDO is already known so no reason to defer registration.
    // Also, note that Register takes fdo as parameter for UMDF.
    //
    status = Register(Device->GetDeviceObject());

    return status;
}

NTSTATUS
FxDeviceInterface::GetSymbolicLinkName(
    _In_ FxString* LinkString
    )
{
    NTSTATUS status;
    PCWSTR symLink = NULL;

    if (m_SymbolicLinkName.Buffer == NULL) {
        IWudfDeviceStack *pDeviceStack;
        IWudfDeviceStack2 *pDeviceStack2;

        //
        // Get the IWudfDeviceStack interface
        //
        pDeviceStack = m_Device->GetDeviceStackInterface();
        HRESULT hrQI;
        HRESULT hr;

        hrQI = pDeviceStack->QueryInterface(IID_IWudfDeviceStack2,
                                            (PVOID*)&pDeviceStack2);
        FX_VERIFY(INTERNAL, CHECK_QI(hrQI, pDeviceStack2));
        pDeviceStack->Release();

        //
        // Get the symbolic link
        //
        hr = pDeviceStack2->GetInterfaceSymbolicLink(&m_InterfaceClassGUID,
                                                     m_ReferenceString.Buffer,
                                                     &symLink);
        if (FAILED(hr)) {
            status = FxDevice::GetFxDevice(m_Device)->NtStatusFromHr(hr);
        }
        else {
            RtlInitUnicodeString(&m_SymbolicLinkName, symLink);
            status = STATUS_SUCCESS;
        }
    }
    else {
        status = STATUS_SUCCESS;
    }

    if (NT_SUCCESS(status)) {
        //
        // Attempt a copy
        //
        status = LinkString->Assign(&m_SymbolicLinkName);
    }

    return status;
}