/*++

Copyright (c) 1997-2000 Microsoft Corporation

Module Name:

    local.h

Abstract:

    This header declares the stuctures and function prototypes shared between
    the various modules.

Author:

    Andy Thornton (andrewth) 20-Oct-97

Revision History:

--*/


#if !defined(_LOCAL_)
#define _LOCAL_

#include <ntddk.h>
#include <arbiter.h>
#include <wdmguid.h>
//#include <initguid.h>
#include <mf.h>

#include "msg.h"
#include "debug.h"

//
// --- Constants ---
//

#define MF_CM_RESOURCE_VERSION          1
#define MF_CM_RESOURCE_REVISION         1
#define MF_ARBITER_INTERFACE_VERSION    1
#define MF_TRANSLATOR_INTERFACE_VERSION 1

//
// These must be updated if any new PNP or PO irps are added
//

#define IRP_MN_PNP_MAXIMUM_FUNCTION IRP_MN_QUERY_LEGACY_BUS_INFORMATION
#define IRP_MN_PO_MAXIMUM_FUNCTION  IRP_MN_QUERY_POWER

//
// Pool Tags
//

#define MF_POOL_TAG                     '  fM'
#define MF_RESOURCE_MAP_TAG             'MRfM'
#define MF_VARYING_MAP_TAG              'MVfM'
#define MF_CHILD_LIST_TAG               'LCfM'
#define MF_DEVICE_ID_TAG                'IDfM'
#define MF_INSTANCE_ID_TAG              'IIfM'
#define MF_CHILD_REQUIREMENTS_TAG       'QCfM'
#define MF_CHILD_RESOURCE_TAG           'RCfM'
#define MF_HARDWARE_COMPATIBLE_ID_TAG   'IHfM'
#define MF_PARENTS_RESOURCE_TAG         'RPfM'
#define MF_PARENTS_REQUIREMENTS_TAG     'QPfM'
#define MF_BUS_RELATIONS_TAG            'RBfM'
#define MF_TARGET_RELATIONS_TAG         'RTfM'
#define MF_REQUIREMENTS_INDEX_TAG       'IRfM'
#define MF_ARBITER_TAG                  'rAfM'

//
// Device state flags
//

#define MF_DEVICE_STARTED               0x00000001
#define MF_DEVICE_REMOVED               0x00000002
#define MF_DEVICE_ENUMERATED            0x00000004
#define MF_DEVICE_REMOVE_PENDING        0x00000008 /* DEPRECATED */
#define MF_DEVICE_STOP_PENDING          0x00000010 /* DEPRECATED */
#define MF_DEVICE_CAPABILITIES_CAPTURED 0x00000020 /* DEPRECATED */
#define MF_DEVICE_REQUIREMENTS_CAPTURED 0x00000040 /* DEPRECATED */
#define MF_DEVICE_DELETED               0x00000080
#define MF_DEVICE_SURPRISE_REMOVED      0x00000100

//
// Flags to MfGetRegistryValue
//

#define MF_GETREG_SZ_TO_MULTI_SZ    0x00000001

//
// --- Type definitions ---
//

typedef enum _MF_OBJECT_TYPE {
    MfPhysicalDeviceObject   = 'dPfM',
    MfFunctionalDeviceObject = 'dFfM'
} MF_OBJECT_TYPE;

typedef
NTSTATUS
(*PMF_DISPATCH)(
    IN PIRP Irp,
    IN PVOID Extension,
    IN PIO_STACK_LOCATION IrpStack
    );

typedef ULONG Mf_MSG_ID;

//
// Structures for storing the resource distributions
//

typedef struct _MF_ARBITER {

    //
    // List of arbiters
    //
    LIST_ENTRY ListEntry;

    //
    // The resource this arbiter arbitrates
    //
    CM_RESOURCE_TYPE Type;

    //
    // The arbiter instance
    //
    ARBITER_INSTANCE Instance;

} MF_ARBITER, *PMF_ARBITER;



typedef struct _MF_COMMON_EXTENSION {

    //
    // Type of device this is
    //
    MF_OBJECT_TYPE Type;

    //
    // Dispatch tables for Pnp and Power Irps.
    //
    PMF_DISPATCH *PnpDispatchTable;
    PMF_DISPATCH *PoDispatchTable;

    //
    // Flags to indicate the device's current state (use MF_DEVICE_*)
    //
    ULONG DeviceState;

    ULONG PagingCount;
    ULONG HibernationCount;
    ULONG DumpCount;

    //
    // The power state of the device
    //
    DEVICE_POWER_STATE PowerState;

} MF_COMMON_EXTENSION, *PMF_COMMON_EXTENSION;

