/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxDeviceInterfaceAPI.cpp

Abstract:

    This module implements the device interface object external APIs

Author:




Environment:

    kernel and user mode

Revision History:

--*/

#include "fxsupportpch.hpp"

extern "C" {
// #include "FxDeviceInterfaceAPI.tmh"
}

//
// extern "C" the entire file
//
extern "C" {

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
STDCALL
WDFEXPORT(WdfDeviceCreateDeviceInterface)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in
    CONST GUID *InterfaceClassGUID,
    __in_opt
    PCUNICODE_STRING ReferenceString
    )
/*++

Routine Description:
    Creates a device interface associated with the passed in device object

Arguments:
    Device - Handle which represents the device exposing the interface

    InterfaceGUID - GUID describing the interface being exposed

    ReferenceString - OPTIONAL string which allows the driver writer to
        distinguish between different exposed interfaces

Return Value:
    STATUS_SUCCESS or appropriate NTSTATUS code

  --*/
{
    DDI_ENTRY();

    SINGLE_LIST_ENTRY **ppPrev, *pCur;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxDeviceInterface *pDeviceInterface;
    FxDevice *pDevice;
    FxPkgPnp* pPkgPnp;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE,
                                   (PVOID*) &pDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, InterfaceClassGUID);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    if (ReferenceString != NULL) {
        status = FxValidateUnicodeString(pFxDriverGlobals, ReferenceString);
        if (!NT_SUCCESS(status)) {
            return status;
        }
    }

    if (pDevice->IsLegacy()) {
        status = STATUS_INVALID_DEVICE_REQUEST;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "WDFDEVICE %p is not a PNP device, device interface creation not "
            "allowed %!STATUS!", Device, status);

        return status;
    }

    pDeviceInterface = new(pFxDriverGlobals, PagedPool) FxDeviceInterface();

    if (pDeviceInterface == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "WDFDEVICE %p DeviceInterface object creation failed, %!STATUS!",
            Device, status);

        return status;
    }

    status = pDeviceInterface->Initialize(pFxDriverGlobals,
                                          InterfaceClassGUID,
                                          ReferenceString);

    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "WDFDEVICE %p, DeviceInterface object initialization failed, %!STATUS!",
            Device, status );

        goto Done;
    }

    pPkgPnp = pDevice->m_PkgPnp;

    pPkgPnp->m_DeviceInterfaceLock.AcquireLock(pFxDriverGlobals);

    status = pDeviceInterface->Register(pDevice);

    if (NT_SUCCESS(status)) {
        //
        // Insert into the end of the list
        //
        ppPrev = &pPkgPnp->m_DeviceInterfaceHead.Next;
        pCur = pPkgPnp->m_DeviceInterfaceHead.Next;
        while (pCur != NULL) {
            ppPrev = &pCur->Next;
            pCur = pCur->Next;
        }

        *ppPrev = &pDeviceInterface->m_Entry;
    }

    pPkgPnp->m_DeviceInterfaceLock.ReleaseLock(pFxDriverGlobals);

Done:
    if (!NT_SUCCESS(status)) {
        delete pDeviceInterface;
        pDeviceInterface = NULL;
    }

    return status;
}

