NTSTATUS
NfcCxLLCPInterfaceConfigure(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
)
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;
    PNFCCX_LLCP_INTERFACE LLCPInterface = NfcCxRFInterfaceGetLLCPInterface(RFInterface);
    uint8_t ServiceName[] = "NFC Application";
    static NFCCX_LLCP_LIBNFC_REQUEST_CONTEXT LibNfcContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    LLCPInterface->eLinkStatus = phFriNfc_LlcpMac_eLinkDefault;
    LLCPInterface->sServiceName.buffer = ServiceName;
    LLCPInterface->sServiceName.length = ARRAYSIZE(ServiceName) - 1;
    LLCPInterface->sLocalLinkInfo.miu = LLCPInterface->sLlcpConfigParams.uMIU;
    LLCPInterface->sLocalLinkInfo.lto = LLCPInterface->sLlcpConfigParams.uLTO;
    LLCPInterface->sLocalLinkInfo.wks = PHFRINFC_LLCP_WKS_DEFAULT;
    LLCPInterface->sLocalLinkInfo.option = PHFRINFC_LLCP_OPTION_DEFAULT;

    LibNfcContext.LLCPInterface = LLCPInterface;
    LibNfcContext.Sequence = RFInterface->pSeqHandler;

    nfcStatus = phLibNfc_Mgt_SetLlcp_ConfigParams(&LLCPInterface->sLocalLinkInfo,
                NfcCxLLCPInterfaceConfigureCB,
                &LibNfcContext);

    Status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
NTSTATUS
NfcCxLLCPInterfaceCheck(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
)
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;
    PNFCCX_LLCP_INTERFACE LLCPInterface = NfcCxRFInterfaceGetLLCPInterface(RFInterface);

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    LLCPInterface->eRequestState = NFCCX_LLCP_REQUEST_PENDING;

    nfcStatus = phLibNfc_Llcp_CheckLlcp(RFInterface->pLibNfcContext->pRemDevList[0].hTargetDev,
                                        NfcCxLLCPInterfaceCheckCB,
                                        NfcCxLLCPInterfaceLinkStatusCB,
                                        (VOID *)LLCPInterface);

    if (LLCPInterface->eRequestState == NFCCX_LLCP_REQUEST_COMPLETE) {
        Status = STATUS_PENDING;
    }
    else {
        Status = NfcCxNtStatusFromNfcStatus(nfcStatus);
    }

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
NTSTATUS
NfcCxSNEPInterfaceClientPut(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
    )
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;
    PNFCCX_SNEP_INTERFACE snepInterface = NfcCxRFInterfaceGetSNEPInterface(RFInterface);
    static NFCCX_SNEP_LIBNFC_REQUEST_CONTEXT LibNfcContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    LibNfcContext.SNEPInterface = snepInterface;
    LibNfcContext.Sequence = RFInterface->pSeqHandler;

    nfcStatus = phLibNfc_SnepProtocolCliReqPut(snepInterface->pClientHandleDef,
                                               &snepInterface->sSendDataBuff,
                                               NfcCxSNEPInterfaceClientPutReqCB,
                                               &LibNfcContext);

    Status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
NTSTATUS
NfcCxSNEPInterfaceClientConnect(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
    )
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;
    PNFCCX_SNEP_INTERFACE snepInterface = NfcCxRFInterfaceGetSNEPInterface(RFInterface);
    static NFCCX_SNEP_LIBNFC_REQUEST_CONTEXT LibNfcContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    LibNfcContext.SNEPInterface = snepInterface;
    LibNfcContext.Sequence = RFInterface->pSeqHandler;

    nfcStatus = phLibNfc_SnepClient_Init(&snepInterface->sConfigInfo,
                                         RFInterface->pLibNfcContext->pRemDevList[0].hTargetDev,
                                         NfcCxSNEPInterfaceClientConnCB,
                                         &LibNfcContext);

    Status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
