This interface is only used when user programs need more than basic VME cycle generation or handling.
//DriveVME.DLL Header File
//Copyright 1996,1997,1999,2007 N4 Communications Co.  All rights reserved.
//This file supports use of DriveVME.DLL, which encapsulates 
//some of the functions of DriveVME.  All of the functions of
//DriveVME are available without using DLLs, this module is
//provided for those who desire Microsoft style DLL usage and operations.

//DriveVME.DLL is multi thread and multi-processor safe.

#include <windows.h>
#include <fcntl.h>
#include <io.h>
#ifdef IS_DMVEDLL_COMPILE
#define DllStatus __declspec( dllexport ) 
#else
#define DllStatus __declspec( dllimport ) 
#endif
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif

#ifdef __cplusplus
extern "C" {
#endif

/*************************************************************************************/
/********* Access VME Memory directly via networkable file I/O **************/
//The "rangespec" parameter is an ASCII string identifying the range to reserved,
//as well as formatting options.
//Use the graphical "AssistVME" program's "Access Via I/O" function to create and test 
//the VME device's proper operations, then cut the string AssistVME provides,
//and paste it in your program as the parameter to these functions.
//NOTE: File I/O based VME access functions IS supported over networks.

//Note: This function checks the passed in "filename" against the list of
//Keys stored in the local chassis DriveVME registry, substituting the stored
//value if any match is found.  If none is, the passed in name is treated
//like a filename in it's own right.  This feature allows for user
//modification of VME realities without recompliling the program.

//Note: The transfer width specified in the setup string is the width
//actually used for all transfers, without regard for the size.
DllStatus HANDLE Open_VME_Range_by_Name(IN const char *rangespec);
#define End_VME_Range_by_File(x) CloseHandle(x)

/*
//Example:
#define linesize 81
void demo_fileIO_vme_resource(){
  HANDLE han;
  char str[linesize+1];
  unsigned long got;
  //Allocate some slave memory, filename from AssistVME.
  puts("Checkout mapping access via file I/O");
  han = Open_VME_Range_by_Name(
    //This DriveVME string pretty prints the data, other choices include binary.
	//and space, fixed field or comma delimited variables.  See AssistVME.
	//There is no reason you couldn't use "open()" "read()" "write()" and "close()".
    "V:\\M\\sEa(a000#100)t(2A)rNwNp(rTwT)c(DC3MS)i(MS800)f(P)e(N)");
  if (han!=INVALID_HANDLE_VALUE) { //Did it.
	//Use the file method to read it back.
	if (-1==SetFilePointer(han,linesize,NULL,FILE_BEGIN)) errors++; //start on the second line
	if (ReadFile(han,&str,linesize,&got,FALSE)) {
		if (got==linesize) { 
		   str[got]=0;
		   printf("DriveVME pretty printed range:\n%s\n",str);
		} else { puts("System Error"); errors++; }
	} //File I/O Seeking, reading and writing work for remote VME chassis.
    if (!End_VME_Range_by_File(han)) errors++; //Optional, program termination will also end the map.
  } else {
    //Note: If DriveVME is loaded, a full reason for the failure is sent to
	//the DriveVME log.  And AssistVME will pop up to display it.
    puts("DriveVME is either not running or couldn't allocate the desired resource.\n");
	printf("Windows Error Code : %lx\n",GetLastError());
	errors++;
  }
}
*/



/**********************************************************************************/
/********* Access VME Memory directly via a local array **************/
//The "rangespec" parameter is an ASCII string identifying the range to be mapped.
//Use the graphical "AssistVME" program's "Map" function to create and test 
//the VME device's proper operations, then cut the string AssistVME provides,
//and paste it in your program as the parameter to these functions.
//NOTE: Array based VME access functions are NOT supported over networks.
//NOTE: The maximum width of the transfer is set by the specification string, so 
//      for example a Map_D32_.. transfer would actually occur as 4 D8 transfers if the
//      maximum permitted transfer width specified in the setup string was D8.
//NOTE: Every platform may not support all possible VME transfer widths.  If the
//      map function succeeds and returns a non-zero pointer, it

//Note: This function checks the passed in "filename" against the list of
//Keys stored in the local chassis DriveVME registry, substituting the stored
//value if any match is found.  If none is, the passed in name is treated
//like a filename in it's own right.  This feature allows for user
//modification of VME realities without recompliling the program.

//These routines return 0 if the hardware doesn't support memory mapped at
//the desired width.  However, if the file handle spec is returned != INVALID_FILE_HANDLE
//then the memory is available nevertheless, but it is only accessible via the
//Mapped_xxx routines below.  This is typically only the case on DEC Alpha computers
//when asking to map D8 or D16 cycle limited memory.  
#define Map_D8_VME_Range_By_Name(rangespec,p_handle)  ((unsigned char *)Map_VME_Range_by_Name(rangespec,p_handle))
#define Map_D16_VME_Range_By_Name(rangespec,p_handle) ((unsigned short *)Map_VME_Range_by_Name(rangespec,p_handle))
#define Map_D32_VME_Range_By_Name(rangespec,p_handle) ((unsigned long *)Map_VME_Range_by_Name(rangespec,p_handle))
#define Map_D64_VME_Range_By_Name(rangespec,p_handle) ((unsigned __int64 *)Map_VME_Range_by_Name(rangespec,p_handle))
DllStatus void *Map_VME_Range_by_Name(IN const char *rangespec,OUT HANDLE *map_handle);
#define End_VME_Range_Map(map_handle)  CloseHandle(map_handle)

/* 
//Example:
#include <dvmedll.h>
#include <stdio.h>
void demo_mapping_vme_resource(){
  HANDLE han;
  unsigned char *A16_offset5;
  puts("Checkout: Presenting VME memory as a 'C' array.");
  A16_offset5 = Map_D8_VME_Range_By_Name("V:\\V\\sIa(5-6)t(1)rNwNp(rAwA)c(DC3MS)i(MS800)e(N)",&han);
  if (A16_offset5) {
    printf("Offset 5 is %x, 6 is %x.\n",A16_offset5[0],A16_offset5[1]);
    if (!End_VME_Range_Map(han)) errors++; //Optional, program termination will also end the map.
  } else {
    //Note: If DriveVME is loaded, a full reason for the failure is sent to the DriveVME log.
	//And AssistVME will pop up to display it.
    puts("DriveVME is either not running or couldn't allocate the desired resource.\n");
	printf("Windows Error Code : %lx\n",GetLastError());
	errors++;
  }
}
*/

//Not all interfaces and processors support 8, 16 and 32 bit transfers natively.
//For example, DEC Alpha processors in their native mode will only perform D32 cycles,
//extracting or splicing in bytes or 16 bit values from these as necessary.

//One way to insure the desired transfer width without processor dependance is to use the file
//I/O method for VME access outlined above, setting the file position
//to the desired offset then reading or writing as many bytes as desired.  This method
//always uses exactly the specified transfer width.  However, these requests may be serialized
//or use DMA and so incur substantial overhead.

//The purpose of these routines is to provide the lowest overhead method in user mode of accessing
//VME resources without serialization.  This method is the fastest for short transfers.

//These Mapped_xx_Read_1 routines will -1 on any error and 
//Bus errors will be treated as specified in the file open string.
DllStatus unsigned char    Mapped_D8_Read_1 (HANDLE Mapped_Range_Handle,unsigned long byte_offset);
DllStatus unsigned short   Mapped_D16_Read_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset);
DllStatus unsigned long	   Mapped_D32_Read_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset);
DllStatus unsigned __int64 Mapped_D64_Read_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset);

