/* Medium Level VME Driver from IPN-CNRS  - MG xx/xx/2005 @ LIXAM-CNRS */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

/* ============================================================================================================================ */
/* INCLUDES */
#include "vme_driver.h"

/* ============================================================================================================================ */
/* DEFINES */
#define DEVICE_DRIVER "/dev/universe"

#define NO_VME_ERROR               0
#define VME_OFF                    0
#define VME_ON                     1
#define VME_ERROR                  0x0E<<16
#define OPEN_ERROR                 VME_ERROR|1
#define READ_A16_ERROR             VME_ERROR|2
#define WRITE_A16_ERROR            VME_ERROR|3
#define READ_A24_ERROR             VME_ERROR|4
#define WRITE_A24_ERROR            VME_ERROR|5
#define READ_A32_ERROR             VME_ERROR|6
#define WRITE_A32_ERROR            VME_ERROR|7
#define MMAP_ERROR                 VME_ERROR|8

/* ============================================================================================================================ */
/* LOCAL VARIABLES */
int l_vme_access=0;         /* Local VME Access variable: pointer/handle to acess to vme-module through unix device*/
/*dma_info l_dma;          Local Direct Memory Access variable: structure to use the dma defined in "universe_io.h" */

/* ============================================================================================================================ */
/* PROTOTYPES */

int is_vme_access_open(vme_access *vme);


/* ============================================================================================================================ */
/* ============================================================================================================================ */
/* This function opens a generic handle/pointer to access to unix device.
With the IPN's driver, the vme-module is seen as a device called "/dev/universe" (see /dev/xxx directory). 
WATCH OUT: At the end of the program, one should close the access using a "close(fd);"

 input: -
output: error code, or NO_VME_ERROR if OK
*/

int open_vme(vme_access *vme, unsigned int address_device)
{

  vme->address_device=address_device;
  vme->mmap_address=NULL;
  vme->mmap_address=NULL;
  vme->fd=1;
#ifdef ON_BOARD
  vme->fd=open(DEVICE_DRIVER,O_RDWR);
#endif   
  if (vme->fd==-1)
    {
      vme->fd=0;
      printf("%s is not a valide device\n", DEVICE_DRIVER);
      return OPEN_ERROR;
    }
  else
    return NO_VME_ERROR;
 
}
 
/* ============================================================================================================================ */
/* This function closes the vme_access.

input : -
output :-
*/

void close_vme(vme_access *vme)
{
  if (vme->mmap_address) 
    {
      close_mmap(vme);
      vme->mmap_address=NULL;
    }
#ifdef ON_BOARD
  close(vme->fd);
#endif
  vme->fd=0;
  return;  
} 

/* ============================================================================================================================ */
/* This function tests whether the pointer/handle (called "l_vme_access") has already been initialized.

input : -
output :  Status ON or OFF about the 
*/

int is_vme_access_open(vme_access *vme)
{
  if (vme->fd) return VME_ON;
  else return VME_OFF;
  
}

/* ============================================================================================================================ */
/* NORMAL VME I/O */
/* ============================================================================================================================ */
int write_vme(vme_access *vme,int mode, int region, unsigned int offset, unsigned int data)
{
  int res=0;
#ifdef ON_BOARD
  struct vme_access_slsi a16,a24;
  struct vme_access_lsi a32;

  if (!is_vme_access_open(vme)) res=open_vme(vme,vme->address_device);
  if (res) return res;
  switch (mode) 
    {
    case A16: 
      {
	a16.offset=vme->address_device+offset;
	a16.region=region;
	a16.valeur=data;
	a16.erreur=0;
	res=ioctl(vme->fd, UNIVERSE_WRITE16, &a16);
	break;
      }
    case A24:
      {
	a24.offset=vme->address_device+offset;
	a24.region=region;
	a24.valeur=data;
	a24.erreur=0;
	res=ioctl(vme->fd, UNIVERSE_WRITE24, &a24);
	break;
      }
    case A32: 
      {
	a32.offset = vme->address_device+offset;
	a32.valeur = data;
	a32.erreur = 0;
	a32.lsi = 0;
	res=ioctl(vme->fd, UNIVERSE_WRITE_LSI, &a32);
	break;
      }
    }

  if (res==-1)
    {
      switch (mode)
	{
	case A16:
	  {
	    printf("Write error in A16 mode :Ox%08x\n",(int)a16.erreur);
	    return WRITE_A16_ERROR;
	    break;
	  }
	case A24:
	  {
	    printf("Write error in A24 mode :Ox%08x\n",(int)a24.erreur);
	    return WRITE_A24_ERROR;
	    break;
	  }
	case A32:
	  {
	    printf("Write error in A32 mode :Ox%08x\n",(int)a32.erreur);
	    return WRITE_A32_ERROR;
	    break;
	  }
	}
    }
#endif
  return NO_VME_ERROR;    
}