static VOID
NfcCxSNEPInterfaceClientConnCB(
    _In_ PVOID pContext, 
    _In_ NFCSTATUS NfcStatus, 
    _In_ phLibNfc_Handle ConnHandle
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_SNEP_INTERFACE snepInterface = ((PNFCCX_SNEP_LIBNFC_REQUEST_CONTEXT)pContext)->SNEPInterface;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    snepInterface->pClientHandleDef = ConnHandle;

    if (NULL != snepInterface->pClientHandleDef && NFCSTATUS_CONNECTION_SUCCESS == NfcStatus) {
        TRACE_LINE(LEVEL_INFO, "Connection Success %p", ConnHandle);
        NfcCxRFInterfaceP2pConnectionEstablished(NfcCxSNEPInterfaceGetRFInterface(snepInterface));
    }
    else {
        TRACE_LINE(LEVEL_ERROR, "Connection Failed, %!NFCSTATUS!", NfcStatus);
        status = STATUS_UNSUCCESSFUL;
    }

    NfcCxInternalSequence(NfcCxSNEPInterfaceGetRFInterface(snepInterface), ((PNFCCX_SNEP_LIBNFC_REQUEST_CONTEXT)pContext)->Sequence, status, NULL, NULL);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
}
NTSTATUS
NfcCxEvtSetLlcpConfig(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE Device,
    _In_ PCNFC_CX_LLCP_CONFIG Config
    )