//The following functions will return TRUE upon success, FALSE if any error.
//They all take as arguments the file handle of the map, the offset in bytes from the beginning of the 
//map to the first desired byte of the transfer, and a pointer to either the source or destination of
//the data.  If there is a range, filehandle or other boundary error, these routines will return FALSE,
//they will not generate any other exceptions.  Use these instead of .._Read_1 if exceptions on errors
//are not desired.  These routines will not align unaligned transfers (use file I/O for that).

DllStatus BOOLEAN Mapped_D8_Read_N (HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int byte_count,unsigned char *values);
DllStatus BOOLEAN Mapped_D16_Read_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int word_count,unsigned short *values);
DllStatus BOOLEAN Mapped_D32_Read_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int long_count,unsigned long *values);
DllStatus BOOLEAN Mapped_D64_Read_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int int64_count,unsigned __int64 *values);

DllStatus BOOLEAN Mapped_D8_Write_1 (HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned char value);
DllStatus BOOLEAN Mapped_D16_Write_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned short value);
DllStatus BOOLEAN Mapped_D32_Write_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned long value);
DllStatus BOOLEAN Mapped_D64_Write_1(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned __int64 value);

DllStatus BOOLEAN Mapped_D8_Write_N (HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int count,unsigned char *values);
DllStatus BOOLEAN Mapped_D16_Write_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int count,unsigned short *values);
DllStatus BOOLEAN Mapped_D32_Write_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int count,unsigned long *values);
DllStatus BOOLEAN Mapped_D64_Write_N(HANDLE Mapped_Range_Handle,unsigned long byte_offset,unsigned int count,unsigned __int64 *values);

//Users can specify patterns of mapped memory transfers by making arrays of this structure:
struct dll_mapped_io_instruction {
  unsigned long zero_for_VME_read_not_zero_for_VME_write;
  unsigned long transfer_width; 
	//0->transfer bytes using D8's,
	//1->transfer words using D16's,
	//2->transfer DWords using D32's, 
	//3->transfer QWords using D64's.
  unsigned long cycle_count;	//The number of consecutive cycles 
	//So if transfer_width is 2 and this is 1, then 2 D32 cycles
	//will move 8 bytes.  An entry of zero will end the processing.
  unsigned long vme_offset;	//BYTE offset from mapped VME start address range for transfer
  void *value;	//The address of the first byte in user space for the transfer.
};

//Passing an array of the above structure to this routine will permit VME master mode
//access via (not network enabled) memory maps.  These routines are available at all
//times and are useful when the processor platform does not permit small memory cycles
//natively.  On processors which do support such native cycles, these routines are
//implemented in the user mode, on processors which don't, the requests are passed
//into kernel mode.
//Mapped_Pattern_Transfer returns the number of structre elements which were processed
//without error, or -1 if the request failed before the first structure element was
//considered.
DllStatus int Mapped_Pattern_Transfer(HANDLE Mapped_Range_Handle,struct dll_mapped_io_instruction *map,int map_count);


