/*
 *************************************************************************
 *  File:       USBCCGP.H
 *
 *  Module:     USBCCGP.SYS
 *              USB Common Class Generic Parent driver.
 *
 *  Copyright (c) 1998  Microsoft Corporation
 *
 *
 *  Author:     ervinp
 *
 *************************************************************************
 */

#include <msosdesc.h>   // Internal definitions for MS OS Desc.

/*
 *  USBCCGP signature tag for memory allocations
 */
#define USBCCGP_TAG (ULONG)'CbsU'

#define GUARD_WORD 'draG'


extern PWCHAR GenericCompositeUSBDeviceString;


enum deviceState {
        STATE_INITIALIZED,
        STATE_STARTING,
        STATE_STARTED,
        STATE_START_FAILED,
        STATE_STOPPING,
        STATE_STOPPED,  // implies device was previously started successfully
        STATE_SUSPENDED,
        STATE_REMOVING,
        STATE_REMOVED
};


typedef struct PARENT_FDO_EXTENSION {

    enum deviceState    state;

    PDRIVER_OBJECT      driverObj;
    PDEVICE_OBJECT      pdo;
    PDEVICE_OBJECT      fdo;
    PDEVICE_OBJECT      topDevObj;

    /*
     *  Counter to keep driver from getting unloaded before all IO completes to us.
     */
    LONG                pendingActionCount;
    KEVENT              removeEvent;

    /*
     *  This buffer will hold a USB_CONFIGURATION_DESCRIPTOR plus
     *  the following interface descriptors.
     */
    PUSB_CONFIGURATION_DESCRIPTOR configDesc;
    PUSB_CONFIGURATION_DESCRIPTOR selectedConfigDesc;
    USBD_CONFIGURATION_HANDLE selectedConfigHandle;

    PUSBD_INTERFACE_LIST_ENTRY interfaceList;

    USB_DEVICE_DESCRIPTOR deviceDesc;

    /*
     *  The parent device has some number of functions.
     *  For each function, we create a PDO and store
     *  it in the deviceRelations array.
     */
    ULONG               numFunctions;
    PDEVICE_RELATIONS   deviceRelations;

    /*
     *  deviceCapabilities includes a
     *  table mapping system power states to device power states.
     */
    DEVICE_CAPABILITIES deviceCapabilities;

    PURB                dynamicNotifyUrb;

    PIRP                parentWaitWakeIrp;
    PIRP                currentSetPowerIrp;
    BOOLEAN             isWaitWakePending;
    LIST_ENTRY          functionWaitWakeIrpQueue;  // WW irps from function client drivers

    KSPIN_LOCK          parentFdoExtSpinLock;

    BOOLEAN             haveCSInterface;
    ULONG               CSInterfaceNumber;
	ULONG				CSChannelId;

    BOOLEAN             resetPortInProgress;
    LIST_ENTRY          pendingResetPortIrpQueue;

    BOOLEAN             cyclePortInProgress;
    LIST_ENTRY          pendingCyclePortIrpQueue;

    PIRP                   pendingIdleIrp;
    USB_IDLE_CALLBACK_INFO idleCallbackInfo;

    PMS_EXT_CONFIG_DESC msExtConfigDesc;

} PARENT_FDO_EXT, *PPARENT_FDO_EXT;


typedef struct FUNCTION_PDO_EXTENSION {

    ULONG               functionIndex;
    ULONG               baseInterfaceNumber;
    ULONG               numInterfaces;
    enum deviceState    state;

    PDEVICE_OBJECT      pdo;
    PPARENT_FDO_EXT     parentFdoExt;

    /*
     *  functionInterfaceList is a pointer into the parent's interfaceList array.
     */
    PUSBD_INTERFACE_LIST_ENTRY functionInterfaceList;

    PUSB_CONFIGURATION_DESCRIPTOR dynamicFunctionConfigDesc;

    USB_DEVICE_DESCRIPTOR functionDeviceDesc;

    KSPIN_LOCK functionPdoExtSpinLock;

    PIRP idleNotificationIrp;

} FUNCTION_PDO_EXT, *PFUNCTION_PDO_EXT;


typedef struct DEVICE_EXTENSION {

    ULONG signature;

    /*
     *  Does the associated device object represent
     *  the parent FDO that we attached to the device object
     *  we got from USBHUB
     *  (as opposed to the function PDO we created
     *   to represent a single function on that device) ?
     */
    BOOLEAN         isParentFdo;

    union {
        PARENT_FDO_EXT      parentFdoExt;
        FUNCTION_PDO_EXT    functionPdoExt;
    };

} DEVEXT, *PDEVEXT;