/*++

Routine Description:

    This routine is called by the CX client to configure
    the RF discovery settings.

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize
    Config - Pointer to a structure containing the LLCP configuration

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_FDO_CONTEXT fdoContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    if (NULL == Config ||
        sizeof(*Config) != Config->Size) {
        TRACE_LINE(LEVEL_ERROR, "Invalid Client Driver Configuration");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    fdoContext = NfcCxFdoGetContext(Device);

    if (fdoContext->RFInterface == NULL) {
        TRACE_LINE(LEVEL_ERROR, "CX not initialized");
        status = STATUS_INVALID_DEVICE_STATE;
        goto Done;
    }

    status = NfcCxRFInterfaceSetLLCPConfig(fdoContext->RFInterface, Config);

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
NTSTATUS
NfcCxBindClient(
    PWDF_CLASS_BIND_INFO ClassInfo,
    PWDF_COMPONENT_GLOBALS ClientGlobals
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_CLIENT_GLOBALS pGlobals;

    static const PFN_NFC_CX exports[] =
    {
        (PFN_NFC_CX)NfcCxEvtDeviceInitConfig,
        (PFN_NFC_CX)NfcCxEvtDeviceInitialize,
        (PFN_NFC_CX)NfcCxEvtDeviceDeinitialize,
        (PFN_NFC_CX)NfcCxEvtHardwareEvent,
        (PFN_NFC_CX)NfcCxEvtNciReadNotification,
        (PFN_NFC_CX)NfcCxEvtSetRfDiscoveryConfig,
        (PFN_NFC_CX)NfcCxEvtSetLlcpConfig,
        (PFN_NFC_CX)NfcCxEvtRegisterSequenceHandler,
        (PFN_NFC_CX)NfcCxEvtUnregisterSequenceHandler,
    };

    UNREFERENCED_PARAMETER(ClientGlobals);

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);
    
    if (ClassInfo->FunctionTableCount != ARRAYSIZE(exports)) {
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    CopyMemory(ClassInfo->FunctionTable, &exports[0], sizeof(exports));

    pGlobals = (PNFCCX_CLIENT_GLOBALS) malloc(sizeof(NFCCX_CLIENT_GLOBALS));
    if (pGlobals == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Done;
    }

    RtlZeroMemory(pGlobals, sizeof(NFCCX_CLIENT_GLOBALS));
    pGlobals->Signature = GLOBALS_SIG;

    *((PNFCCX_DRIVER_GLOBALS*)ClassInfo->ClassBindInfo) = &pGlobals->Public;

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);

    return status;
}
NTSTATUS
NfcCxEvtDeviceDeinitialize(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This routine is called by the CX client to indicate
    that a device deinitialization is required.

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_FDO_CONTEXT fdoContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    fdoContext = NfcCxFdoGetContext(Device);

    status = NfcCxFdoCleanup(fdoContext);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "Failed to cleanup the Fdo context failed %!STATUS!", status);
        goto Done;
    }

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    TRACE_LOG_NTSTATUS_ON_FAILURE(status);
    
    return status;
}
static VOID
NfcCxLLCPInterfaceConfigureCB(
    _In_ VOID *pContext,
    _In_ NFCSTATUS NfcStatus
)
{
    NTSTATUS status = NfcCxNtStatusFromNfcStatus(NfcStatus);
    PNFCCX_LLCP_INTERFACE LLCPInterface = ((PNFCCX_LLCP_LIBNFC_REQUEST_CONTEXT)pContext)->LLCPInterface;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    NfcCxInternalSequence(NfcCxLLCPInterfaceGetRFInterface(LLCPInterface), NfcCxLLCPInterfaceGetRFInterface(LLCPInterface)->pSeqHandler, status, NULL, NULL);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
}
static VOID
NfcCxLLCPInterfaceCheckCB(
    _In_ VOID* pContext,
    _In_ NFCSTATUS NfcStatus
)
{
    NTSTATUS status = NfcCxNtStatusFromNfcStatus(NfcStatus);
    PNFCCX_LLCP_INTERFACE LLCPInterface = (PNFCCX_LLCP_INTERFACE)pContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    LLCPInterface->eRequestState = NFCCX_LLCP_REQUEST_COMPLETE;
    NfcCxInternalSequence(NfcCxLLCPInterfaceGetRFInterface(LLCPInterface), NfcCxLLCPInterfaceGetRFInterface(LLCPInterface)->pSeqHandler, status, NULL, NULL);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
}
NTSTATUS
NfcCxLLCPInterfaceActivate(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
)
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    nfcStatus = phLibNfc_Llcp_Activate(RFInterface->pLibNfcContext->pRemDevList[0].hTargetDev);
    Status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
NTSTATUS
NfcCxSNEPInterfaceServerInit(
    _In_ PNFCCX_SNEP_INTERFACE SNEPInterface
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    nfcStatus = phLibNfc_SnepServer_Init(&SNEPInterface->sConfigInfo,
                                         NfcCxSNEPInterfaceConnCB,
                                         &SNEPInterface->pServerHandleDef,
                                         (VOID *)SNEPInterface);

    status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
static VOID
NfcCxSNEPInterfaceClientPutReqCB(
    _In_ phLibNfc_Handle ConnHandle, 
    _In_ VOID *pContext, 
    _In_ NFCSTATUS NfcStatus,
    _In_ phLibNfc_Data_t *pReqResponse
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_SNEP_INTERFACE snepInterface = ((PNFCCX_SNEP_LIBNFC_REQUEST_CONTEXT)pContext)->SNEPInterface;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    UNREFERENCED_PARAMETER(pReqResponse);
    UNREFERENCED_PARAMETER(ConnHandle);

    status = NfcCxNtStatusFromNfcStatus(NfcStatus);
    NfcCxInternalSequence(NfcCxSNEPInterfaceGetRFInterface(snepInterface), ((PNFCCX_SNEP_LIBNFC_REQUEST_CONTEXT)pContext)->Sequence, status, NULL, NULL);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
}
NTSTATUS
NfcCxLLCPInterfaceDeactivate(
    _In_ PNFCCX_RF_INTERFACE RFInterface,
    _In_ NTSTATUS Status,
    _In_opt_ VOID* /*Param1*/,
    _In_opt_ VOID* /*Param2*/
)
{
    NFCSTATUS nfcStatus = NFCSTATUS_SUCCESS;
    PNFCCX_LLCP_INTERFACE LLCPInterface = NfcCxRFInterfaceGetLLCPInterface(RFInterface);

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (LLCPInterface->eLinkStatus == phFriNfc_LlcpMac_eLinkActivated) {
        nfcStatus = phLibNfc_Llcp_Deactivate(RFInterface->pLibNfcContext->pRemDevList[0].hTargetDev);
    }

    Status = NfcCxNtStatusFromNfcStatus(nfcStatus);

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, Status);
    return Status;
}
VOID
NfcCxUnbindClient(
    PWDF_CLASS_BIND_INFO ClassInfo,
    PWDF_COMPONENT_GLOBALS ClientGlobals
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_CLIENT_GLOBALS pGlobals;
    UNREFERENCED_PARAMETER(ClientGlobals);
    
    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (ClassInfo->ClassBindInfo == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Done;
    }

    if (!VerifyPrivateGlobals(*((PNFCCX_DRIVER_GLOBALS*)ClassInfo->ClassBindInfo))) {
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    pGlobals = GetPrivateGlobals(
        *((PNFCCX_DRIVER_GLOBALS*)ClassInfo->ClassBindInfo)
        );

    if (pGlobals == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Done;
    }

    free (pGlobals);
    *((PVOID*)ClassInfo->ClassBindInfo) = NULL;

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
}
NTSTATUS
NfcCxSCPresentAbsentDispatcherSetRequest(
    _In_ PNFCCX_FDO_CONTEXT FdoContext,
    _In_ PNFCCX_SC_PRESENT_ABSENT_DISPATCHER Dispatcher,
    _In_ WDFREQUEST Request
    )
/*++

Routine Description:

    Stores a request in the dispatcher. If a request is already pending in the dispatcher, the new request will
    be completed with STATUS_DEVICE_BUSY.

Arguments:

    FdoContext - Pointer to the FDO Context
    Dispatcher - Pointer to the Dispatcher.
    Request - The request to store in the dispatcher.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status;
    bool powerReferenceAcquired = false;
    bool cancelCallbackSet = false;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request);
    PNFCCX_FILE_CONTEXT fileContext = NfcCxFileGetContext(fileObject);

    // Pre-check if there is a current request.
    if (ReadPointerAcquire((void**)&Dispatcher->CurrentRequest) != nullptr)
    {
        status = STATUS_DEVICE_BUSY;
        TRACE_LINE(LEVEL_ERROR, "An Is Present/Absent request is already pending. %!STATUS!", status);
        goto Done;
    }

    // Allocate and initialize the request context
    PNFCCX_SC_REQUEST_CONTEXT requestContext = nullptr;

    WDF_OBJECT_ATTRIBUTES contextAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&contextAttributes, NFCCX_SC_REQUEST_CONTEXT);

    status = WdfObjectAllocateContext(Request, &contextAttributes, (void**)&requestContext);
    if (!NT_SUCCESS(status))
    {
        TRACE_LINE(LEVEL_ERROR, "Failed to set request's context. %!STATUS!", status);
        goto Done;
    }

    requestContext->Dispatcher = Dispatcher;

    // Add a power reference (if required).
    if (Dispatcher->PowerManaged)
    {
        status = NfcCxPowerFileAddReference(FdoContext->Power, fileContext, NfcCxPowerReferenceType_Proximity);
        if (!NT_SUCCESS(status))
        {
            TRACE_LINE(LEVEL_VERBOSE, "Failed to acquire power reference. %!STATUS!", status);
            goto Done;
        }

        powerReferenceAcquired = true;
    }

    // Setup cancel callback.
    status = WdfRequestMarkCancelableEx(Request, NfcCxSCPresentAbsentDispatcherRequestCanceled);
    if (!NT_SUCCESS(status))
    {
        TRACE_LINE(LEVEL_ERROR, "Failed to set request canceled callback. %!STATUS!", status);
        goto Done;
    }

    // Add a ref-count to the request, which is owned by the cancel callback.
    WdfObjectReference(Request);
    cancelCallbackSet = true;

    // Try to set the current request.
    void* previousRequest = InterlockedCompareExchangePointer((void**)&Dispatcher->CurrentRequest, /*exchange*/ Request, /*compare*/ nullptr);

    // Check if we already have a pending request.
    if (previousRequest != nullptr)
    {
        status = STATUS_DEVICE_BUSY;
        TRACE_LINE(LEVEL_ERROR, "An Is Present/Absent request is already pending. %!STATUS!", status);
        goto Done;
    }