/***************************************************************************************/
/***** Offer local memory to VME boards, access locally via array or by network file ****/
//The "rangespec" parameter is an ASCII string identifying the range to be mapped.
//Use the graphical "AssistVME" program's "Slave" function to create and test.
//Then cut the string AssistVME provides, paste it in your program as the parameter
//to these functions.

struct Offered_Memory_Details {
	PVOID *offered_memory_base;	//base address of shared memory in local process space
	unsigned long vme_base_address; //VME offset to (char *)offered_memory_base
	unsigned long vme_base;	//low VME address decoded to support this request.
	unsigned long vme_bound; //high VME address decoded to support this request.
};
//Note: This function checks the passed in "filename" against the list of
//Keys stored in the local chassis DriveVME registry, substituting the stored
//value if any match is found.  If none is, the passed in name is treated
//like a filename in it's own right.  This feature allows for user
//modification of VME realities without recompliling the program.
//The function returns result->offered_memory_base
DllStatus void *Offer_VME_Local_Memory_by_Name(IN const char *rangespec,
		OUT HANDLE *map_handle,OUT /*optional*/ struct Offered_Memory_Details *result);

#define End_Offering_to_VME(map_handle)  CloseHandle(map_handle)
/* 
//Example:
#include <dvmedll.h>
#include <stdio.h>
void demo_offering_local_memory(){
  struct Offered_Memory_Details ranges;
  HANDLE han;
  unsigned long *A32_offset,test,got;
  //Allocate some slave memory, filename from AssistVME.
  puts("Checkout: Offer local memory to VME, access as an array");
  A32_offset = Offer_VME_Local_Memory_by_Name(
    "V:\\S\\sEcNcPp(A)t(N)a(#100)uNuCuIe(N)",&han,&ranges);
  if (A32_offset) { //Did it.
	//Use the array method to set a value.
	A32_offset[2] = 0x01010101; //Assume opening a local VME chassis
    printf("If not via network: VME Offset %lx is now %lx\n",
		ranges.vme_base_address+2*sizeof(long),
		A32_offset[2]);

	//Use the file method to read it back.
	if (-1==SetFilePointer(han,2*sizeof(long),NULL,FILE_BEGIN)) errors++;
	if (ReadFile(han,&test,sizeof(test),&got,FALSE)) {
		if (got==sizeof(test)) { 
			if (test != 0x01010101) { errors++; puts("DriveVME Data Error."); }
			else puts("OK File & Array method matched");
		} else { puts("System Read Error"); errors++; }
	} //File I/O Seeking, reading and writing work for remote VME chassis.
    if (!End_VME_Range_Map(han)) errors++; //Optional, program termination will also end the map.
  } else {
    //Note: If DriveVME is loaded, a full reason for the failure is sent to
	//the DriveVME log.  And AssistVME will pop up to display it.
    puts("DriveVME is either not running or couldn't allocate the desired resource.\n");
	printf("Windows Error Code : %lx\n",GetLastError());
	errors++;
  }
}
*/


/**************************************************************************/
/****** Lock the VME bus during uninterruptable operations or to avoid ***/
/****** deadlocks on systems which don't support simultaneous master/slave cycles.*/
// These functions ensure access to the VME bus is locked to this computer
// for the duration of the call to the function passed in.  Whether the bus
// is or is not locked outside the context of this call is undefined, however
// the bus does not stay locked outside the context of standing requests from
// at least one process.  The return value is the value the passed function returns.
// NOTE: The calling thread is set to operate at the highest possible scheduling
//       priority while the bus is locked.  Don't execute any busy loops
//       in expectation of action by other tasks or processes within this call.
//       The thread is returned to it's previous priority when the call returns.
// NOTE: There is some time overhead associated with these calls, it is good
//       design to perform fewer calls that do more than many sequential calls.
// WARNING:  While the VME bus is locked to this computer, no other transfers can
//           occur on the VME bus.
DllStatus unsigned char    D8_Lock_VME_Bus_Then_Call (unsigned char (*yourfunc )(long arg,void *context),long arg,void *context);
DllStatus unsigned short   D16_Lock_VME_Bus_Then_Call(unsigned short(*yourfunc)(long arg,void *context),long arg,void *context);
DllStatus unsigned int     D32_Lock_VME_Bus_Then_Call(unsigned int  (*yourfunc)(long arg,void *context),long arg,void *context);
DllStatus unsigned __int64 D64_Lock_VME_Bus_Then_Call(__int64       (*yourfunc)(long arg,void *context),long arg,void *context);
// NOTE: Some VME hardware implementations suffer from a bug which will result in bus
//       errors or even deadlocks when concurrent slave/master cycles occur.
//       If you map memory on such a system, unless you know that no other program
//       (like bus networks) will _ever_ run concurrently you should only access
//       VME memory via array in the context of one of these calls.

//Returns TRUE iff concurrent master/slave cycles are not supported on this system.
DllStatus BOOLEAN VME_MasterSlaveDeadLock_Watch(void); 

