/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxUsbDevice.hpp

Abstract:

Author:

Environment:

    kernel mode only

Revision History:

--*/

#ifndef _FXUSBDEVICE_H_
#define _FXUSBDEVICE_H_

#include "fxusbrequestcontext.hpp"

typedef enum _FX_URB_TYPE : UCHAR {
    FxUrbTypeLegacy,
    FxUrbTypeUsbdAllocated
} FX_URB_TYPE;

struct FxUsbDeviceControlContext : public FxUsbRequestContext {
    FxUsbDeviceControlContext(
        __in FX_URB_TYPE FxUrbType
        );

    ~FxUsbDeviceControlContext(
        VOID
        );

    __checkReturn
    NTSTATUS
    AllocateUrb(
        __in USBD_HANDLE USBDHandle
        );

    virtual
    VOID
    Dispose(
        VOID
        );

    virtual
    VOID
    CopyParameters(
        __in FxRequestBase* Request
        );

    VOID
    StoreAndReferenceMemory(
        __in FxUsbDevice* Device,
        __in FxRequestBuffer* Buffer,
        __in PWDF_USB_CONTROL_SETUP_PACKET SetupPacket
        );

    virtual
    VOID
    ReleaseAndRestore(
        __in FxRequestBase* Request
        );

    USBD_STATUS
    GetUsbdStatus(
        VOID
        );

private:
    USBD_HANDLE m_USBDHandle;

public:

    _URB_CONTROL_TRANSFER m_UrbLegacy;

    //
    // m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate
    //
    _URB_CONTROL_TRANSFER* m_Urb;

    PMDL m_PartialMdl;

    BOOLEAN m_UnlockPages;
};

struct FxUsbDeviceStringContext : public FxUsbRequestContext {
    FxUsbDeviceStringContext(
        __in FX_URB_TYPE FxUrbType
        );

    ~FxUsbDeviceStringContext(
        VOID
        );

    __checkReturn
    NTSTATUS
    AllocateUrb(
        __in USBD_HANDLE USBDHandle
        );

    virtual
    VOID
    Dispose(
        VOID
        );

    virtual
    VOID
    CopyParameters(
        __in FxRequestBase* Request
        );

    VOID
    SetUrbInfo(
        __in  UCHAR StringIndex,
        __in  USHORT LangID
        );

    USBD_STATUS
    GetUsbdStatus(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    AllocateDescriptor(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals,
        __in size_t BufferSize
        );

private:
    USBD_HANDLE m_USBDHandle;

public:

    _URB_CONTROL_DESCRIPTOR_REQUEST m_UrbLegacy;

    //
    // m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate
    //
    _URB_CONTROL_DESCRIPTOR_REQUEST* m_Urb;

    PUSB_STRING_DESCRIPTOR m_StringDescriptor;

    ULONG m_StringDescriptorLength;
};

class FxUsbUrb : public FxMemoryBufferPreallocated {
public:

    FxUsbUrb(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals,
        __in USBD_HANDLE USBDHandle,
        __in_bcount(BufferSize) PVOID Buffer,
        __in size_t BufferSize
        );

protected:

    virtual
    BOOLEAN
    Dispose(
        VOID
        );

    ~FxUsbUrb();

private:

    USBD_HANDLE m_USBDHandle;
};


#define FX_USB_DEVICE_TAG   'sUfD'

class FxUsbDevice : public FxIoTarget {
public:
    friend FxUsbPipe;
    friend FxUsbInterface;

    FxUsbDevice(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        );

    _Must_inspect_result_
    NTSTATUS
    InitDevice(
        __in ULONG USBDClientContractVersionForWdfClient
        );

    _Must_inspect_result_
    NTSTATUS
    GetConfigDescriptor(
        __out PVOID ConfigDescriptor,
        __inout PUSHORT ConfigDescriptorLength
        );

    _Must_inspect_result_
    NTSTATUS
    GetString(
        __in_ecount(*NumCharacters) PUSHORT String,
        __in PUSHORT NumCharacters,
        __in UCHAR StringIndex,
        __in_opt USHORT LangID,
        __in_opt WDFREQUEST Request = NULL,
        __in_opt PWDF_REQUEST_SEND_OPTIONS Options = NULL
        );

    __inline
    VOID
    CopyDeviceDescriptor(
        __out PUSB_DEVICE_DESCRIPTOR UsbDeviceDescriptor
        )
    {
        RtlCopyMemory(UsbDeviceDescriptor,
                      &m_DeviceDescriptor,
                      sizeof(m_DeviceDescriptor));
    }

    VOID
    GetInformation(
        __out PWDF_USB_DEVICE_INFORMATION Information
        );

    __inline
    USBD_CONFIGURATION_HANDLE
    GetConfigHandle(
        VOID
        )
    {
        return m_ConfigHandle;
    }