typedef struct _MF_CHILD_EXTENSION *PMF_CHILD_EXTENSION;
typedef struct _MF_PARENT_EXTENSION *PMF_PARENT_EXTENSION;

typedef struct _MF_CHILD_EXTENSION {

    //
    // The common extension
    //
    MF_COMMON_EXTENSION Common;

    //
    // Various Flags
    //
    ULONG Flags;

    //
    // Backpointer to the device object we are are the extension of
    //
    PDEVICE_OBJECT Self;

    //
    // The FDO who enumerated us
    //
    PMF_PARENT_EXTENSION Parent;

    //
    // Other children enumerated by the same FDO
    //
    LIST_ENTRY ListEntry;

    //
    // The pnp device state of the device
    //
    PNP_DEVICE_STATE PnpDeviceState;

    //
    // The information about this device
    //
    MF_DEVICE_INFO Info;

} MF_CHILD_EXTENSION, *PMF_CHILD_EXTENSION;


typedef struct _MF_PARENT_EXTENSION {

    //
    // The common extension
    //
    MF_COMMON_EXTENSION Common;

    //
    // Backpointer to the device object of whom we are the extension
    //
    PDEVICE_OBJECT Self;

    //
    // The PDO for the multi-function device
    //
    PDEVICE_OBJECT PhysicalDeviceObject;

    //
    // Lock for the children database
    //
    KEVENT ChildrenLock;

    //
    // List of children enumerated by this device
    //
    LIST_ENTRY Children;

    //
    // The next device in the stack who we should send our IRPs down to
    //
    PDEVICE_OBJECT AttachedDevice;

    //
    // The resources with which the parent was stated
    //
    PCM_RESOURCE_LIST ResourceList;
    PCM_RESOURCE_LIST TranslatedResourceList;

    //
    // The device and instance ID's of our parent
    //
    UNICODE_STRING DeviceID;
    UNICODE_STRING InstanceID;

    //
    // The already instantiated arbiters for this device
    //
    LIST_ENTRY Arbiters;

    //
    // If we had to traverse the children in order to determine what
    // the lowest power state the parent can go to, then the
    // synchronization of the children list would become extremely
    // complicated.
    //
    // Instead, have a spinlock protected data structure consisting of
    // an array of device power states.  Each element of the array is
    // a count of how many children are in that power state.  
    //

    KSPIN_LOCK PowerLock;
    LONG ChildrenPowerReferences[PowerDeviceMaximum];

    //
    // Remove lock.  Used to prevent the FDO from being removed while
    // other operations are digging around in the extension.
    //

    IO_REMOVE_LOCK RemoveLock;

} MF_PARENT_EXTENSION, *PMF_PARENT_EXTENSION;

//
// A list of MF_CHILD_LIST_ENTRYs is returned by MfEnumerate
//

typedef struct _MF_CHILD_LIST_ENTRY {
    LIST_ENTRY ListEntry;
    MF_DEVICE_INFO Info;
} MF_CHILD_LIST_ENTRY, *PMF_CHILD_LIST_ENTRY;

//
// Registry structure - from our friends in Win9x so it must be byte aligned
//

#include <pshpack1.h>

typedef struct _MF_REGISTRY_VARYING_RESOURCE_MAP {

    UCHAR ResourceIndex; // Win9x BYTE
    ULONG Offset;
    ULONG Size;

} MF_REGISTRY_VARYING_RESOURCE_MAP, *PMF_REGISTRY_VARYING_RESOURCE_MAP;

#include <poppack.h>

typedef
NTSTATUS
(*PMF_REQUIREMENT_FROM_RESOURCE)(
    IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource,
    OUT PIO_RESOURCE_DESCRIPTOR Requirement
    );

typedef
NTSTATUS
(*PMF_UPDATE_RESOURCE)(
    IN OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource,
    IN ULONGLONG Start,
    IN ULONG Length
    );

typedef struct _MF_RESOURCE_TYPE {

    CM_RESOURCE_TYPE Type;
    PARBITER_UNPACK_REQUIREMENT UnpackRequirement;
    PARBITER_PACK_RESOURCE PackResource;
    PARBITER_UNPACK_RESOURCE UnpackResource;
    PMF_REQUIREMENT_FROM_RESOURCE RequirementFromResource;
    PMF_UPDATE_RESOURCE UpdateResource;

} MF_RESOURCE_TYPE, *PMF_RESOURCE_TYPE;