__drv_maxIRQL(PASSIVE_LEVEL)
VOID
STDCALL
WDFEXPORT(WdfDeviceSetDeviceInterfaceState)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in
    CONST GUID *InterfaceClassGUID,
    __in_opt
    PCUNICODE_STRING RefString,
    __in
    BOOLEAN State
    )
{
    DDI_ENTRY();

    PSINGLE_LIST_ENTRY ple;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    NTSTATUS status;
    FxDevice* pDevice;
    FxPkgPnp* pPkgPnp;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE,
                                   (PVOID*) &pDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, InterfaceClassGUID);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        FxVerifierDbgBreakPoint(pFxDriverGlobals);
        return;
    }

    if (RefString != NULL) {
        status = FxValidateUnicodeString(pFxDriverGlobals, RefString);
        if (!NT_SUCCESS(status)) {
            FxVerifierDbgBreakPoint(pFxDriverGlobals);
            return;
        }
    }

    if (pDevice->IsLegacy()) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "WDFDEVICE %p is not a PNP device, device interfaces not allowed",
            Device);
        FxVerifierDbgBreakPoint(pFxDriverGlobals);
        return;
    }

    pPkgPnp = pDevice->m_PkgPnp;

    pPkgPnp->m_DeviceInterfaceLock.AcquireLock(pFxDriverGlobals);

    //
    // Iterate over the interfaces and see if we have a match
    //
    for (ple = pPkgPnp->m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
        FxDeviceInterface *pDI;

        pDI = FxDeviceInterface::_FromEntry(ple);

        if (FxIsEqualGuid(&pDI->m_InterfaceClassGUID, InterfaceClassGUID)) {
            if (RefString != NULL) {
                if ((RefString->Length == pDI->m_ReferenceString.Length)
                    &&
                    (RtlCompareMemory(RefString->Buffer,
                                      pDI->m_ReferenceString.Buffer,
                                      RefString->Length) == RefString->Length)) {
                    //
                    // They match, carry on
                    //
                    DO_NOTHING();
                }
                else {
                    //
                    // The ref strings do not match, continue on in the search
                    // of the collection.
                    //
                    continue;
                }
            }
            else if (pDI->m_ReferenceString.Length > 0) {
                //
                // Caller didn't specify a ref string but this interface has
                // one, continue on in the search through the collection.
                //
                continue;
            }

            //
            // Set the state and break out of the loop because we found our
            // interface.
            //
            pDI->SetState(State);
            break;
        }
    }

    pPkgPnp->m_DeviceInterfaceLock.ReleaseLock(pFxDriverGlobals);
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
STDCALL
WDFEXPORT(WdfDeviceRetrieveDeviceInterfaceString)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in
    CONST GUID* InterfaceClassGUID,
    __in_opt
    PCUNICODE_STRING RefString,
    __in
    WDFSTRING String
    )
/*++

Routine Description:
    Returns the symbolic link value of the registered device interface.

Arguments:


Return Value:


  --*/

{
    DDI_ENTRY();

    PSINGLE_LIST_ENTRY ple;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxDevice* pDevice;
    FxPkgPnp* pPkgPnp;
    FxString* pString;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE,
                                   (PVOID*) &pDevice,
                                   &pFxDriverGlobals );

    FxPointerNotNull(pFxDriverGlobals, InterfaceClassGUID);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    if (RefString != NULL) {
        status = FxValidateUnicodeString(pFxDriverGlobals, RefString);
        if (!NT_SUCCESS(status)) {
            return status;
        }
    }

    if (pDevice->IsLegacy()) {
        status = STATUS_INVALID_DEVICE_REQUEST;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
            "WDFDEVICE %p is not a PNP device, device interface creation not "
            "allowed %!STATUS!", Device, status);

        return status;
    }

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         String,
                         FX_TYPE_STRING,
                         (PVOID*) &pString);

    pPkgPnp = pDevice->m_PkgPnp;

    status = STATUS_OBJECT_NAME_NOT_FOUND;

    pPkgPnp->m_DeviceInterfaceLock.AcquireLock(pFxDriverGlobals);

    //
    // Iterate over the interfaces and see if we have a match
    //
    for (ple = pPkgPnp->m_DeviceInterfaceHead.Next;
         ple != NULL;
         ple = ple->Next) {
        FxDeviceInterface *pDI;

        pDI = FxDeviceInterface::_FromEntry(ple);

        if (FxIsEqualGuid(&pDI->m_InterfaceClassGUID, InterfaceClassGUID)) {
            if (RefString != NULL) {
                if ((RefString->Length == pDI->m_ReferenceString.Length)
                    &&
                    (RtlCompareMemory(RefString->Buffer,
                                      pDI->m_ReferenceString.Buffer,
                                      RefString->Length) == RefString->Length)) {
                    //
                    // They match, carry on
                    //
                    DO_NOTHING();
                }
                else {
                    //
                    // The ref strings do not match, continue on in the search
                    // of the collection.
                    //
                    continue;
                }
            }
            else if (pDI->m_ReferenceString.Length > 0) {
                //
                // Caller didn't specify a ref string but this interface has
                // one, continue on in the search through the collection.
                //
                continue;
            }

            status = pDI->GetSymbolicLinkName(pString);

            break;
        }
    }

    pPkgPnp->m_DeviceInterfaceLock.ReleaseLock(pFxDriverGlobals);

    return status;
}

} // extern "C"