/*++

Copyright (c) Microsoft Corporation. All rights reserved.


Module Name:

    rt.h

Abstract:

    This is the public include file for realtime executive (rt.sys) clients.

Author:

    Joseph Ballantyne

Environment:

    Kernel Mode

Revision History:

--*/




#ifdef __cplusplus
extern "C" {
#endif


// The following values can be ORed together and the result passed as the Flags argument
// to the RtCreateThread and RtAdjustCpuLoad routines.

#define CPUCYCLES		0x10000
#define INSTRUCTIONS	0x20000

#define USESFLOAT		0x00001
#define USESMMX			0x00002


// These should be used when calculating the desired period and duration to be
// passed to RtCreateThread and RtAdjustCpuLoad.

#define WEEK 604800000000000000I64
#define DAY   86400000000000000I64
#define HOUR   3600000000000000I64
#define MIN      60000000000000I64
#define SEC       1000000000000I64
#define MSEC         1000000000I64
#define USEC            1000000I64
#define NSEC               1000I64
#define PSEC                  1I64


#define X86 1


#define INTEL 1
#define AMD 2



typedef struct {
	ULONG ProcessorCount;	// Number of CPUs in the system.
	ULONG CpuArchitecture;	// Architecture of CPU, currently always X86==1
	ULONG CpuManufacturer;	// Manufacturer ID, Intel==1, AMD==2
	ULONG CpuFamily;		// CPU Family as reported by cpuid instruction.  0x0-0xf
	ULONG CpuModel;			// CPU Model as reported by cpuid instruction.  0x0-0xf
	ULONG CpuStepping;		// CPU Stepping as reported by cpuid instruction.  0x0-0xf
	ULONGLONG CpuFeatures;	// CPU features as reported by cpuid instruction.
	ULONGLONG CpuExtendedFeatures;	// AMD extended features.  (Not implemented.)  Always 0.
	ULONGLONG ProcessorID[2];		// Processor Unique ID.  If enabled.
	ULONG CpuCyclesPerMsec;			// Number of cpu cycles per MSEC.
	ULONG SystemBusCyclesPerMsec;	// Number of system bus cycles per MSEC.
	ULONG ReservedCpuPerMsec;		// Total cpu time reserved per ms by existing rt threads. (in picoseconds)
	ULONG UsedCpuPerMsec;			// Estimate of cpu time used per ms by existing rt threads. (in picoseconds)
	ULONG AvailableCpuPerMsec;		// Cpu time available per ms for allocation to new rt threads. (in picoseconds)
	} SystemInfo;



// The following realtime thread statistics are updated just before control is
// passed by the realtime executive to the realtime thread.  Everytime a realtime
// thread is being switched in, these statistics are updated before control is transfered.
// This means the statistics will change over time, but not while a realtime
// thread is running between thread switches.

#pragma pack(push,2)

typedef struct threadstats {
	ULONGLONG Period;		// Period as passed to RtCreateThread or latest RtAdjustCpuLoad call.
	ULONGLONG Duration;		// Duration from RtCreateThread or latest RtAdjustCpuLoad call.
	ULONG Flags;			// Flags from RtCreateThread or latest RtAdjustCpuLoad call.
	ULONG StackSize;		// StackSize from RtCreateThread call.
	ULONGLONG PeriodIndex;					// Number of periods since start of thread.
	ULONGLONG TimesliceIndex;				// Number of times thread has been switched to.
	ULONGLONG TimesliceIndexThisPeriod;		// Number of times thread switch to this period.
	ULONGLONG ThisPeriodStartTime;			// Starting time for current period.
	ULONGLONG ThisTimesliceStartTime;		// Starting time for current timeslice.
	ULONGLONG DurationRunThisPeriod;		// Total time run so far this period.
	ULONGLONG DurationRunLastPeriod;		// Total time run in the last period.
	} ThreadStats;

#pragma pack(pop)


typedef VOID (*RTTHREADPROC)(PVOID Context, ThreadStats *Statistics);



NTSTATUS
RtVersion (
	OUT PULONG Version
	);

// RtVersion will return the version number of the currently running
// realtime executive.

// If the realtime executive is running, this function returns
// STATUS_SUCCESS.  If for some reason the realtime executive
// cannot run on the current machine then STATUS_NOT_SUPPORTED
// is returned.

// Currently the realtime executive will only run on PII class or newer
// machines.

// If the pointer to the version number is non NULL, then the
// version information for the currently loaded realtime executive
// is returned.  The version information will be returned regardless
// of the NTSTATUS code returned by the function.

// The version number returned is in the format xx.xx.xx.xx where each
// xx is 1 byte of the ULONG and the ordering left to right is high
// order byte - > low order byte.  ie: 0x01020304 is version 1.2.3.4

// It IS acceptable to pass in a NULL version pointer.  In that case
// no version information is returned.

// If this function is called from a real time thread, then the version
// pointer MUST either be NULL, or it MUST point to a local variable on 
// that real time thread's stack.  Otherwise this function will return 
// STATUS_INVALID_PARAMETER.

// If this function is called from Windows, then the pointer must be
// valid for writing.  Otherwise it will return STATUS_INVALID_PARAMETER.

// This function may be called from any thread.  Windows or realtime.



BOOLEAN
RtThread (
    VOID
    );

// RtThread returns TRUE if called from within a realtime thread.  Otherwise
// it returns FALSE.



NTSTATUS
RtSystemInfo (
	ULONG Processor,
	SystemInfo *pSystemInfo
	);

// RtSystemInfo copies the pertinant processor and system information into the memory 
// pointed to by pSystemInfo.  If pSystemInfo is null or invalid, then RtSystemInfo 
// returns STATUS_INVALID_PARAMETER_2.  Otherwise RtSystemInfo will return STATUS_SUCCESS.

// For uniprocessor systems, the Processor number should be zero.  For N processor
// systems, the processor numbers range from 0 to N-1.  An invalid processor number
// will cause a STATUS_INVALID_PARAMETER_1 to be returned.



NTSTATUS
RtCreateThread (
	ULONGLONG Period,
	ULONGLONG Duration,
	ULONG Flags,
	ULONG StackSize,
	RTTHREADPROC RtThread,
	IN PVOID pRtThreadContext,
	OUT PHANDLE pRtThreadHandle
	);

// RtCreateThread is used to create a realtime thread.

// Period is the used to determine the frequency at which the realtime thread must be
// run.  The current minimum period that can be specified is 1ms.

// Duration is the amount of time within the period that the realtime thread will
// need to run.  Percentage CPU load can be calculated as 100*(Duration/Period) as long
// as Duration and Period are both specified in units of time.

// Flags
// This parameter is used to indicate specific requirements of the realtime thread
// being created.  Currently supported values for Flags are USESFLOAT and USESMMX.
// A realtime thread that can use floating point instructions must specify the
// USESFLOAT flag.  A realtime thread that can use MMX instructions must specify the
// USESMMX flag.

// StackSize is the size of the stack required by the realtime thread in 4k blocks.
// Currently StackSize must be between 1 and 8 inclusive.  RtCreateThread will fail
// with STATUS_UNSUCCESSFUL for any other values of StackSize.

// pRtThreadContext is a pointer to the context that should be passed to the thread.
// It may be NULL.  It is passed to the realtime thread as the Context parameter.

// pRtThreadHandle is a pointer to an RtThreadHandle that can be output from
// RtCreateThread.  pRtThreadHandle can be NULL, in which case no RtThreadHandle is
// returned.  Storage for the HANDLE RtThreadHandle must be allocated by the code
// that calls RtCreateThread.

// RtCreateThread may only be called from within a standard windows thread.  It MUST NOT
// be called from within a realtime thread.



NTSTATUS
RtDestroyThread (
	HANDLE RtThreadHandle
	);

// RtDestroyThread removes the realtime thread identified by RtThreadHandle from the
// list of running realtime threads, and releases all resources that were allocated when
// the thread was created.  RtThreadHandle must be a handle returned from RtCreateThread.

// RtDestroyThread may only be called from within a standard windows thread.  It MUST NOT
// be called from within a realtime thread.



NTSTATUS
RtAdjustCpuLoad (
	ULONGLONG Period,
	ULONGLONG Duration,
	ULONGLONG Phase,
	ULONG Flags
	);

// This function allows a realtime thread to adjust the amount of CPU that is allocated
// to it.  The Flags parameter must currently match that passed in at thread creation
// time, however, the Period and Duration may be different from the Period and Duration
// passed at thread create time.  If there is sufficient CPU to meet the new request,
// the function will return STATUS_SUCCESS and the Period and Duration in the thread's
// statistics will be updated to match the values passed in to this function.  If
// there is not enough CPU available to meet the request, this function will leave
// the Period and Duration recorded in Statistics unchanged and will return
// STATUS_INSUFFICIENT_RESOURCES.

// This function MUST be called from within a realtime thread.  A realtime thread can
// only change its OWN allocation.  It cannot change the allocation of any other
// realtime thread.



VOID
RtYield (
	ULONGLONG Mark,
	ULONGLONG Delta
	);

// RtYield will yield execution to other realtime threads in the system.

// It should be called whenever a realtime thread does not require further CPU resources.

// Parameters:
//  Mark
//		This is the reference time which will be subtracted from the current
//		realtime executive scheduler time.  Note that this time is ALWAYS
//		considered by the scheduler to be in the past.  Do NOT pass a time
//		which occurs in the future to this parameter.
//	Delta
//		This is the time that will be compared to the difference between the current
//		scheduler time and the mark.  The thread will yield execution until
//		the difference between the current scheduler time and the mark is greater 
//		than delta.

//		After a thread has called RtYield it will only be run when the following
//		code evaluates TRUE.  ( (RtTime() - Mark) >= Delta )  Until that occurs
//		the thread will NOT run.  Unless it is holding a spinlock required by
//		some other realtime thread - in which case it will run until it releases
//		the spinlock at which point it will again yield.



PVOID
RtAddLogEntry (
    ULONG Size
    );

// RtAddLogEntry reserves space for a new entry in the realtime logging buffer.
// It returns a pointer to the reserved space.  Note that if an unsupported Size
// is specified, or if there is no realtime logging buffer available on the
// system, this routine will return NULL.

// Parameters:
//  Size
//      This is the size in bytes of the chunk to reserve in the log.  It MUST be
//      an integral multiple of 16.



// The following standard WDM functions are also safe to call from within a real time 
// thread:  KeAcquireSpinLock and KeReleaseSpinLock.

// They have been modified to support realtime threads in the following ways:



// KeAcquireSpinLock

// KeAcquireSpinLock will now always attempt to take the spinlock regardless of whether it
// is running on a multiproc or uniproc machine.  If the spinlock is already acquired,
// then KeAcquireSpinLock will spin in a loop that calls RtYield(THISTIMESLICE) until
// the spinlock is released.

// It will then claim the spinlock.  This means that realtime threads that attempt to 
// acquire a held spinlock will BLOCK until the spinlock is free.  If you don't HAVE to use 
// spinlocks in your realtime threads, DON'T.

// Note that other realtime threads will continue to run as scheduled, but the thread
// waiting for the spinlock will continue yielding all its timeslices until the spinlock
// is released.

// If KeAcquireSpinLock is called from a realtime thread, then it will NOT attempt to
// change any irql levels.  This is important, since the current Windows IRQL level may
// be at higher than DISPATCH_LEVEL when this function is called.  Furthermore, the OldIrql
// returned by this function when it is called from a realtime thread is always 0xff - 
// which is an INVALID irql level.

// If you call KeAcquireSpinLock from a realtime thread you MUST call KeReleaseSpinLock
// for that spinlock from a realtime thread.

// Evenutally, KeAcquireSpinLock will be modified to do an RtDirectedYield to the realtime
// thread that is holding the spinlock.

// KeAcquireSpinLock may be called from within any thread.  Realtime or windows.



// KeReleaseSpinLock

// KeReleaseSpinLock now always attempts to release a held spinlock regardless of whether
// it is running on a multiproc or uniproc machine.

// If KeReleaseSpinLock is called from a realtime thread, then it will NOT change any irql
// levels.  It will also validate that it has been called with a new irql level of 0xff
// as would have been returned by the KeAcquireSpinLock call in the realtime thread to
// acquire the spinlock.

// At some point KeReleaseSpinLock may do an RtDirectedYield back to the realtime thread
// that yielded when it attempted to acquire the spinlock.

// KeReleaseSpinLock may be called from within any thread.  Realtime or windows.


#ifdef __cplusplus
}
#endif