//Returns TRUE iff concurrent master/slave cycles are not supported on this system
//AND some process somewhere is currently offering slave cycles.
DllStatus BOOLEAN VME_MasterSlaveDeadLock_Warning(void); 

//       These next procedures act as their above counterparts if
//       VME_MasterSlaveDeadLock_Warning() is true, otherwise they will
//       just call the supplied function with no VME or other priority changes.
//		 These are the safest calls to use, as the overhead is one test in systems
//       which support concurrent master/slave operations.
DllStatus unsigned char    D8F_Lock_VME_Bus_Then_Call (unsigned char (*yourfunc)(long arg,void *context),long arg,void *context);
DllStatus unsigned short   D16F_Lock_VME_Bus_Then_Call(unsigned short(*yourfunc)(long arg,void *context),long arg,void *context);
DllStatus unsigned int     D32F_Lock_VME_Bus_Then_Call(unsigned int  (*yourfunc)(long arg,void *context),long arg,void *context);
DllStatus unsigned __int64 D64F_Lock_VME_Bus_Then_Call(__int64       (*yourfunc)(long arg,void *context),long arg,void *context);


/******************************************************************************/
/****************** Kernel C Program Support **********************************/
//These two routines support running a "C" program in the kernel mode of this
//processor.  First you should use AssistVME's development environment to verify
//the operation of the program.  Then, you can either pass the source as a string
//or the name of a file to these routines.  The first action must be a read on the
//file handle, this will compile and begin the operations of the program.  Anything
//output by the program (printf, console writes, etc) will satisfy reads you make
//to the provided handle.  Writes you make to the handle satisfy console input 
//functions (scanf, gets, etc..).  Overlapped read/writes are satisfied in the
//order recieved and are supported (supports double buffering).  The program is
//terminated either due to run error, normal exit, or closing the file handle.  
//Upon termination, all pending I/O is satisfied with End of file status.

//NOTE:  These kernel C programs can generate and service interrupts, have timers,
//       and many most other usual features.  They can also share memory with the 
//       calling program and send windows messages.  Use them to do limited, high
//       priority or real-time system actions, reserving all other processing to
//       the calling program.
//NOTE:  If these functions return with a valid handle, the supplied program has
//       started to run, and may have finished or had some error.  Reads or writes
//       to the handle will be returned immediately with EOF if the program isn't
//       running, pending asychronous writes/reads will also be returned that way.
//NOTE:  These functions merely open the file "V:\I\C, write the source to it, then
//       start the program by doing a zero byte read.  (The first read could be of
//       any size, but zero byte reads are satisfied with no action needed by the
//       supplied program.)  If the V: drive or the V:\I directory is marked as
//       shared by the operating system, then programs may be submitted via a 
//       network to remote processors, supporting remote diagnostics and debugging,
//       file I/O, etc.
//WARNING:  Access to the V:\ drive should be restricted to
//       authorized system level users.  Full internal access to the system is exposed
//       via this interface.

//The "overlapped" parameter if true opens the file in a mode which permits
//the ReadFile and WriteFile commands to specify "overlapped" parameter which
//permits them to return before the transfer is completed.  This is handy for
//transferring data in and out of background processing- supplying buffers
//before they are needed without waiting for them to be filled.  If the
//parameter is false, the FILE_FLAG_OVERLAPPED parameter is not part of the
//file open call, and the I/O operations block until the transfers are complete.
DllStatus HANDLE Run_Win32_Kernel_C_Program_By_String(char *source,BOOLEAN overlapped);
DllStatus HANDLE Run_Win32_Kernel_C_Program_By_File(char *source_filename,BOOLEAN overlapped);
#define End_Win32_Kernel_C_Program(h) CloseHandle(h)
/* Example:
Run_Win32_Kernel_C_Program_By_String(
"#include <stdio.h>\n\main(){ puts("Hello World"); }");
*/

//These two routines work exactly as the above, but offer file handles
//as supported by the <fcntl.h> mechanism, "open" "read" "write" "lseek" "close", etc.
//These can be converted to streams by run time library functions.
//*************WARNING***********
//Be sure to compile any application using the standard library calls to use the
//multithreaded DLL version of the C runtime libraries.  The statically linked libraries
//do not share the file handle data.  See the Build "settings" C/C++ "Code generation 
//menu, or just be sure to use the /MD or (/MDd for debug) flags
//*******************************
DllStatus int Run_Std_Kernel_C_Program_By_String(char *source);
DllStatus int Run_Std_Kernel_C_Program_By_File(char *source_filename);
#define End_Std_Kernel_C_Program(h) close(h)