Done:
    if (!NT_SUCCESS(status))
    {
        if (cancelCallbackSet)
        {
            NTSTATUS unmarkStatus = WdfRequestUnmarkCancelable(Request);
            if (unmarkStatus != STATUS_CANCELLED)
            {
                // Cancel callback will not be called.
                // So release cancel callback's request ref-count.
                WdfObjectDereference(Request);
            }
        }

        if (powerReferenceAcquired)
        {
            NfcCxPowerFileRemoveReference(FdoContext->Power, fileContext, NfcCxPowerReferenceType_Proximity);
        }
    }

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
NTSTATUS
NfcCxEvtUnregisterSequenceHandler(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE Device,
    _In_ NFC_CX_SEQUENCE Sequence
    )
/*++

Routine Description:

    This routine is called by the CX client to unregister
    a previously registered sequence handler

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize
    Sequence - The sequence to unregister

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_FDO_CONTEXT fdoContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    if (SequenceMaximum <= Sequence) {
        TRACE_LINE(LEVEL_ERROR, "Invalid Client Driver Parameters");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    fdoContext = NfcCxFdoGetContext(Device);

    if (fdoContext->RFInterface == NULL) {
        TRACE_LINE(LEVEL_ERROR, "CX not initialized");
        status = STATUS_INVALID_DEVICE_STATE;
        goto Done;
    }

    TraceLoggingWrite(
        g_hNfcCxProvider,
        "NfcCxEvtUnregisterSequence",
        TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY),
        TraceLoggingValue((DWORD)Sequence, "sequence"));

    status = NfcCxRFInterfaceUnregisterSequenceHandler(fdoContext->RFInterface, Sequence);

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING  RegistryPath
    )
/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

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, error code otherwise.

--*/
{
    NTSTATUS status;
    WDF_DRIVER_CONFIG config;
    WDFDRIVER hDriver;

#ifdef WPP_MACRO_USE_KM_VERSION_FOR_UM
    WPP_INIT_TRACING(DriverObject, RegistryPath);
#endif

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    WDF_DRIVER_CONFIG_INIT(&config, NULL);
    config.DriverInitFlags = WdfDriverInitNonPnpDriver;
    config.DriverPoolTag = NFCCX_POOL_TAG;
    config.EvtDriverUnload = NfcCxEvtDriverUnload;

    status = WdfDriverCreate(DriverObject,
                             RegistryPath,
                             WDF_NO_OBJECT_ATTRIBUTES,
                             &config,
                             &hDriver);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "WdfDriverCreate failed status = %!STATUS!", status);