typedef struct _USB_REQUEST_TIMEOUT_CONTEXT {

    PKEVENT event;
    PLONG   lock;

} USB_REQUEST_TIMEOUT_CONTEXT, *PUSB_REQUEST_TIMEOUT_CONTEXT;


#define ALLOCPOOL(pooltype, size)   ExAllocatePoolWithTag(pooltype, size, USBCCGP_TAG)
#define FREEPOOL(ptr)               ExFreePool(ptr)

#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
#define MIN(a, b) (((a) <= (b)) ? (a) : (b))

#define POINTER_DISTANCE(ptr1, ptr2) (ULONG)(((PUCHAR)(ptr1))-((PUCHAR)(ptr2)))

//  Counting the byte count of an ascii string or wide char string
//
#define STRLEN( Length, p )\
    {\
    int i;\
    for ( i=0; (p)[i]; i++ );\
    Length = i*sizeof(*p);\
    }

/*
 *  We use this value, which is guaranteed to never be defined as a status by the kernel,
 *  as a default status code to indicate "do nothing and pass the irp down".
 */
#define NO_STATUS   0x80000000



NTSTATUS                    DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING UniRegistryPath);
NTSTATUS                    USBC_AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject);
VOID                        USBC_DriverUnload(IN PDRIVER_OBJECT DriverObject);
NTSTATUS                    USBC_Dispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS                    USBC_Create(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_Close(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_DeviceControl(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_SystemControl(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_InternalDeviceControl(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_PnP(PDEVEXT devExt, PIRP irp);
NTSTATUS                    USBC_PnpComplete(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context);
NTSTATUS                    USBC_Power(PDEVEXT devExt, PIRP irp);
NTSTATUS                    CreateStaticFunctionPDOs(PPARENT_FDO_EXT fdoExt);
NTSTATUS                    TryGetConfigDescriptor(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    GetConfigDescriptor(PPARENT_FDO_EXT fdoExt);
VOID                        PrepareParentFDOForRemove(PPARENT_FDO_EXT parentFdoExt);
VOID                        FreeParentFDOResources(PPARENT_FDO_EXT fdoExt);
PWCHAR                      BuildCompatibleIDs(IN PUCHAR CompatibleID, IN PUCHAR SubCompatibleID, IN UCHAR Class, IN UCHAR SubClass, IN UCHAR Protocol);
NTSTATUS                    QueryFunctionPdoID(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    QueryParentDeviceRelations(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
NTSTATUS                    QueryFunctionDeviceRelations(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    QueryFunctionCapabilities(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    HandleParentFdoPower(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
NTSTATUS                    HandleFunctionPdoPower(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
VOID                        ParentPowerRequestCompletion(IN PDEVICE_OBJECT devObj, IN UCHAR minorFunction, IN POWER_STATE powerState, IN PVOID context, IN PIO_STATUS_BLOCK ioStatus);
NTSTATUS                    StartParentFdo(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
NTSTATUS                    QueryFunctionCapabilities(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    ParentPdoPowerCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context);
NTSTATUS                    FunctionInternalDeviceControl(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    ParentInternalDeviceControl(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
NTSTATUS                    ParentResetOrCyclePort(PPARENT_FDO_EXT parentFdoExt, PIRP irp, ULONG ioControlCode);
NTSTATUS                    BuildFunctionConfigurationDescriptor(PFUNCTION_PDO_EXT functionPdoExt, PUCHAR buffer, ULONG bufferLength, PULONG bytesReturned);
VOID                        FreeFunctionPDOResources(PFUNCTION_PDO_EXT functionPdoExt);
NTSTATUS                    GetParentFdoCapabilities(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    CallDriverSyncCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
NTSTATUS                    CallDriverSync(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
NTSTATUS                    CallNextDriverSync(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
VOID                        IncrementPendingActionCount(PPARENT_FDO_EXT parentFdoExt);
VOID                        DecrementPendingActionCount(PPARENT_FDO_EXT parentFdoExt);
PWCHAR                      AppendInterfaceNumber(PWCHAR oldIDs, ULONG interfaceNum);
PVOID                       MemDup(PVOID dataPtr, ULONG length);
ULONG                       WStrLen(PWCHAR str);
ULONG                       WStrCpy(PWCHAR dest, PWCHAR src);
BOOLEAN                     WStrCompareN(PWCHAR str1, PWCHAR str2, ULONG maxChars);
NTSTATUS                    GetDeviceDescriptor(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    SubmitUrb(PPARENT_FDO_EXT parentFdoExt, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext);
NTSTATUS                    UrbFunctionSelectConfiguration(PFUNCTION_PDO_EXT functionPdoExt, PURB urb);
NTSTATUS                    UrbFunctionGetDescriptorFromDevice(PFUNCTION_PDO_EXT functionPdoExt, PURB urb);
PFUNCTION_PDO_EXT           FindFunctionByIndex(PPARENT_FDO_EXT parentFdoExt, ULONG functionIndex);
PUSBD_INTERFACE_LIST_ENTRY  GetFunctionInterfaceListBase(PPARENT_FDO_EXT parentFdoExt, ULONG functionIndex, PULONG numFunctionInterfaces);
PDEVICE_RELATIONS           CopyDeviceRelations(PDEVICE_RELATIONS deviceRelations);
PUSBD_INTERFACE_LIST_ENTRY  GetInterfaceList(PPARENT_FDO_EXT parentFdoExt, PUSB_CONFIGURATION_DESCRIPTOR configDesc);
VOID                        FreeInterfaceList(PPARENT_FDO_EXT parentFdoExt, BOOLEAN freeListItself);
NTSTATUS                    ParentSelectConfiguration(PPARENT_FDO_EXT parentFdoExt, PUSB_CONFIGURATION_DESCRIPTOR configDesc, PUSBD_INTERFACE_LIST_ENTRY interfaceList);
VOID                        ParentCloseConfiguration(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    GetStringDescriptor(PPARENT_FDO_EXT parentFdoExt, UCHAR stringIndex, LANGID langId, PUSB_STRING_DESCRIPTOR stringDesc, ULONG bufferLen);
NTSTATUS                    SetPdoRegistryParameter(IN PDEVICE_OBJECT PhysicalDeviceObject, IN PWCHAR KeyName, IN PVOID Data, IN ULONG DataLength, IN ULONG KeyType, IN ULONG DevInstKeyType);
NTSTATUS                    GetPdoRegistryParameter(IN PDEVICE_OBJECT PhysicalDeviceObject, IN PWCHAR ValueName, OUT PVOID Data, IN ULONG DataLength, OUT PULONG Type, OUT PULONG ActualDataLength);
NTSTATUS                    GetMsOsFeatureDescriptor(PPARENT_FDO_EXT ParentFdoExt, UCHAR Recipient, UCHAR InterfaceNumber, USHORT Index, PVOID DataBuffer, ULONG DataBufferLength, PULONG BytesReturned);
NTSTATUS                    GetMsExtendedConfigDescriptor(IN PPARENT_FDO_EXT ParentFdoExt);
BOOLEAN                     ValidateMsExtendedConfigDescriptor(IN PMS_EXT_CONFIG_DESC MsExtConfigDesc, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor);
NTSTATUS                    GetDeviceText(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
NTSTATUS                    EnqueueFunctionWaitWakeIrp(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp);
VOID                        FunctionWaitWakeIrpCancelRoutine(IN PDEVICE_OBJECT deviceObject, IN PIRP irp);
NTSTATUS                    SubmitParentWaitWakeIrp(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    ParentWaitWakeComplete(IN PDEVICE_OBJECT deviceObject, IN UCHAR minorFunction, IN POWER_STATE powerState, IN PVOID context, IN PIO_STATUS_BLOCK ioStatus);
VOID                        CompleteAllFunctionWaitWakeIrps(PPARENT_FDO_EXT parentFdoExt, NTSTATUS status);
VOID                        CompleteAllFunctionIdleIrps(PPARENT_FDO_EXT parentFdoExt, NTSTATUS status);
VOID                        InstallExtPropDesc(IN PFUNCTION_PDO_EXT FunctionPdoExt);
VOID                        InstallExtPropDescSections(PDEVICE_OBJECT DeviceObject, PMS_EXT_PROP_DESC pMsExtPropDesc);
NTSTATUS                    ParentDeviceControl(PPARENT_FDO_EXT parentFdoExt, PIRP irp);
VOID                        CompleteFunctionIdleNotification(PFUNCTION_PDO_EXT functionPdoExt);
VOID                        CheckParentIdle(PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    ParentSetD0(IN PPARENT_FDO_EXT parentFdoExt);
NTSTATUS                    USBC_SetContentId(IN PIRP irp, IN PVOID pKsProperty, IN PVOID pvData);
NTSTATUS                    RegQueryGenericCompositeUSBDeviceString(PWCHAR *GenericCompositeUSBDeviceString);