//Interrupt Generation
//This generates a VME interrupt on the given level (1, 2, 3, 4, 5, 6 or 7)
//and responding with the given vector (between 0-255). Using -1 for the vector
//will use the system default VME interrupt acknowledgement vector.
//Note:  If it is necessary to generate more interrupts per second than this
//method will accomodate, you should use the overhead reducing interrupt calls
//below this one. Otherwise you must craft your own Drive/VME "C" program, which
//can run uninterruptibly if need be.  If that level of performance is not
//sufficient, then you must craft your own Windows/NT device driver, which can
//make direct interrupt generation calls to Drive/VME (as well have higher speed
//access to all the functionality described here, and more, but at the expense
//of complexity and lack of portability.
//NOTE: Generate_VME_Interrupt will not return either VME_INT_PENDING or
//VME_INT_ISSUED.  When it returns, the interrupt will have either been
//ackowledged, timed out, or rejected.  There is a performance loss associated
//with this, because the system waits for the acknowledgement.  To queue calls
//and process during the timeout or waiting for acknowledge period, (and better
//performance generally) use the three calls following this one.
DllStatus int Generate_VME_Interrupt(int level,int vector);
//Return value is one of:
#define VME_INT_ACKED			0	//Interrupt issued, acknowledged, idle.
#define VME_INT_PENDING			1	//Interrupt waiting to be issued when cancelled.
#define VME_INT_ISSUED			2	//Interrupt issued, not yet acked.
#define VME_INT_IACK_TIMEOUT	-1  //Interrupt issued, ack took too long. Aborted.
#define VME_INT_NOT_ACCEPTED	-2  //Interrupt never issued, not acceptable.
//!note must agree with dvmeddk.h

//The generate interrupt function is easiest for occasional interrupts which
//are responded to within 3 seconds.  For faster generation of interrupts use
//the following three functions.  Each returns one of VME_INT_... above.
//The savings is in that the overhead of setting up the interrupt is accomplished
//once.  From then on, it is a matter of much lower overhead to trigger the interrupt.
//NOTE:  SOME VME implementations can only generate one interrupt at a time.  That
//means that requests to generate interrupts are serialized within Drive/VME.  The
//timeout on an interrupt which waits for the acknowledgement does not begin counting
//until the interrupt is issued, but if there are many interrupt sources on your CPU
//for VME, your requested interrupt may not begin until those "in line" ahead of it
//are not only sent but also either timed out or acknowledged.  Use AssistVME's
//interrupt generation screen to see how many interrupts on any level have been
//issued systemwide to check for possible timing contention issues with other
//software.  NOTE:  The default timeout period to wait for an IACK is three seconds.

//Note:  The each chassis permits its own set of keys, using/setting up the keys
//implies a reference to the currently chosen chassis.  That is to say,
//key one on chassis 1 is not the same as key 1 on chassis 2 unless you
//specifically set it up to be that way with a chassis select + key setup calls.

#define NUM_GENINT_KEYS 16 //Must agree with DriveVME kernel driver.
//The following call sets up an interrupt for future generation.  The key value 
//must be between 1 and below NUM_GENINT_KEYS.  (key 0 is reserved for the 
//Generate_VME_Interrupt call.)  If this call returns "ACKED", then future calls
//to generate or read the generation status can proceed with the passed in key value.
//the timeout is in units of 100ns and must be below 10 seconds, max.  The minimum
//value is 333.  NOTE: Generating a VME interrupt or resetting a key before the
//previous one has completed will result in the cancellation of the pending
//interrupt if any.
DllStatus int Generate_VME_Interrupt_KeySetup(int key,int level,int vector,int timeout);
//If the above call returned no error, then initiate an interrupt passing only the
//key with this call.  If this call does not returns either VME_INT_PENDING
//or VME_INT_ISSUED then the acknowledgement did not come in before the call returned.
//Before generating another interrupt using key, see the next call.
DllStatus int Generate_VME_Interrupt_ByKey(int key);
//If the generate interrupt by key returned that the interrupt was either pending or
//issued by not acknowledged, then call this until the result is not either pending
//or issued.
DllStatus int Generate_VME_Interrupt_KeyStatus(int key);
//Programming Example:
/*	//Set up to generate a VME level 2 interrupt vector 0xac waiting at most 3 secs for 
	//the iack. Set this up for key 1.
	if (!Generate_VME_Interrupt_KeySetup(1,2,0xac,3*10*1000*1000)) {
		int i,j;
		//Generate 5 of them as fast as possible, quit on any error.
		for (j=0;j<5;j++) {
			i =  Generate_VME_Interrupt_ByKey(1);
recheck:
			switch (i){
			case VME_INT_PENDING: //When a VME interface can only
			//generate one interrupt and wait for one iack at a time,  DriveVME
			//serializes interrupt requests in this case, and this one is still
			//"in line", another (from some source) is pending and not iacked yet.
			case VME_INT_ISSUED://sent, no iack yet, but not timeout either.
				i = Generate_VME_Interrupt_KeyStatus(1);
				goto recheck; 
			case VME_INT_ACKED: break; //Interrupt sent and acknowledged
			case VME_INT_IACK_TIMEOUT: puts("Interrupt sent, timed out waiting for iack."); j=5; break;
			case VME_INT_NOT_ACCEPTED: puts("System not able to send VME interrupts."); j=5;  break;
			default: puts("Unexplained error"); errors++; j=5; break;
			}
		}
	}
*/

#include "../commondist/dvmeinterface.h"
//Interrupt sensing
//These routines set the system to respond to VME interrupts.  
//The different VME related interrupt events are:
//Note: for ASYNC_TIMER_WAKEUP, "low_limit" below specifies the wakeup
//time in 100 nanosecond units.  if "high_limit" below is 0, then the
//event will recur until watching for it is cancelled, and the timing
//period will not drift (though notice of the event may be delayed from
//the actual time somewhat).  If "high_limit" is not zero, the event
//will occur once after the specified delay, then stop.