#ifdef WPP_MACRO_USE_KM_VERSION_FOR_UM
        WPP_CLEANUP(DriverObject);
#endif
        goto Done;
    }

    status = WdfRegisterClassLibrary(&WdfClassLibraryInfo,
                                     RegistryPath,
                                     NULL);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "WdfRegisterClassLibrary failed status = %!STATUS!", status);
#ifdef WPP_MACRO_USE_KM_VERSION_FOR_UM
        WPP_CLEANUP(DriverObject);
#endif
        goto Done;
    }

Done:
    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
NTSTATUS
NfcCxEvtNciReadNotification(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE    Device,
    _In_ WDFMEMORY    Memory
    )
/*++

Routine Description:

    This routine is called by the CX client to signal a
    read notification.  The implementation of this function forwards
    the content of the notification to the Tml layer which will complete
    a pended read request up to the NfcLib.

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize
    Memory - A pointer to a WDFMEMORY object that contains the 
             content of the read notification

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    
    PUCHAR buffer = NULL;
    size_t bufferSize = 0;

    UNREFERENCED_PARAMETER(NfcCxGlobals);

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    buffer = (PUCHAR)WdfMemoryGetBuffer(Memory, &bufferSize);

    if (MAX_USHORT < bufferSize) {
        TRACE_LINE(LEVEL_ERROR, "Invalid read notification sent, ignoring!!!");
        NT_ASSERTMSG("ReadNotification too large", FALSE);
        goto Done;
    }

    //
    // Forward the read to the Tml interface
    //
    status = NfcCxTmlDispatchReadNotification((NfcCxFdoGetContext(Device))->TmlInterface,
                                              buffer,
                                              (USHORT)bufferSize);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "Failed to dispatch read notification, %!STATUS!", status);
        goto Done;
    }

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    return status;
}
NTSTATUS
NfcCxEvtHardwareEvent(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE Device,
    _In_ PNFC_CX_HARDWARE_EVENT NciCxHardwareEventParams
    )
/*++

Routine Description:

    This routine is called by the CX client to indicate
    that a Hardware Event has occured.

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize
    NciCxHardwareEventParams - A pointer to a hardware event description structure

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PNFCCX_FDO_CONTEXT fdoContext;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    fdoContext = NfcCxFdoGetContext(Device);

    TRACE_LINE(LEVEL_INFO, "Client signalled an event, %!STATUS!, %!NFC_CX_HOST_ACTION!", 
                NciCxHardwareEventParams->HardwareStatus,
                NciCxHardwareEventParams->HostAction);

    switch (NciCxHardwareEventParams->HostAction) {
    case HostActionStart:
        status = NfcCxFdoInitialize(fdoContext);
        if (!NT_SUCCESS(status)) {
            NfcCxFdoDeInitialize(fdoContext);
            TRACE_LINE(LEVEL_ERROR, "Failed to initialize the Fdo, %!STATUS!", status);
            goto Done;
        }
        break;
    case HostActionStop:
        status = NfcCxFdoDeInitialize(fdoContext);
        if (!NT_SUCCESS(status)) {
            TRACE_LINE(LEVEL_ERROR, "Failed to NfcCxFdoDeInitialize the Fdo, %!STATUS!", status);
            goto Done;
        }
        break;
    case HostActionRestart:
        WdfDeviceSetFailed(Device, WdfDeviceFailedAttemptRestart);
        break;
    case HostActionUnload:
        WdfDeviceSetFailed(Device, WdfDeviceFailedNoRestart);
        break;
    default:
        TRACE_LINE(LEVEL_ERROR, "Invalid Host Action %d", NciCxHardwareEventParams->HostAction);
        break;
    }

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    TRACE_LOG_NTSTATUS_ON_FAILURE(status);

    return status;
}
NTSTATUS
NfcCxEvtDeviceInitialize(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This routine is called by the CX client to indicate
    that a device initialization is required.

Arguments:

    NfcCxGlobal - CX global pointer
    Device - WDF device to initialize

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                              status = STATUS_SUCCESS;
    WDF_OBJECT_ATTRIBUTES                 fdoAttributes;
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
    WDF_IO_QUEUE_CONFIG                   queueConfig;
    PNFCCX_FDO_CONTEXT                    fdoContext;
    WDFQUEUE                              queue;
    PNFCCX_CLIENT_GLOBALS                 nfcCxClientGlobal;
    WDF_OBJECT_ATTRIBUTES                 objectAttrib;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    nfcCxClientGlobal = GetPrivateGlobals(NfcCxGlobals);

    //
    // Create class extension device context
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, NFCCX_FDO_CONTEXT);
    fdoAttributes.EvtCleanupCallback = NfcCxFdoContextCleanup;
    
    status = WdfObjectAllocateContext(Device, &fdoAttributes, (PVOID*)&fdoContext);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "Failed to allocate the client context");
        goto Done;
    }
    fdoContext->Device = Device;
    fdoContext->NfpRadioInterfaceCreated = FALSE;
    fdoContext->NfpPowerOffSystemOverride = FALSE;
    fdoContext->NfpPowerOffPolicyOverride = FALSE;
    fdoContext->NfpPowerPolicyReferences = 0;
    fdoContext->SERadioInterfaceCreated = FALSE;
    fdoContext->SEPowerOffSystemOverride = FALSE;
    fdoContext->SEPowerOffPolicyOverride = FALSE;
    fdoContext->SEPowerPolicyReferences = 0;
    fdoContext->NfcCxClientGlobal = nfcCxClientGlobal;

    status = NfcCxFdoReadCxDriverRegistrySettings(&fdoContext->LogNciDataMessages);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "NfcCxFdoReadCxDriverRegistrySettings failed, %!STATUS!", status);
        goto Done;
    }

    WDF_OBJECT_ATTRIBUTES_INIT(&objectAttrib);
    objectAttrib.ParentObject = Device;
    
    status = WdfWaitLockCreate(&objectAttrib,
                                &fdoContext->PowerPolicyWaitLock);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "Failed to create the PowerPolicy WaitLock, %!STATUS!", status);
        goto Done;
    }
    
    //
    // Register I/O callbacks to tell the framework that you are interested
    // in handling 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);

    //
    // Our default queue is non power managed. Based on the request and the Radio on/off
    // state, we forward the request to the power managed queue to wake the system as
    // appropriate
    //
    queueConfig.PowerManaged = WdfFalse;
    queueConfig.EvtIoDeviceControl = NfcCxEvtDefaultIoControl;

    WDF_OBJECT_ATTRIBUTES_INIT(&objectAttrib);
    objectAttrib.ParentObject = Device;

    status = WdfIoQueueCreate(Device,
                                &queueConfig,
                                &objectAttrib,
                                &queue
                                );
    if (!NT_SUCCESS (status)) {
        TRACE_LINE(LEVEL_ERROR, "WdfIoQueueCreate failed %!STATUS!", status);
        goto Done;
    }
    fdoContext->DefaultQueue = queue;

    //
    // Our internal queue is non power managed because we need to send IO during.
    // D0Entry/D0Exit routines. It is the default queue for SelfIoTarget requests.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
                             WdfIoQueueDispatchParallel);

    queueConfig.PowerManaged = WdfFalse;
    queueConfig.EvtIoDeviceControl = NfcCxEvtSelfIoControl;

    WDF_OBJECT_ATTRIBUTES_INIT(&objectAttrib);
    objectAttrib.ParentObject = Device;

    status = WdfIoQueueCreate(Device,
                                &queueConfig,
                                &objectAttrib,
                                &queue
                                );
    if (!NT_SUCCESS (status)) {
        TRACE_LINE(LEVEL_ERROR, "WdfIoQueueCreate failed %!STATUS!", status);
        goto Done;
    }
    fdoContext->SelfQueue = queue;

    //
    // Assign our internal queue as the default queue for the SelfIoTarget. Any IO
    // sent to the SelfIoTarget would be dispatched from this queue.
    //
    status = WdfIoTargetSelfAssignDefaultIoQueue(WdfDeviceGetSelfIoTarget(Device),
                                                 fdoContext->SelfQueue);
    if (!NT_SUCCESS (status)) {
        TRACE_LINE(LEVEL_ERROR, "WdfIoTargetSelfAssignDefaultIoQueue failed %!STATUS!", status);
        goto Done;
    }

    status = NfcCxFdoCreate(fdoContext);
    if (!NT_SUCCESS(status)) {
        TRACE_LINE(LEVEL_ERROR, "Failed to create the Fdo context failed %!STATUS!", status);
        goto Done;
    }

    if (NFC_CX_DEVICE_MODE_NCI == fdoContext->NfcCxClientGlobal->Config.DeviceMode) {
        //
        // Read the FDO's persisted settings from the registry
        //
        status = NfcCxFdoReadPersistedDeviceRegistrySettings(fdoContext);
        if (!NT_SUCCESS(status)) {
            TRACE_LINE(LEVEL_ERROR, "NfcCxFdoReadPersistedDeviceRegistrySettings, %!STATUS!", status);
            goto Done;
        }

        //
        // Log all settings that could have been overridden from the registry.
        //
        EventWriteDevicePersistedRegistrySettings(fdoContext->NfpPowerOffPolicyOverride,
                                                  fdoContext->NfpPowerOffSystemOverride,
                                                  fdoContext->SEPowerOffPolicyOverride,
                                                  fdoContext->SEPowerOffSystemOverride);

        //
        // Check the currently required power state
        //
        fdoContext->NfpRadioState = (!fdoContext->NfpPowerOffPolicyOverride && !fdoContext->NfpPowerOffSystemOverride);
        fdoContext->SERadioState = (!fdoContext->SEPowerOffPolicyOverride && !fdoContext->SEPowerOffSystemOverride);
    }

    //
    // The class extension is the default power policy owner. This option allows the client
    // to now be the power policy owner
    //
    if (fdoContext->NfcCxClientGlobal->Config.IsPowerPolicyOwner == WdfUseDefault) {
        fdoContext->NfcCxClientGlobal->Config.IsPowerPolicyOwner = WdfFalse;
    }

    if (fdoContext->NfcCxClientGlobal->Config.IsPowerPolicyOwner == WdfFalse) {
        //
        // 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 non wakeable 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.IdleTimeoutType = fdoContext->NfcCxClientGlobal->Config.PowerIdleType;
        idleSettings.IdleTimeout = fdoContext->NfcCxClientGlobal->Config.PowerIdleTimeout;

        status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
        if (!NT_SUCCESS(status)) {
            TRACE_LINE(LEVEL_ERROR, "WdfDeviceAssignS0IdleSettings failed %!STATUS!", status);
            goto Done;
        }
    }

Done:

    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    TRACE_LOG_NTSTATUS_ON_FAILURE(status);
    
    return status;
}
NTSTATUS
NfcCxEvtDeviceInitConfig(
    _In_ PNFCCX_DRIVER_GLOBALS NfcCxGlobals,
    _In_ PWDFDEVICE_INIT DeviceInit,
    _In_ PNFC_CX_CLIENT_CONFIG Config
    )
/*++
Routine Description:

    NfcCxEvtDeviceInitConfig is called by the client driver from within
    its AddDevice callback to finish initializing the device before creation.

Arguments:

    NfcCxGlobals - Pointer to the Class extension globals
    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
    Config - Pointer to a client provided structure with their configuration.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                          status = STATUS_SUCCESS; 
    WDFCX_FILEOBJECT_CONFIG           fileConfig;
    WDF_OBJECT_ATTRIBUTES             fileObjectAttributes;
    PWDFCXDEVICE_INIT                 exDeviceInit;
    PNFCCX_CLIENT_GLOBALS             nfcCxClientGlobal;

    TRACE_FUNCTION_ENTRY(LEVEL_VERBOSE);

    if (!VerifyPrivateGlobals(NfcCxGlobals)) {
        TRACE_LINE(LEVEL_ERROR, "Invalid CX global pointer");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    if (NULL == Config ||
        sizeof(NFC_CX_CLIENT_CONFIG) != Config->Size ||
        NULL == Config->EvtNfcCxWriteNciPacket) {
        TRACE_LINE(LEVEL_ERROR, "Invalid Client Driver Configuration");
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    if (NFC_CX_DEVICE_MODE_NCI != Config->DeviceMode &&
        NFC_CX_DEVICE_MODE_DTA != Config->DeviceMode &&
        NFC_CX_DEVICE_MODE_RAW != Config->DeviceMode) {
        TRACE_LINE(LEVEL_ERROR, "Invalid Client Driver Device Mode %d", Config->DeviceMode);
        status = STATUS_INVALID_PARAMETER;
        goto Done;
    }

    nfcCxClientGlobal = GetPrivateGlobals(NfcCxGlobals);

    //
    // The CX is the power policy owner of this stack.
    //
    WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, TRUE);

    //
    // Allocate the CX DeviceInit
    //
    exDeviceInit = WdfCxDeviceInitAllocate(DeviceInit);
    if (NULL == exDeviceInit) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        TRACE_LINE(LEVEL_ERROR,
            "Failed WdfCxDeviceInitAllocate, status=%!STATUS!", status);
        goto Done;
    }

    //
    // Enable SelfIoTarget
    //
    WdfDeviceInitAllowSelfIoTarget(DeviceInit);

    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // per handle (fileobject) context.
    //
    WDFCX_FILEOBJECT_CONFIG_INIT(
                            &fileConfig,
                            NfcCxEvtDeviceFileCreate,
                            NfcCxEvtFileClose,
                            WDF_NO_EVENT_CALLBACK // not interested in Cleanup
                            );

    WDF_OBJECT_ATTRIBUTES_INIT(&fileObjectAttributes);
    fileObjectAttributes.SynchronizationScope = WdfSynchronizationScopeNone;

    WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&fileObjectAttributes,
                                            NFCCX_FILE_CONTEXT);

    WdfCxDeviceInitSetFileObjectConfig(exDeviceInit,
                                       &fileConfig,
                                       &fileObjectAttributes);

    TRACE_LINE(LEVEL_INFO, "DriverFlags=0x%x PowerIdleType=%d PowerIdleTimeout=%d",
                            Config->DriverFlags, Config->PowerIdleType, Config->PowerIdleTimeout);

    TraceLoggingWrite(g_hNfcCxProvider,
        "NfcCxClientConfig",
        TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY),
        TraceLoggingHexInt32(Config->DriverFlags, "DriverFlags"),
        TraceLoggingValue((INT32)(Config->PowerIdleType), "PowerIdleType"),
        TraceLoggingValue(Config->PowerIdleTimeout, "PowerIdleTimeout"));

    //
    // Save the client driver configs
    //
    RtlCopyMemory(&nfcCxClientGlobal->Config, Config, Config->Size);

Done:
    
    TRACE_FUNCTION_EXIT_NTSTATUS(LEVEL_VERBOSE, status);
    TRACE_LOG_NTSTATUS_ON_FAILURE(status);
    
    return status;
}