    _Must_inspect_result_
    __inline
    NTSTATUS
    GetCurrentFrameNumber(
        __in PULONG Current
        )
    {
        if (m_QueryBusTime != NULL) {
            return m_QueryBusTime(m_BusInterfaceContext, Current);
        }
        else {
            return STATUS_UNSUCCESSFUL;
        }
    }

    _Must_inspect_result_
    NTSTATUS
    SelectConfigAuto(
        __in PWDF_OBJECT_ATTRIBUTES PipeAttributes
        );

    _Must_inspect_result_
    NTSTATUS
    SelectConfigInterfaces(
        __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
        __in PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
        __in_ecount(NumInterfaces)PUSB_INTERFACE_DESCRIPTOR* InterfaceDescriptors,
        __in ULONG NumInterfaces
        );

    _Must_inspect_result_
    NTSTATUS
    SelectConfig(
        __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
        __in PURB Urb,
        __in FX_URB_TYPE FxUrbType,
        __out_opt PUCHAR NumConfiguredInterfaces
        );

    _Must_inspect_result_
    NTSTATUS
    Deconfig(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    SelectInterfaceByInterface(
        __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
        __in PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor
        );

    _Must_inspect_result_
    NTSTATUS
    SelectInterface(
        __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
        __in PURB Urb
        );

    UCHAR
    GetNumInterfaces(
        VOID
        )
    {
        return m_NumInterfaces;
    }

    UCHAR
    GetInterfaceNumEndpoints(
        __in UCHAR InterfaceNumber
        );

    WDFUSBPIPE
    GetInterfacePipeReferenced(
        __in UCHAR InterfaceNumber,
        __in UCHAR EndpointNumber
        );

    _Must_inspect_result_
    NTSTATUS
    FormatStringRequest(
        __in FxRequestBase* Request,
        __in FxRequestBuffer *RequestBuffer,
        __in  UCHAR StringIndex,
        __in  USHORT LangID
        );

    _Must_inspect_result_
    NTSTATUS
    FormatControlRequest(
        __in FxRequestBase* Request,
        __in PWDF_USB_CONTROL_SETUP_PACKET Packet,
        __in FxRequestBuffer *RequestBuffer
        );

    _Must_inspect_result_
    NTSTATUS
    IsConnected(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    Reset(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    CyclePort(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    FormatCycleRequest(
        __in FxRequestBase* Request
        );

    BOOLEAN
    OnUSBD(
        VOID
        )
    {
        return m_OnUSBD;
    }

    USBD_PIPE_HANDLE
    GetControlPipeHandle(
        VOID
        )
    {
        return m_ControlPipe;
    }

    _Must_inspect_result_
    NTSTATUS
    CreateInterfaces(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    SelectConfigSingle(
        __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
        __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
        );

    _Must_inspect_result_
    NTSTATUS
    SelectConfigMulti(
        __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
        __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
        );

    _Must_inspect_result_
    NTSTATUS
    SelectConfigDescriptor(
        __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
        __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
        );

    FxUsbInterface *
    GetInterfaceFromIndex(
        __in UCHAR InterfaceIndex
        );

    BOOLEAN
    HasMismatchedInterfacesInConfigDescriptor(
        VOID
        )
    {
        return m_MismatchedInterfacesInConfigDescriptor;
    }

    VOID
    CancelSentIo(
        VOID
        );

    BOOLEAN
    IsEnabled(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    QueryUsbCapability(
        __in
        CONST GUID* CapabilityType,
        __in
        ULONG CapabilityBufferLength,
        __drv_when(CapabilityBufferLength == 0, __out_opt)
        __drv_when(CapabilityBufferLength != 0 && ResultLength == NULL, __out_bcount(CapabilityBufferLength))
        __drv_when(CapabilityBufferLength != 0 && ResultLength != NULL, __out_bcount_part_opt(CapabilityBufferLength, *ResultLength))
        PVOID CapabilityBuffer,
        __out_opt
        __drv_when(ResultLength != NULL,__deref_out_range(<=,CapabilityBufferLength))
        PULONG ResultLength
        );

    __checkReturn
    NTSTATUS
    CreateUrb(
        __in_opt
        PWDF_OBJECT_ATTRIBUTES Attributes,
        __out
        WDFMEMORY* UrbMemory,
        __deref_opt_out_bcount(sizeof(URB))
        PURB* Urb
        );

#ifdef _MSC_VER
#pragma warning(disable:28285)
#endif
    __checkReturn
    NTSTATUS
    CreateIsochUrb(
        __in_opt
        PWDF_OBJECT_ATTRIBUTES Attributes,
        __in
        ULONG NumberOfIsochPackets,
        __out
        WDFMEMORY* UrbMemory,
        __deref_opt_out_bcount(GET_ISOCH_URB_SIZE(NumberOfIsochPackets))
        PURB* Urb
        );

    USBD_HANDLE
    GetUSBDHandle(
        VOID
        )
    {
        return m_USBDHandle;
    }

    FX_URB_TYPE
    GetUrbType(
        VOID
        )
    {
        return m_UrbType;
    }

    FX_URB_TYPE
    GetFxUrbTypeForRequest(
        __in FxRequestBase* Request
        );

    BOOLEAN
    IsObjectDisposedOnRemove(
        __in FxObject* Object
        );

protected:
    ~FxUsbDevice(
        VOID
        );

    VOID
    RemoveDeletedInterface(
        __in FxUsbInterface* Interface
        );

    //
    // FxIoTarget overrides
    //
    virtual
    _Must_inspect_result_
    NTSTATUS
    Start(
        VOID
        );

    virtual
    VOID
    Stop(
        __in WDF_IO_TARGET_SENT_IO_ACTION Action
        );

    virtual
    VOID
    Purge(
        __in WDF_IO_TARGET_PURGE_IO_ACTION Action
        );
    // end FxIoTarget overrides

    VOID
    PipesGotoRemoveState(
        __in BOOLEAN ForceRemovePipes
        );

    static
    VOID
    _CleanupPipesRequests(
        __in PLIST_ENTRY PendHead,
        __in PSINGLE_LIST_ENTRY SentHead
        );

    FxUsbInterface *
    GetInterfaceFromNumber(
        __in UCHAR InterfaceNumber
        );

    _Must_inspect_result_
    NTSTATUS
    GetInterfaceNumberFromInterface(
        __in WDFUSBINTERFACE UsbInterface,
        __out PUCHAR InterfaceNumber
        );

    VOID
    CleanupInterfacePipesAndDelete(
        __in FxUsbInterface * UsbInterface
        );

    _Acquires_lock_(_Global_critical_region_)
    VOID
    AcquireInterfaceIterationLock(
        VOID
        )
    {
        m_InterfaceIterationLock.AcquireLock(GetDriverGlobals());
    }

    _Releases_lock_(_Global_critical_region_)
    VOID
    ReleaseInterfaceIterationLock(
        VOID
        )
    {
        m_InterfaceIterationLock.ReleaseLock(GetDriverGlobals());
    }

    ULONG
    GetDefaultMaxTransferSize(
        VOID
        );

    VOID
    FormatInterfaceSelectSettingUrb(
        __in PURB Urb,
        __in USHORT NumEndpoints,
        __in UCHAR InterfaceNumber,
        __in UCHAR SettingNumber
        );

    virtual
    BOOLEAN
    Dispose(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    GetPortStatus(
        __out PULONG PortStatus
        );

#if (FX_CORE_MODE == FX_CORE_USER_MODE)
    _Must_inspect_result_
    NTSTATUS
    FxUsbDevice::SendSyncRequest(
        __in FxSyncRequest* Request,
        __in ULONGLONG Time
        );

    _Must_inspect_result_
    NTSTATUS
    SendSyncUmUrb(
        __inout PUMURB Urb,
        __in ULONGLONG Time,
        __in_opt WDFREQUEST Request = NULL,
        __in_opt PWDF_REQUEST_SEND_OPTIONS Options = NULL
        );
#endif

protected:
    USBD_HANDLE m_USBDHandle;

    USBD_PIPE_HANDLE m_ControlPipe;

    FxUsbInterface ** m_Interfaces;

    USBD_CONFIGURATION_HANDLE m_ConfigHandle;

    USB_DEVICE_DESCRIPTOR m_DeviceDescriptor;

    PUSB_CONFIGURATION_DESCRIPTOR m_ConfigDescriptor;

    USBD_VERSION_INFORMATION m_UsbdVersionInformation;

    PUSB_BUSIFFN_QUERY_BUS_TIME m_QueryBusTime;

    PVOID m_BusInterfaceContext;

    PINTERFACE_DEREFERENCE m_BusInterfaceDereference;

    FxWaitLockInternal m_InterfaceIterationLock;

    ULONG m_HcdPortCapabilities;

    ULONG m_Traits;

    BOOLEAN m_OnUSBD;

    UCHAR m_NumInterfaces;

    BOOLEAN m_MismatchedInterfacesInConfigDescriptor;

    FX_URB_TYPE m_UrbType;

#if (FX_CORE_MODE == FX_CORE_USER_MODE)
private:
    //
    // Used to format the IWudfIrp for user-mode requests
    //
    IWudfFile* m_pHostTargetFile;

    //
    // Handle to the default USB interface exposed by WinUsb
    //
    WINUSB_INTERFACE_HANDLE m_WinUsbHandle;
#endif
};

#endif // _FXUSBDEVICE_H_