#pragma pack(push,1)
struct async_event_entry {
	int		event_count;	//count since monitoring began of interrupt events.
	int		error_count;	//count of interrupt acknowledgment (bus) errors.
	__int64 last_event_time;//Ticks since boot count of the last processed event.
	void	*reserved;
	int		last_result;	//usually the last interrupt acknowledge vector
};

//This structure describes the asynchronous event (like interrupts)
//you want to watch or respond to.
struct async_event_setup {
	int	event_to_watch;		//one of ASYNC_.... above.
	int high_limit;			//like low_limit, except acknowledgement vectors 
							//above high_limit will be ignored.  256 will capture
							//all interrupts at the chosen level.  Note:
							//it makes no sense to have high limit less than low limit.
	__int64 low_limit;		//in the case of VME interrupts, should the
	                        //interrupting source (VME_INT_xx) offer
							//an acknowledgement vector below low_limit,
							//the interrupt will not trigger this routine.
							//use 0 to trap all interrupts at the chosen level
	//Note that high_limit and low_limit may be ignored for interrupt events
	//which have no information compontent (all of them above VME_INT_7 except
	//the timer).
};


//This call returns the total number of VME bus errors which have occured
//on the target system while it was executing VME master cycles. Note that
//there is no good way to detect reliably which process caused the bus error.
DllStatus unsigned long Query_VME_Bus_Errors();

//asserts VME sysfail signal for a little over, but at least, 250 milliseconds.
DllStatus int Pulse_VME_SysFail(); 

//vme_bus_locking:
//Call with 1 to ask that the bus be locked to this CPU.  Returns with bus locked
//and priority set above all tasks but below interrupts if it isn't already higher.
//Call with 2 to decrement the bus lock counter, which when it hits 0 releases
//the bus and resets to the priority extant at the time of bus locking.
//Returns: how many outstanding requests this system has outstanding overall to keep
//the VME bus locked (not just this process, but instantaneiously system wide).
//Call with 0 to just get the return value but otherwise take no action
//Note: an error occured if asking for the bus to be locked returns 0.
//Note: using this call is generally a bad idea if your program is to 
//operate with third party VME programs.  If your transaction needs the bus
//to be locked, write a "kernel C" program to do the transfer quickly, or
//second best is to use the DLL calls above for quickly lock the VME
//bus, raising this process's task priority, call your function,
//then release the VME bus
//automatically (if the calling function is the only task that wants it)
//upon return.
#define VME_LOCK_SET 1
#define VME_LOCK_RELEASE 2
#define VME_LOCK_QUERY 0
DllStatus int vme_bus_locking(int);

//By default, the DriveVME dll operates on the local chassis.
//If you call set_vme_chassis(NULL), or set_vme_chassis("V:\\");
//operation is (re)set to the local chassis.  To set further operations
//to a remote chassis, supply the string referring to the shared Drive/VME
//drive on that chassis.  For example, set_vme_chassis("\\Rack2Chassis5\\V\\");
//Note that calls which explicitly require a filepath DO NOT refer to this
//value (like Open_VME_Range_by_Name or Offer_VME_Local_Memory_by_Name) but
//all other calls (like vme_bus_locking or Generate_VME_Interrupt and so on) do.
//This function will return TRUE if the chassis value was changed to the
//supplied value, otherwise FALSE (indicating no network VME chassis available).
//To get a list of the available chassis and generally check them out, use
//AssistVME's network button.
//Note:  Changing from one chassis to another does not end operations on
//the first chassis.  To end operations on a chassis use the call below.
//Note: Chassis identifiers are not case sensitive.
DllStatus BOOLEAN set_vme_chassis(char *chassis);
//Returns the currently set chassis. declare chassis[255] in your code.
//If the currently set chassis has been closed, this will return *0.
DllStatus void get_vme_chassis(char *chassis);
//Drive/VME operations on a chassis consume a thread and other resources,
//Chassis operations should be ended if no further calls are planned.
//Call this routine with the chassis name to end operations.
//The function returns true if ongoing operations were ended, false if
//there were no ongoing operations.
DllStatus BOOLEAN free_vme_chassis(char *chassis);


//Each process using DriveVME.DLL can support concurrent operations on
//NUM_CONCURRENT_CHASSIS at the same time.  This is an arbitrary figure,
//If your application calls for more concurrent operations, contact
//your supplier or N4.
#define NUM_CONCURRENT_CHASSIS 50	

//Front panel lamp management.  Assumes two lamps.  If the system has
//only one lamp, it is implemented as the "Pass" lamp.
#define PassLampTest	0	//returns PassLampSetOn or PassLampSetOff
#define FailLampTest	1	//returns FailLampSetOn or FailLampSetOff
#define LampSetOn		0x100
#define LampSetOff		0x200
#define PassLampSetOn	(LampSetOn |PassLampTest)
#define FailLampSetOn	(LampSetOn |FailLampTest)
#define PassLampSetOff	(LampSetOff|PassLampTest)
#define FailLampSetOff	(LampSetOff|FailLampTest)
//Returns -1 if error.
DllStatus int front_panel_lamps(int lamp_action);

