#ifndef _STREAM_H
#define _STREAM_H

#include <ntddk.h>
#include <windef.h>
#include <ks.h>

#define STREAMAPI __stdcall
#define STREAM_SYSTEM_TIME_MASK   ((STREAM_SYSTEM_TIME)0x00000001FFFFFFFF)

typedef enum {
  DebugLevelFatal = 0,
  DebugLevelError,
  DebugLevelWarning,
  DebugLevelInfo,
  DebugLevelTrace,
  DebugLevelVerbose,
  DebugLevelMaximum
} STREAM_DEBUG_LEVEL;


#if DBG

#define DebugPrint(x) StreamClassDebugPrint x
#define DEBUG_BREAKPOINT() DbgBreakPoint()
#define DEBUG_ASSERT(exp) \
            if ( !(exp) ) { \
                StreamClassDebugAssert( __FILE__, __LINE__, #exp, exp); \
            }
#else

#define DebugPrint(x)
#define DEBUG_BREAKPOINT()
#define DEBUG_ASSERT(exp)

#endif

typedef PHYSICAL_ADDRESS STREAM_PHYSICAL_ADDRESS, *PSTREAM_PHYSICAL_ADDRESS;
__GNU_EXTENSION typedef unsigned __int64 STREAM_SYSTEM_TIME, *PSTREAM_SYSTEM_TIME;
__GNU_EXTENSION typedef unsigned __int64 STREAM_TIMESTAMP, *PSTREAM_TIMESTAMP;

typedef enum {
  TIME_GET_STREAM_TIME,
  TIME_READ_ONBOARD_CLOCK,
  TIME_SET_ONBOARD_CLOCK
} TIME_FUNCTION;

typedef struct _HW_TIME_CONTEXT {
  struct _HW_DEVICE_EXTENSION *HwDeviceExtension;
  struct _HW_STREAM_OBJECT *HwStreamObject;
  TIME_FUNCTION Function;
  ULONGLONG Time;
  ULONGLONG SystemTime;
} HW_TIME_CONTEXT, *PHW_TIME_CONTEXT;

typedef struct _HW_EVENT_DESCRIPTOR {
  BOOLEAN Enable;
  PKSEVENT_ENTRY EventEntry;
  PKSEVENTDATA EventData;
  __GNU_EXTENSION union {
    struct _HW_STREAM_OBJECT * StreamObject;
    struct _HW_DEVICE_EXTENSION *DeviceExtension;
  };
  ULONG EnableEventSetIndex;
  PVOID HwInstanceExtension;
  ULONG Reserved;
} HW_EVENT_DESCRIPTOR, *PHW_EVENT_DESCRIPTOR;

struct _HW_STREAM_REQUEST_BLOCK;

typedef VOID (STREAMAPI * PHW_RECEIVE_STREAM_DATA_SRB) (IN struct _HW_STREAM_REQUEST_BLOCK * SRB);
typedef VOID (STREAMAPI * PHW_RECEIVE_STREAM_CONTROL_SRB) (IN struct _HW_STREAM_REQUEST_BLOCK  * SRB);
typedef NTSTATUS (STREAMAPI * PHW_EVENT_ROUTINE) (IN PHW_EVENT_DESCRIPTOR EventDescriptor);
typedef VOID (STREAMAPI * PHW_CLOCK_FUNCTION) (IN PHW_TIME_CONTEXT HwTimeContext);

typedef struct _HW_CLOCK_OBJECT {
  PHW_CLOCK_FUNCTION HwClockFunction;
  ULONG ClockSupportFlags;
  ULONG Reserved[2];
} HW_CLOCK_OBJECT, *PHW_CLOCK_OBJECT;

#define CLOCK_SUPPORT_CAN_SET_ONBOARD_CLOCK 0x1
#define CLOCK_SUPPORT_CAN_READ_ONBOARD_CLOCK 0x2
#define CLOCK_SUPPORT_CAN_RETURN_STREAM_TIME 0x4

typedef struct _HW_STREAM_OBJECT {
  ULONG           SizeOfThisPacket;
  ULONG           StreamNumber;
  PVOID           HwStreamExtension;
  PHW_RECEIVE_STREAM_DATA_SRB ReceiveDataPacket;
  PHW_RECEIVE_STREAM_CONTROL_SRB ReceiveControlPacket;
  HW_CLOCK_OBJECT HwClockObject;
  BOOLEAN         Dma;
  BOOLEAN         Pio;
  PVOID           HwDeviceExtension;
  ULONG    StreamHeaderMediaSpecific;
  ULONG    StreamHeaderWorkspace;
  BOOLEAN Allocator;
  PHW_EVENT_ROUTINE HwEventRoutine;
  ULONG Reserved[2];
} HW_STREAM_OBJECT, *PHW_STREAM_OBJECT;

typedef struct _HW_STREAM_HEADER {
  ULONG           NumberOfStreams;
  ULONG           SizeOfHwStreamInformation;
  ULONG           NumDevPropArrayEntries;
  PKSPROPERTY_SET DevicePropertiesArray;
  ULONG           NumDevEventArrayEntries;
  PKSEVENT_SET    DeviceEventsArray;
  PKSTOPOLOGY     Topology;
  PHW_EVENT_ROUTINE DeviceEventRoutine;
  LONG            NumDevMethodArrayEntries;
  PKSMETHOD_SET   DeviceMethodsArray;
} HW_STREAM_HEADER, *PHW_STREAM_HEADER;

typedef struct _HW_STREAM_INFORMATION {
  ULONG           NumberOfPossibleInstances;
  KSPIN_DATAFLOW  DataFlow;
  BOOLEAN         DataAccessible;
  ULONG           NumberOfFormatArrayEntries;
  PKSDATAFORMAT*  StreamFormatsArray;
  PVOID           ClassReserved[4];
  ULONG           NumStreamPropArrayEntries;
  PKSPROPERTY_SET StreamPropertiesArray;
  ULONG           NumStreamEventArrayEntries;
  PKSEVENT_SET    StreamEventsArray;
  GUID*                   Category;
  GUID*                   Name;
  ULONG                   MediumsCount;
  const KSPIN_MEDIUM*     Mediums;
  BOOLEAN         BridgeStream;
  ULONG Reserved[2];
} HW_STREAM_INFORMATION, *PHW_STREAM_INFORMATION;


typedef struct _HW_STREAM_DESCRIPTOR {
  HW_STREAM_HEADER StreamHeader;
  HW_STREAM_INFORMATION StreamInfo;
} HW_STREAM_DESCRIPTOR, *PHW_STREAM_DESCRIPTOR;

typedef struct _STREAM_TIME_REFERENCE {
  STREAM_TIMESTAMP CurrentOnboardClockValue;
  LARGE_INTEGER    OnboardClockFrequency;
  LARGE_INTEGER    CurrentSystemTime;
  ULONG Reserved[2];
} STREAM_TIME_REFERENCE, *PSTREAM_TIME_REFERENCE;

typedef struct _STREAM_DATA_INTERSECT_INFO {
  ULONG StreamNumber;
  PKSDATARANGE DataRange;
  PVOID   DataFormatBuffer;
  ULONG  SizeOfDataFormatBuffer;
} STREAM_DATA_INTERSECT_INFO, *PSTREAM_DATA_INTERSECT_INFO;

typedef struct _STREAM_PROPERTY_DESCRIPTOR {
  PKSPROPERTY     Property;
  ULONG           PropertySetID;
  PVOID           PropertyInfo;
  ULONG           PropertyInputSize;
  ULONG           PropertyOutputSize;
} STREAM_PROPERTY_DESCRIPTOR, *PSTREAM_PROPERTY_DESCRIPTOR;

typedef struct _STREAM_METHOD_DESCRIPTOR {
  ULONG		MethodSetID;
  PKSMETHOD	Method;
  PVOID		MethodInfo;
  LONG		MethodInputSize;
  LONG		MethodOutputSize;
} STREAM_METHOD_DESCRIPTOR, *PSTREAM_METHOD_DESCRIPTOR;

#define STREAM_REQUEST_BLOCK_SIZE sizeof(STREAM_REQUEST_BLOCK)

typedef enum _SRB_COMMAND {
  SRB_READ_DATA,
  SRB_WRITE_DATA, 
  SRB_GET_STREAM_STATE,
  SRB_SET_STREAM_STATE,
  SRB_SET_STREAM_PROPERTY,
  SRB_GET_STREAM_PROPERTY,
  SRB_OPEN_MASTER_CLOCK,

  SRB_INDICATE_MASTER_CLOCK,
  SRB_UNKNOWN_STREAM_COMMAND,
  SRB_SET_STREAM_RATE,
  SRB_PROPOSE_DATA_FORMAT,
  SRB_CLOSE_MASTER_CLOCK,
  SRB_PROPOSE_STREAM_RATE,
  SRB_SET_DATA_FORMAT,
  SRB_GET_DATA_FORMAT,
  SRB_BEGIN_FLUSH,
  SRB_END_FLUSH,

  SRB_GET_STREAM_INFO = 0x100,
  SRB_OPEN_STREAM,
  SRB_CLOSE_STREAM,
  SRB_OPEN_DEVICE_INSTANCE,
  SRB_CLOSE_DEVICE_INSTANCE,
  SRB_GET_DEVICE_PROPERTY,
  SRB_SET_DEVICE_PROPERTY,
  SRB_INITIALIZE_DEVICE,
  SRB_CHANGE_POWER_STATE,
  SRB_UNINITIALIZE_DEVICE,
  SRB_UNKNOWN_DEVICE_COMMAND,
  SRB_PAGING_OUT_DRIVER,
  SRB_GET_DATA_INTERSECTION,
  SRB_INITIALIZATION_COMPLETE,
  SRB_SURPRISE_REMOVAL

#if (NTDDI_VERSION >= NTDDI_WINXP)
 ,SRB_DEVICE_METHOD
 ,SRB_STREAM_METHOD
#if ( (NTDDI_VERSION >= NTDDI_WINXPSP2) && (NTDDI_VERSION < NTDDI_WS03) ) || (NTDDI_VERSION >= NTDDI_WS03SP1)
 ,SRB_NOTIFY_IDLE_STATE
#endif
#endif
} SRB_COMMAND;

typedef struct {
  PHYSICAL_ADDRESS    PhysicalAddress;
  ULONG               Length;
} KSSCATTER_GATHER, *PKSSCATTER_GATHER;


typedef struct _HW_STREAM_REQUEST_BLOCK {
  ULONG           SizeOfThisPacket;
  SRB_COMMAND     Command;
  NTSTATUS        Status;
  PHW_STREAM_OBJECT StreamObject;
  PVOID           HwDeviceExtension;
  PVOID           SRBExtension;

  union _CommandData {
    PKSSTREAM_HEADER DataBufferArray;
    PHW_STREAM_DESCRIPTOR StreamBuffer;
    KSSTATE         StreamState;
    PSTREAM_TIME_REFERENCE TimeReference;
    PSTREAM_PROPERTY_DESCRIPTOR PropertyInfo;
    PKSDATAFORMAT   OpenFormat;
    struct _PORT_CONFIGURATION_INFORMATION *ConfigInfo;
    HANDLE          MasterClockHandle;
    DEVICE_POWER_STATE DeviceState;
    PSTREAM_DATA_INTERSECT_INFO IntersectInfo;

#if (NTDDI_VERSION >= NTDDI_WINXP)
    PVOID	MethodInfo;
    LONG	FilterTypeIndex;
#if ( (NTDDI_VERSION >= NTDDI_WINXPSP2) && (NTDDI_VERSION < NTDDI_WS03) ) || (NTDDI_VERSION >= NTDDI_WS03SP1)
    BOOLEAN Idle;
#endif
#endif
  } CommandData;

  ULONG NumberOfBuffers;
  ULONG           TimeoutCounter;
  ULONG           TimeoutOriginal;
  struct _HW_STREAM_REQUEST_BLOCK *NextSRB;

  PIRP            Irp;
  ULONG           Flags;
  PVOID       HwInstanceExtension;

  __GNU_EXTENSION union {
    ULONG         NumberOfBytesToTransfer;
    ULONG         ActualBytesTransferred;
  };

  PKSSCATTER_GATHER ScatterGatherBuffer;
  ULONG           NumberOfPhysicalPages;
  ULONG           NumberOfScatterGatherElements;
  ULONG Reserved[1];
} HW_STREAM_REQUEST_BLOCK, *PHW_STREAM_REQUEST_BLOCK;

#define SRB_HW_FLAGS_DATA_TRANSFER  0x01
#define SRB_HW_FLAGS_STREAM_REQUEST 0x2

typedef enum {
  PerRequestExtension,
  DmaBuffer,
  SRBDataBuffer
} STREAM_BUFFER_TYPE;

typedef struct _ACCESS_RANGE {
  STREAM_PHYSICAL_ADDRESS RangeStart;
  ULONG           RangeLength;
  BOOLEAN         RangeInMemory;
  ULONG           Reserved;
} ACCESS_RANGE, *PACCESS_RANGE;

typedef struct _PORT_CONFIGURATION_INFORMATION {
  ULONG           SizeOfThisPacket;
  PVOID           HwDeviceExtension;
  PDEVICE_OBJECT  ClassDeviceObject;
  PDEVICE_OBJECT  PhysicalDeviceObject;
  ULONG           SystemIoBusNumber;
  INTERFACE_TYPE  AdapterInterfaceType;
  ULONG           BusInterruptLevel;
  ULONG           BusInterruptVector;
  KINTERRUPT_MODE InterruptMode;
  ULONG           DmaChannel;
  ULONG           NumberOfAccessRanges;
  PACCESS_RANGE   AccessRanges;
  ULONG           StreamDescriptorSize;
  PIRP            Irp;
  PKINTERRUPT  InterruptObject;
  PADAPTER_OBJECT  DmaAdapterObject;
  PDEVICE_OBJECT  RealPhysicalDeviceObject;
  ULONG Reserved[1];
} PORT_CONFIGURATION_INFORMATION, *PPORT_CONFIGURATION_INFORMATION;

typedef VOID (STREAMAPI * PHW_RECEIVE_DEVICE_SRB) (IN PHW_STREAM_REQUEST_BLOCK SRB);
typedef VOID (STREAMAPI * PHW_CANCEL_SRB) (IN PHW_STREAM_REQUEST_BLOCK SRB);
typedef VOID (STREAMAPI * PHW_REQUEST_TIMEOUT_HANDLER) (IN PHW_STREAM_REQUEST_BLOCK SRB);
typedef BOOLEAN (STREAMAPI * PHW_INTERRUPT) (IN PVOID DeviceExtension);
typedef VOID (STREAMAPI * PHW_TIMER_ROUTINE) (IN PVOID Context);
typedef VOID (STREAMAPI * PHW_PRIORITY_ROUTINE) (IN PVOID Context);
typedef VOID (STREAMAPI * PHW_QUERY_CLOCK_ROUTINE) (IN PHW_TIME_CONTEXT TimeContext);
typedef BOOLEAN (STREAMAPI * PHW_RESET_ADAPTER) (IN PVOID DeviceExtension);

typedef enum _STREAM_MINIDRIVER_STREAM_NOTIFICATION_TYPE {
  ReadyForNextStreamDataRequest,
  ReadyForNextStreamControlRequest,
  HardwareStarved,
  StreamRequestComplete,
  SignalMultipleStreamEvents,
  SignalStreamEvent,
  DeleteStreamEvent,
  StreamNotificationMaximum
} STREAM_MINIDRIVER_STREAM_NOTIFICATION_TYPE, *PSTREAM_MINIDRIVER_STREAM_NOTIFICATION_TYPE;

typedef enum _STREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE {
  ReadyForNextDeviceRequest,
  DeviceRequestComplete,
  SignalMultipleDeviceEvents,
  SignalDeviceEvent,
  DeleteDeviceEvent,
#if (NTDDI_VERSION >= NTDDI_WINXP)
  SignalMultipleDeviceInstanceEvents,
#endif
  DeviceNotificationMaximum
} STREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE, *PSTREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE;

#define STREAM_CLASS_VERSION_20 0x0200

typedef struct _HW_INITIALIZATION_DATA {
#if (NTDDI_VERSION >= NTDDI_WINXP)
  __GNU_EXTENSION union {
    ULONG         HwInitializationDataSize;
    __GNU_EXTENSION struct {
      USHORT      SizeOfThisPacket;
      USHORT      StreamClassVersion;
    };
  };
#else
  ULONG           HwInitializationDataSize;
#endif /* NTDDI_VERSION >= NTDDI_WINXP */

  PHW_INTERRUPT   HwInterrupt;
  PHW_RECEIVE_DEVICE_SRB HwReceivePacket;
  PHW_CANCEL_SRB  HwCancelPacket;
  PHW_REQUEST_TIMEOUT_HANDLER HwRequestTimeoutHandler;
  ULONG           DeviceExtensionSize;
  ULONG           PerRequestExtensionSize;
  ULONG           PerStreamExtensionSize;
  ULONG           FilterInstanceExtensionSize;
  BOOLEAN         BusMasterDMA;
  BOOLEAN         Dma24BitAddresses;
  ULONG           BufferAlignment;
  BOOLEAN         TurnOffSynchronization;
  ULONG           DmaBufferSize;

#if (NTDDI_VERSION >= NTDDI_WINXP)
  ULONG		NumNameExtensions;
  PWCHAR	*NameExtensionArray;
#else
  ULONG Reserved[2];
#endif
} HW_INITIALIZATION_DATA, *PHW_INITIALIZATION_DATA;

typedef enum _STREAM_PRIORITY {
  High,
  Dispatch,
  Low,
  LowToHigh
} STREAM_PRIORITY, *PSTREAM_PRIORITY;


VOID
StreamClassAbortOutstandingRequests(
    IN PVOID HwDeviceExtension,
    IN PHW_STREAM_OBJECT HwStreamObject,
    IN NTSTATUS Status
);

VOID
STREAMAPI 
StreamClassCallAtNewPriority(
    IN PHW_STREAM_OBJECT  StreamObject,
    IN PVOID  HwDeviceExtension,
    IN STREAM_PRIORITY  Priority,
    IN PHW_PRIORITY_ROUTINE  PriorityRoutine,
    IN PVOID  Context
    );

VOID
STREAMAPI
StreamClassCompleteRequestAndMarkQueueReady(
    IN PHW_STREAM_REQUEST_BLOCK Srb
);

VOID
STREAMAPI
StreamClassDebugAssert(
    IN PCHAR File,
    IN ULONG Line,
    IN PCHAR AssertText,
    IN ULONG AssertValue
);

VOID
__cdecl
StreamClassDebugPrint(
    IN STREAM_DEBUG_LEVEL DebugPrintLevel,
    IN PCCHAR DebugMessage,
    ...
);

VOID
__cdecl
StreamClassDeviceNotification(
    IN STREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE NotificationType,
    IN PVOID HwDeviceExtension,
    IN PHW_STREAM_REQUEST_BLOCK  pSrb,
    IN PKSEVENT_ENTRY  EventEntry,
    IN GUID  *EventSet,
    IN ULONG  EventId
);

VOID
STREAMAPI
StreamClassFilterReenumerateStreams(
    IN PVOID HwInstanceExtension,
    IN ULONG StreamDescriptorSize
);

PVOID
STREAMAPI
StreamClassGetDmaBuffer(
    IN PVOID HwDeviceExtension
);


PKSEVENT_ENTRY
StreamClassGetNextEvent(
    IN PVOID HwInstanceExtension_OR_HwDeviceExtension,
    IN PHW_STREAM_OBJECT HwStreamObject,
    IN GUID * EventGuid,
    IN ULONG EventItem,
    IN PKSEVENT_ENTRY CurrentEvent
);

STREAM_PHYSICAL_ADDRESS
STREAMAPI
StreamClassGetPhysicalAddress(
    IN PVOID HwDeviceExtension,
    IN PHW_STREAM_REQUEST_BLOCK HwSRB,
    IN PVOID VirtualAddress,
    IN STREAM_BUFFER_TYPE Type,
    IN ULONG * Length
);

VOID
StreamClassQueryMasterClock(
    IN PHW_STREAM_OBJECT HwStreamObject,
    IN HANDLE MasterClockHandle,
    IN TIME_FUNCTION TimeFunction,
    IN PHW_QUERY_CLOCK_ROUTINE ClockCallbackRoutine
);

VOID
STREAMAPI
StreamClassQueryMasterClockSync(
    IN HANDLE MasterClockHandle,
    IN PHW_TIME_CONTEXT TimeContext
);

BOOLEAN
STREAMAPI
StreamClassReadWriteConfig( 
    IN PVOID HwDeviceExtension,
    IN BOOLEAN Read,
    IN PVOID Buffer,
    IN ULONG Offset,
    IN ULONG Length
);

VOID
STREAMAPI
StreamClassReenumerateStreams(
    IN PVOID HwDeviceExtension,
    IN ULONG StreamDescriptorSize
);

NTSTATUS
STREAMAPI
StreamClassRegisterAdapter(
    IN PVOID Argument1,
    IN PVOID Argument2,
    IN PHW_INITIALIZATION_DATA HwInitializationData
);

#define StreamClassRegisterMinidriver StreamClassRegisterAdapter

NTSTATUS
StreamClassRegisterFilterWithNoKSPins( 
    IN PDEVICE_OBJECT   DeviceObject,
    IN const GUID     * InterfaceClassGUID,
    IN ULONG            PinCount,
    IN BOOLEAN * PinDirection,
    IN KSPIN_MEDIUM * MediumList,
    IN GUID * CategoryList
);

VOID
STREAMAPI
StreamClassScheduleTimer(
    IN PHW_STREAM_OBJECT StreamObject,
    IN PVOID HwDeviceExtension,
    IN ULONG NumberOfMicroseconds,
    IN PHW_TIMER_ROUTINE TimerRoutine,
    IN PVOID Context
);

VOID
__cdecl
StreamClassStreamNotification(
    IN STREAM_MINIDRIVER_STREAM_NOTIFICATION_TYPE NotificationType,
    IN PHW_STREAM_OBJECT StreamObject,
    IN ...
);

#endif /* _STREAM_H */