typedef struct _MF_POWER_COMPLETION_CONTEXT {

    //
    // Event that will be set when the operation is complete
    //
    KEVENT Event;

    //
    // The status of the completed operation
    //
    NTSTATUS Status;

} MF_POWER_COMPLETION_CONTEXT, *PMF_POWER_COMPLETION_CONTEXT;


//
// --- Globals ---
//

extern PDRIVER_OBJECT MfDriverObject;

//
// --- Function prototypes ---
//

//
// arbiter.c
//

NTSTATUS
MfInitializeArbiters(
    IN PMF_PARENT_EXTENSION Parent
    );

//
// common.c
//

NTSTATUS
MfDeviceUsageNotificationCommon(
    IN PIRP Irp,
    IN PMF_COMMON_EXTENSION Common,
    IN PIO_STACK_LOCATION IrpStack
    );

//
// dispatch.c
//

NTSTATUS
MfAddDevice(
    IN PDRIVER_OBJECT  DriverObject,
    IN PDEVICE_OBJECT  PhysicalDeviceObject
    );

NTSTATUS
MfDispatchPnp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS
MfDispatchPower(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS
MfIrpNotSupported(
    IN PIRP Irp,
    IN PVOID Extension,
    IN PIO_STACK_LOCATION IrpStack
    );

NTSTATUS
MfForwardIrpToParent(
    IN PIRP Irp,
    IN PMF_CHILD_EXTENSION Extension,
    IN PIO_STACK_LOCATION IrpStack
    );
    
NTSTATUS
MfDispatchNop(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

//
// enum.c
//

NTSTATUS
MfEnumerate(
    IN PMF_PARENT_EXTENSION Parent
    );

NTSTATUS
MfBuildDeviceID(
    IN PMF_PARENT_EXTENSION Parent,
    OUT PWSTR *DeviceID
    );

NTSTATUS
MfBuildInstanceID(
    IN PMF_CHILD_EXTENSION Child,
    OUT PWSTR *InstanceID
    );

NTSTATUS
MfBuildChildRequirements(
    IN PMF_CHILD_EXTENSION Child,
    OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList
    );

//
// fdo.c
//

NTSTATUS
MfDispatchPnpFdo(
    IN PDEVICE_OBJECT DeviceObject,
    IN PMF_PARENT_EXTENSION Parent,
    IN PIO_STACK_LOCATION IrpStack,
    IN OUT PIRP Irp
    );

NTSTATUS
MfDispatchPowerFdo(
    IN PDEVICE_OBJECT DeviceObject,
    IN PMF_PARENT_EXTENSION Parent,
    IN PIO_STACK_LOCATION IrpStack,
    IN OUT PIRP Irp
    );

NTSTATUS
MfCreateFdo(
    PDEVICE_OBJECT *Fdo
    );

VOID
MfAcquireChildrenLock(
    IN PMF_PARENT_EXTENSION Parent
    );

VOID
MfReleaseChildrenLock(
    IN PMF_PARENT_EXTENSION Parent
    );

//
// init.c
//

//
// pdo.c
//

NTSTATUS
MfDispatchPnpPdo(
    IN PDEVICE_OBJECT DeviceObject,
    IN PMF_CHILD_EXTENSION Parent,
    IN PIO_STACK_LOCATION IrpStack,
    IN OUT PIRP Irp
    );

NTSTATUS
MfDispatchPowerPdo(
    IN PDEVICE_OBJECT DeviceObject,
    IN PMF_CHILD_EXTENSION Parent,
    IN PIO_STACK_LOCATION IrpStack,
    IN OUT PIRP Irp
    );

NTSTATUS
MfCreatePdo(
    IN PMF_PARENT_EXTENSION Parent,
    OUT PDEVICE_OBJECT *PhysicalDeviceObject
    );

VOID
MfDeletePdo(
    IN PMF_CHILD_EXTENSION Child
    );

//
// resource.c
//

PMF_RESOURCE_TYPE
MfFindResourceType(
    IN CM_RESOURCE_TYPE Type
    );

//
// utils.c
//

NTSTATUS
MfGetSubkeyByIndex(
    IN HANDLE ParentHandle,
    IN ULONG Index,
    IN ACCESS_MASK Access,
    OUT PHANDLE ChildHandle,
    OUT PUNICODE_STRING Name
    );

VOID
MfInitCommonExtension(
    IN OUT PMF_COMMON_EXTENSION Common,
    IN MF_OBJECT_TYPE Type
    );

VOID
MfFreeDeviceInfo(
    PMF_DEVICE_INFO Info
    );

NTSTATUS
MfGetRegistryValue(
    IN HANDLE Handle,
    IN PWSTR Name,
    IN ULONG Type,
    IN ULONG Flags,
    IN OUT PULONG DataLength,
    IN OUT PVOID *Data OPTIONAL
    );

NTSTATUS
MfSendPnpIrp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIO_STACK_LOCATION Location,
    OUT PULONG_PTR Information OPTIONAL
    );

NTSTATUS
MfSendSetPowerIrp(
    IN PDEVICE_OBJECT Target,
    IN POWER_STATE State
    );

DEVICE_POWER_STATE
MfUpdateChildrenPowerReferences(
    IN PMF_PARENT_EXTENSION Parent,
    IN DEVICE_POWER_STATE PreviousPowerState,
    IN DEVICE_POWER_STATE NewPowerState
    );

NTSTATUS
MfUpdateParentPowerState(
    IN PMF_PARENT_EXTENSION Parent,
    IN DEVICE_POWER_STATE TargetPowerState
    );

//
// --- Macros ---
//

#define IS_FDO(Extension) \
    (((PMF_COMMON_EXTENSION)Extension)->Type == MfFunctionalDeviceObject)

#define MfCompareGuid(a,b)                                         \
    (RtlEqualMemory((PVOID)(a), (PVOID)(b), sizeof(GUID)))

//
// Control macro (used like a for loop) which iterates over all entries in
// a standard doubly linked list.  Head is the list head and the entries are of
// type Type.  A member called ListEntry is assumed to be the LIST_ENTRY
// structure linking the entries together.  Current contains a pointer to each
// entry in turn.
//
#define FOR_ALL_IN_LIST(Type, Head, Current)                            \
    for((Current) = CONTAINING_RECORD((Head)->Flink, Type, ListEntry);  \
       (Head) != &(Current)->ListEntry;                                 \
       (Current) = CONTAINING_RECORD((Current)->ListEntry.Flink,        \
                                     Type,                              \
                                     ListEntry)                         \
       )

#define FOR_ALL_IN_LIST_SAFE(Type, Head, Current, Next)                 \
    for((Current) = CONTAINING_RECORD((Head)->Flink, Type, ListEntry),  \
            (Next) = CONTAINING_RECORD((Current)->ListEntry.Flink,      \
                                       Type, ListEntry);                \
       (Head) != &(Current)->ListEntry;                                 \
       (Current) = (Next),                                              \
            (Next) = CONTAINING_RECORD((Current)->ListEntry.Flink,      \
                                     Type, ListEntry)                   \
       )

//
// Similar to the above only iteration is over an array of length _Size.
//
#define FOR_ALL_IN_ARRAY(_Array, _Size, _Current)                       \
    for ( (_Current) = (_Array);                                        \
          (_Current) < (_Array) + (_Size);                              \
          (_Current)++ )

//
// FOR_ALL_CM_DESCRIPTORS(
//      IN PCM_RESOURCE_LIST _ResList,
//      OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR _Descriptor
//      )
//
//  Iterates over the resource descriptors in a CM_RESOURCE_LIST of Count 1
//
#define FOR_ALL_CM_DESCRIPTORS(_ResList, _Descriptor)               \
    ASSERT((_ResList)->Count == 1);                                 \
    FOR_ALL_IN_ARRAY(                                               \
        (_ResList)->List[0].PartialResourceList.PartialDescriptors, \
        (_ResList)->List[0].PartialResourceList.Count,              \
        (_Descriptor)                                               \
        )

//
// BOOLEAN
// IS_ARBITRATED_RESOURCE(
//      IN CM_RESOURCE_TYPE _Resource
//      )
//
// If the top bit of the resource type (when viewed as a UCHAR) is set
// then the resource is nonarbitrated.
//
#define IS_ARBITRATED_RESOURCE(_Resource)                           \
    (!(((UCHAR)(_Resource)) & 0x80) &&                              \
     !(((UCHAR)(_Resource)) == 0x00))

#define END_OF_RANGE(_Start, _Length)                               \
    ((_Start)+(_Length)-1)

#endif // !defined(_LOCAL_)