//Returns the basic make and model of this particular system.
//Note that the software interface to all systems is the same,
//to ease porting and design flexibility
//error if return is -1.
DllStatus int get_vme_system_information(struct  s_vme_system_information *sys);


#define SYSRESET_QUERY 0	//return 1 if this system will reset upon VME sysreset, 0 if not, else not supported
#define SYSRESET_TO_LOCAL 1	//cause sysreset to reset local CPU
#define SYSRESET_IGNORE 2	//cause VME sysreset to have no local effect.
DllStatus int vme_localsysreset(int action);


#define PANELSYSRESET_QUERY 0	//return 1 if front panel switch or button causes SYSRESET, 0 if not, else not supported.
#define PANELSYSRESET_SET 1		//Cause the front panel reset button or switch to reset the VME bus
#define PANELSYSRESET_LOCAL 2	//Cause the front panel reset button or switch to not affect the VME bus
DllStatus int vme_panelsysreset(int action);


#define SYSCON_QUERY 0	//no changes, return current setting only
#define SYSCON_ON	 1	//set this processor as the system controller
#define SYSCON_OFF	 2	//set this processor as NOT the system controller
//This function returns the current state of the processor after any requested
//action is taken.  Not all processors can enable or disable whether they
//are the system controller after power up.  A return value of 1 indicates
//VME system control services are being provided.  0 -> not.
DllStatus int vme_system_controller(int action);


//if !vme_system_status(stats), then *stats and stats[1] are set as:
//*stats& 2  -> VME interrupt 1 is enabled
//*stats& 4  -> VME interrupt 2 is enabled
//*stats& 8  -> VME interrupt 3 is enabled
//*stats& 16 -> VME interrupt 4 is enabled
//*stats& 32 -> VME interrupt 5 is enabled
//*stats& 64 -> VME interrupt 6 is enabled
//*stats&128 -> VME interrupt 7 is enabled
//*stats&0x8000 -> ACFail interrupt enabled
//*stats&0x4000 -> SysFail interrupt enabled
//*stats&0x2000 -> Front Panel Switch nterrupt enabled
//*stats&0x1000 -> Bus error interrupt enabled
//stats[1] & 0x8000 -> ACFail asserted
//stats[1] & 0x4000 -> Sysfail asserted
DllStatus int vme_system_status(int *stats);



//USER MODE INTERRUPT PROCESSING.
//DriveVME supplies many modes of interrupt processing both for local
//chassis and over networks.  The interrupts can be due to the result
//of physical events or the no-drift timers.  These include sending windows
//messages to a calling process, calling functions you provide, or providing
//what appear to be ordinary files which satisfy read file calls when either
//the events of interest or timeouts occur.  The information supplied to the
//caller of the file read descibes the event and when it happened.  There
//are two other higher performance methods of handling events and interrupts,
//the kernel "C" Java routines, which are available over networks, and
//also the Windows/NT kernel driver interface for highest performance.
//The information supplied to each file read is:

//This is the structure of the data when DriveVME
//event structure files are read ( V:\H\....)  (interrutps, ticks, alerts)
struct DriveVME_Async_Event_ReadFileData {
	unsigned short status; //This value is ascii 'OK', with the letter 'O' in the low byte if the event
		//completed normally (whether a bus error or not), or 'TO' with a 'T' in the low byte
		//if completed due to a timeout.  The ascii is to make it easier for high level applications
		//to use this facility, a simple one byte character read will get the most important information.
	unsigned short event;   //This is the xx of the I(xx) parameter which opened this event file (16 bits)
	unsigned long number_of_events; //total number of times the event occurred without error
		//Note the above is incremented whether there was a read pending to record the event
		//or not.  If 100 events happened and only one read request was made just before the
		//100th event happened, the value returned here would be 100, not 1.
	unsigned long information; //the acknowledgment IACK value, -1 if bus error during iack.
	__int64 timestamp;
		//64 bits, the local performance counter time in QueryPerformanceFrequency()
		//units since bootup when the event happened.
};
    


#pragma pack(pop)


//Programmers can specify that functions of the following type be called when
//events and/or interrupts of interest occured, or when there was a 
//timeout waiting for the event.  These functions are called independantly
//of whether or not the user has asked that windows messages are sent 
//upon event or interrupt detection.  Remember, these callbacks in particular
//and user mode program interrupt handling in general have slower performance
//than kernel-"C"-Java routines.  If the best interrupt performance is needed,
//use the DriveVME Windows/NT kernel driver interface.
typedef BOOLEAN (*PON_INTERRUPT_OR_EVENT)(
	BOOLEAN timeout, 
		//If the event completed due to a timeout or error, this is true. If the 
		//event occured it is false.
	unsigned short event, //one of ASYNC_xxx described above and also--
		//This is the xx of the I(xx) parameter used which opened this event file (16 bits)
		//IF this event is 0xffff then there was a file read error.  The result
		//of the file read error (GetLastError) is stored in the "information" parameter.
		//if information is 0, then the error was that a different number of bytes
		//than expected was returned.
	unsigned long number_of_events, 
		//Total number of times the event occurred without error
		//Note the above is incremented whether there was a read pending to record the event
		//or not.  If 100 events happened and only one read request was made just before the
		//100th event happened, the value returned here would be 100, not 1.
	unsigned long information, 
		//In responding to an interrupt, the VME acknowledgment IACK value, This 
		//value is -1 if bus error during iack.
	__int64 timestamp,
		//64 bits, the local performance counter time in QueryPerformanceFrequency()
		//units since bootup when the event happened.
	void *context
		 //whatever parameter was passed to Open_Interrupts_And_Events_Handler
);
//If the function returns TRUE, then the function will be called again when the next
//event or timeout is detected.  If the function returns false, then the function
//will not be called again as a result of the event handle.  Returning FALSE has the
//same effect as calling Close_Interrupts_And_Events_Handler.  If you call 
//Close_Interrupts_And_Event_Handler for a handle after returning FALSE on an event
//call, your application will crash with a memory error.  Use either one method, or the other,
//don't use both.