/* ============================================================================================================================ */
int read_vme(vme_access *vme,int mode, int region, unsigned int offset, unsigned int *data)
{
  int res=0;
#ifdef ON_BOARD
  struct vme_access_slsi a16,a24;
  struct vme_access_lsi a32;

  if (!is_vme_access_open(vme)) res=open_vme(vme,vme->address_device);
  if (res) return res;
  switch (mode) 
    {
    case A16: 
      {
	a16.offset=vme->address_device+offset;
	a16.region=region;
	a16.valeur=0;
	a16.erreur=0;
	res=ioctl(vme->fd, UNIVERSE_READ16, &a16);
	*data=a16.valeur;
	break;
      }
    case A24:
      {
	a24.offset=vme->address_device+offset;
	a24.region=region;
	a24.valeur=0;
	a24.erreur=0;
	res=ioctl(vme->fd, UNIVERSE_READ24, &a24);
	*data=a24.valeur;
	break;
      }
    case A32: 
      {
	a32.offset = vme->address_device+offset;
	a32.valeur = 0;
	a32.erreur = 0;
	a32.lsi = 0;
	res=ioctl(vme->fd, UNIVERSE_READ_LSI, &a32);
	*data=a32.valeur;
	break;
      }
    }
  if (res==-1)
    {
      switch (mode)
	{
	case A16:
	  {
	    printf("Read error in A16 mode :Ox%08x\n",(int)a16.erreur);
	    return READ_A16_ERROR;
	    break;
	  }
	case A24:
	  {
	    printf("Read error in A24 mode :Ox%08x\n",(int)a24.erreur);
	    return READ_A24_ERROR;
	    break;
	  }
	case A32:
	  {
	    printf("Read error in A32 mode :Ox%08x\n",(int)a32.erreur);
	    return READ_A32_ERROR;
	    break;
	  }
	}
    }
 #endif
  return NO_VME_ERROR;    
}
/* ============================================================================================================================ */
/* Memory Map Access */
/* ============================================================================================================================ */
int init_mmap(vme_access *vme,int mode)
{
  int res=0;  
  if (!is_vme_access_open(vme)) res=open_vme(vme,vme->address_device);
  if (res) return res;
  vme->mmap_size=16*1024; /*Depth of the mapping ???*/ 
#ifdef ON_BOARD
  switch (mode)
    {
    case A16 : res = ioctl(vme->fd,UNIVERSE_MMAP_SET_REGION_PCI_VME,REGION0|SLSI_MMAP|A16); 
      break;
    case A24 : res = ioctl(vme->fd,UNIVERSE_MMAP_SET_REGION_PCI_VME,REGION0|SLSI_MMAP|A24); 
      break;
    case A32 : res = ioctl(vme->fd,UNIVERSE_MMAP_SET_REGION_PCI_VME,0|LSI_MMAP);
      break;
    }
  if (res==-1) printf ("Error in mmap access");
  vme->mmap_address = (unsigned int *) mmap(NULL,vme->mmap_size,PROT_READ|PROT_WRITE,MAP_SHARED,vme->fd,vme->address_device);
  if (vme->mmap_address == MAP_FAILED)
    {
      printf("Low level error opening memory mapping\n");
      return MMAP_ERROR;
    }
  else
#endif
    return NO_VME_ERROR;
}

/* ============================================================================================================================ */
 void write_mmap(vme_access *vme,unsigned int offset,int datatype,  unsigned int data)
{
  unsigned int *ptr_mmap_offset;

  ptr_mmap_offset=vme->mmap_address+(int)(offset/4);
#ifdef ON_BOARD 
  switch (datatype)
    {
    case D8: *ptr_mmap_offset=(short)data;
      break;
    case D16: *ptr_mmap_offset=(int)data;
      break;
    case D32: *ptr_mmap_offset=data;
      break;
    }
#endif
  return;
}
/* ============================================================================================================================ */
unsigned int read_mmap(vme_access *vme,unsigned int offset, int datatype)
{
  unsigned int *ptr_mmap_offset;
 
  ptr_mmap_offset=vme->mmap_address+(int)(offset/4);
  return *ptr_mmap_offset;
}

/* ============================================================================================================================ */
void close_mmap(vme_access *vme)
{
#ifdef ON_BOARD
  munmap(vme->mmap_address, vme->mmap_size);
#endif
  return;
}

/* ============================================================================================================================*/

