VOID
UfxDevice_EvtDeviceRemoteWakeupSignal (
    _In_ UFXDEVICE UfxDevice
    )
/*++
Routine Description:

    Signals Remote Wakeup to the Host by issuing a link state change command.
    It acquires and releases the power reference to ensure a valid power state
    before accessing the device.

Arguments:

    UfxDevice - UFXDEVICE object representing the device.

--*/
{
    NTSTATUS Status;
    PUFXDEVICE_CONTEXT DeviceContext;

    PAGED_CODE();

    TraceEntry();

    DeviceContext = UfxDeviceGetContext(UfxDevice);

    //
    // Stop Idle to ensure the device is in working state
    //
    Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE);
    if (!NT_SUCCESS(Status)) {
        TraceError("Failed to stop idle %!STATUS!", Status);
        goto End;
    }

    //
    // Issue a Link State Change Request.
    //

    //
    // #### TODO: Insert code to issue a link state change on the controller ####
    //

    WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice);

End:
    UfxDeviceEventComplete(UfxDevice, Status);
    TraceExit();
}
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 = ExAllocatePoolWithTag(
                                                 NonPagedPool,
                                                 extension->BufferSize,
                                                 POOL_TAG
                                                 );

            if (extension->InterruptReadBuffer) {
                break;
            }

        }

        case MmMediumSystem: {

            extension->BufferSize = 1024;
            extension->InterruptReadBuffer = ExAllocatePoolWithTag(
                                                 NonPagedPool,
                                                 extension->BufferSize,
                                                 POOL_TAG
                                                 );

            if (extension->InterruptReadBuffer) {
                break;
            }

        }

        case MmSmallSystem: {

            extension->BufferSize = 128;
            extension->InterruptReadBuffer = ExAllocatePoolWithTag(
                                                 NonPagedPool,
                                                 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;

}
NTSTATUS
UfxDeviceStopOrResumeIdle (
    _In_ UFXDEVICE Device,
    _In_ USBFN_DEVICE_STATE UsbState,
    _In_ USBFN_PORT_TYPE UsbPort
    )
/*++

Routine Description:

    Examines the device USB state and port type and determines
    if it needs to stop or resume idle.

Arguments:

    UfxDevice - UFXDEVICE object representing the device.

Return Value:

    STATUS_SUCCESS on success, or an appropirate NTSTATUS message on failure.

--*/
{
    PUFXDEVICE_CONTEXT DeviceContext;
    PCONTROLLER_CONTEXT ControllerContext;
    NTSTATUS Status;
    BOOLEAN NeedPower;
    DEVICE_POWER_STATE DxState = PowerDeviceD3;

    PAGED_CODE();

    TraceEntry();

    DeviceContext = UfxDeviceGetContext(Device);
    ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice);

    switch (UsbState) {

        case UsbfnDeviceStateAttached:
            __fallthrough;
        case UsbfnDeviceStateDefault:

            switch (UsbPort) {

                case UsbfnStandardDownstreamPort:
                    __fallthrough;
                case UsbfnChargingDownstreamPort:
                    __fallthrough;
                case UsbfnUnknownPort:
                    NeedPower = TRUE;
                    break;

                case UsbfnDedicatedChargingPort:
                case UsbfnProprietaryDedicatedChargingPort:
                    NeedPower = FALSE;
                    DxState = PowerDeviceD2;
                    break;

                default:
                    NeedPower = FALSE;
            }
            break;

        case UsbfnDeviceStateSuspended:
            NeedPower = FALSE;
            DxState = PowerDeviceD2;
            break;

        case UsbfnDeviceStateAddressed:
            __fallthrough;

        case UsbfnDeviceStateConfigured:
            NeedPower = TRUE;
            break;

        default:
            NeedPower = FALSE;
    }

    //
    // Determine if our lowest idle state has changed, and set it if so
    //
    if (DxState != ControllerContext->IdleSettings.DxState)
    {
        ControllerContext->IdleSettings.DxState = DxState;
        Status = WdfDeviceAssignS0IdleSettings(
            DeviceContext->FdoWdfDevice,
            &ControllerContext->IdleSettings);
        CHK_NT_MSG(Status, "Failed to update device idle settings");
    }

    if (NeedPower && DeviceContext->IsIdle) {
        //
        // We don't want to update the USB state /port until we stop idle to
        // prevent D0 -> DX path from reading the wrong state.
        //
        TraceInformation("Stopping idle");
        Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE);
        CHK_NT_MSG(Status, "Failed to stop idle");
        DeviceContext->UsbState = UsbState;
        DeviceContext->UsbPort = UsbPort;
        DeviceContext->IsIdle = FALSE;

    } else if (!NeedPower && !DeviceContext->IsIdle) {
        //
        // We need to update USB state / port before resume idle to ensure
        // D0 -> DX path reads the correct state.
        //
        DeviceContext->UsbState = UsbState;
        DeviceContext->UsbPort = UsbPort;
        DeviceContext->IsIdle = TRUE;
        TraceInformation("Resuming idle");
        WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice);

    } else {
        TraceInformation("No idle action");
        DeviceContext->UsbState = UsbState;
        DeviceContext->UsbPort = UsbPort;
    }

    Status = STATUS_SUCCESS;

End:
    TraceExit();
    return Status;
}