//Note: This function checks the passed in "filename" against the list of
//Keys stored in the local chassis DriveVME registry, substituting the stored
//value if any match is found.  If none is, the passed in name is treated
//like a filename in it's own right.  This feature allows for user
//modification of VME realities without recompliling the program.

DllStatus PVOID Open_Interrupts_And_Events_Handler(IN const char *interrupt_spec,PON_INTERRUPT_OR_EVENT callback_func,PVOID context);
//This routine expects a local or remote DriveVME filename, as developed in the graphical
//AssistVME program's "ISR" section, and as documented in the DriveVME interrupt handler
//filespec document.  Basically a local or network remote filename that determines the
//interrupt or event of interest and what to do when it happens.  One option (which can't be
//used over networks) is that the calling thread recieve a windows message when the event occurs. 
//This option can be specified in the filename, and if specified occurs whether or not a
//callback function is supplied here.
//
//The context parameter passed into this function is passed to the interrupt service routine.
//
//Another option supported by these routines both on the local chassis and on network chassis
//is that a function whose name you specify be called
//upon the detection of each event (or timeout notice if no event occurs).  This is implemented
//by creating a second thread of execution in the calling process-- so be advised that if
//a function name is supplied to the following function, a thread will be created to run it,
//and that the supplied function may be called upon at any time or point of execution in the
//caller's program.
//
//Therefore, if the supplied function makes changes to data that may also be in use by the
//user's main thread, the user must use mutex, event or semaphore synchronization objects
//to avoid data corruption by synchronizing access to the shared data.
//
//If the supplied function pointer is NULL, then no function will be called upon event
//occurance. If a NULL function is supplied, this presumes that the user's file
//specification called for windows mesages be sent to the calling thread
//This function returns NULL if there was an error, use GetLastError() to determine the
//cause of any failure.  Otherwise the function returns a pointer which can be passed to
//Close_Interrupts_and_Event_Handler to conclude operations.
//
//Note:  These routines do nothing more than open the supplied file, and if a callback
//function is supplied, create a thread which keeps two overlapped reads outstanding,
//calling the supplied function with the results of those reads, until such time as the
//Close_Interrupts_And_Events_Handler is called.  Cleanup upon abnormal program termination
//is enabled.
//
//The user is free to use any file open method to open an event or interrupt handling
//file.  If asked for in the file specification, windows messages will be sent so long
//as the file is open.  Any program which can read files can just read the interrupt
//file handle, which will return with data when the event happens or there is a timeout.
//The format of the returned data is:
/*
struct DriveVME_Async_Event_Read {
	unsigned short status; //This value is ascii 'OK', with the letter 'O' in the low byte if the event
		//completed normally (whether a bus error or not), or 'TO' with a 'T' in the low byte
		//if completed due to a timeout.  The ascii is to make it easier for high level applications
		//to use this facility, a simple one byte character read will get the most important information.
	unsigned short event;   //This is the xx of the I(xx) parameter which opened this event file (16 bits)
	unsigned long number_of_events; //total number of times the event occurred without error
		//Note the above is incremented whether there was a read pending to record the event
		//or not.  If 100 events happened and only one read request was made just before the
		//100th event happened, the value returned here would be 100, not 1.
	unsigned long information; //the acknowledgment IACK value, -1 if bus error during iack.
	__int64 timestamp;         //64 bits, the local system time in 100ns units since bootup when the event happened.
};
*/
//If the file is opened with the Win32 function CreateFile with the FILE_FLAG_WRITE_THROUGH 
//(for network operations) and FILE_FLAG_OVERLAPPED, then the user may issue read requests
//which it can check at it's convenience and so avoid having to contend with synchronization
//issues.  Check the Win32 functions ReadFile and GetOverlappedResult for programming details,
//as well as the DvmeMFCDemo file for programming examples of this sort of use.

//This function takes the result of Open_Interrupts_and_Event_Handler, frees
//resources used, cleans up, and stops detecting events.  NOTE: IT IS AN ERROR
//to call this function if a supplied interrupt handling function returned FALSE.
//returning FALSE has the same effect as this call.
DllStatus void Close_Interrupts_and_Event_Handler(PVOID Open_Interrupts_Result);








#ifdef __cplusplus
}
#endif