/************************************************/
/*                                              */
/* File        : rockhard.c                     */
/* Description : ROCK hardware base access      */
/*                                              */
/* Author: Sfiligoi Igor                        */
/*                                              */
/* Created      : 20.01.1997                    */
/* Last modified: 12.05.1997                    */
/*                                              */
/************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <Error.h>
#include <GeoVme.h>
#include <Vme.h>
#include <Vme_am.h>
#include "rockhard.h"
#include "rockbits.h"


#define ROCKH_SLOT			21
#define ROCKH_DMA_BUFFER_SIZE		(20*1024)


/*********************************************************************/
/*                          Query routines                           */
/*********************************************************************/

/* returns 0 if ROCK not present, 1 else */
int rockh_ispresent(int chain,                   /* IN, VIC chain */
		    int crate)                   /* IN, position in the VIC chain  */
{
 return ((GeoVme_exist(chain,crate,ROCKH_SLOT)==GEOVME_BOARD_EXIST) &&
	 (GeoVme_btype(chain,crate,ROCKH_SLOT)==GEOVME_ROCK_BTYPE));
}

/*********************************************************************/
/*                       Initialization routines                     */
/*********************************************************************/

int rockh_open(int chain,                   /* IN, VIC chain */
	       int crate,                   /* IN, position in the VIC chain  */
	       ROCKH_id *rock_id)           /* OUT */
{
 int result;
 GeoBoardAddr GeoData;
 
#ifdef DEBUG
 printf("rockh_open:\nchain %x\ncrate: %x\n",chain,crate);
#endif
 *rock_id = (ROCKH_id) malloc(sizeof(ROCKH_id_base));
  if (*rock_id==NULL)
   {
     ErrorSet(ROCKH_ERROR_UNKNOWN,"rockh_open","Malloc returned NULL.");
     return ROCKH_ERROR_UNKNOWN;
   }

 result = GeoVmeOpen(chain,crate,ROCKH_SLOT,&GeoData);

 if (result!=GEOVME_ERROR_OK)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","GeoVmeOpen error: %s",ErrorGetMessage());
     return ROCKH_ERROR_UNKNOWN;
   }

 if (GeoData.btype!=GEOVME_ROCK_BTYPE)
   {
     GeoVmeClose(chain,crate,ROCKH_SLOT);
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","Chain %i Crate %i do not have a ROCK!",chain,crate);
     return ROCKH_ERROR_UNKNOWN;
   }
   
 (*rock_id)->chain = chain;
 (*rock_id)->crate = crate;
 (*rock_id)->cid = GeoData.cid;
 (*rock_id)->map_ptr = GeoData.map_ptr;
 (*rock_id)->vme_addr = GeoData.addr;
 (*rock_id)->offs = GeoData.offs;
 (*rock_id)->vme_am = GeoData.am;

 /* alloc a buffer for DMA */
 
 (*rock_id)->DMAbuffer = (unsigned int *) VmeAllocateMemory((*rock_id)->cid,NULL,ROCKH_DMA_BUFFER_SIZE*sizeof(unsigned int),Vme_MEMORY_FASTEST);
 /*
 (*rock_id)->DMAbuffer = (unsigned int *) malloc(ROCKH_DMA_BUFFER_SIZE*sizeof(unsigned int));
 */
 if ((*rock_id)->DMAbuffer==NULL)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","VmeAllocateMemory error: %s",ErrorGetMessage());
     return ROCKH_ERROR_UNKNOWN;
   }


#ifdef DEBUG
 printf("map: %x\naddr: %p\noffs: %x\nDMAbuffer: %p\n",
	(*rock_id)->map_ptr,
	(*rock_id)->vme_addr,
	(*rock_id)->offs,
	(*rock_id)->DMAbuffer);
#endif

 VmeSetProperty((*rock_id)->cid,Vme_SET_COPY_FIFO,1);

 return ROCKH_ERROR_OK;
}

int rockh_open_raw(unsigned int vme_addr,       /* IN  */
		   int vme_size,                /* IN  */
		   int vme_am,                  /* IN  */
		   int offs,                    /* IN, ROCK offset  */
		   ROCKH_id *rock_id)           /* OUT */
{
 *rock_id = (ROCKH_id) malloc(sizeof(ROCKH_id_base));
  if (*rock_id==NULL)
   {
     ErrorSet(ROCKH_ERROR_UNKNOWN,"rockh_open","Malloc returned NULL.");
     return ROCKH_ERROR_UNKNOWN;
   }


 (*rock_id)->cid = VmeOpenChannel("Rock","pio,dma");

 if ((*rock_id)->cid==Vme_FAILURE)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","VmeOpenChannel error: %s",ErrorGetMessage());
     return ROCKH_ERROR_UNKNOWN;
   }

 /* alloc a buffer for DMA */
 
 
 (*rock_id)->DMAbuffer = (unsigned int *) VmeAllocateMemory((*rock_id)->cid,NULL,ROCKH_DMA_BUFFER_SIZE*sizeof(unsigned int),Vme_MEMORY_FASTEST);
 /*
 (*rock_id)->DMAbuffer = (unsigned int *) malloc(ROCKH_DMA_BUFFER_SIZE*sizeof(unsigned int));
 */
 if ((*rock_id)->DMAbuffer==NULL)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","VmeAllocateMemory error: %s",ErrorGetMessage());
     return ROCKH_ERROR_UNKNOWN;
   }

 (*rock_id)->map_ptr=VmeMapAddress((*rock_id)->cid,vme_addr,vme_size,vme_am);
 (*rock_id)->vme_addr = vme_addr;
 (*rock_id)->vme_am = vme_am;
 (*rock_id)->offs = offs;
 (*rock_id)->chain = -1;  /* set to an invalid vaule */
 (*rock_id)->crate = -1;  /* set to an invalid vaule */


 if ((*rock_id)->map_ptr==NULL)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_open","VmeMapAddress error: %s",ErrorGetMessage());
     return ROCKH_ERROR_UNKNOWN;
   }

#ifdef DEBUG
 printf("map: %x\naddr: %p\noffs: %x\nDMAbuffer: %p\n",
	(*rock_id)->map_ptr,
	(*rock_id)->vme_addr,
	(*rock_id)->offs,
	(*rock_id)->DMAbuffer);
#endif

 VmeSetProperty((*rock_id)->cid,Vme_SET_COPY_FIFO,1);

 return ROCKH_ERROR_OK;
}


int rockh_close(ROCKH_id rock_id)            /* IN  */
{
 int result;

 if ((rock_id->chain>=0)&&(rock_id->crate>=0))
   { /* normal opened */
     result = GeoVmeClose(rock_id->chain,rock_id->crate,ROCKH_SLOT);

     free(rock_id);

     if (result!=GEOVME_ERROR_OK)
       {
	 ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_close","GeoVmeClose(%i,%i) error: %s",rock_id->chain,rock_id->crate,ErrorGetMessage());
	 return ROCKH_ERROR_UNKNOWN;
       }
     else
       return ROCKH_ERROR_OK;
   }
 else
   { /* raw opened */
     result = VmeCloseChannel(rock_id->cid);

     free(rock_id);

     if (result!=Vme_SUCCESS)
       {
	 ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_close","VmeCloseChannel error: %s",ErrorGetMessage());
	 return ROCKH_ERROR_UNKNOWN;
       }
     else
       return ROCKH_ERROR_OK;
   }
}

/*********************************************************************/
/*                       read routines                               */
/*                       return the read value                       */
/*********************************************************************/

unsigned char  rockh_read_reg08(ROCKH_id rock_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned char reg08;
 unsigned char *aptr;

 aptr = (unsigned char *)rock_id->map_ptr+rock_id->offs+offset;

#ifdef DEBUG
 printf("read addr: %p",aptr);
#endif

 Vme_D08READ(rock_id->map_ptr,aptr,reg08);

#ifdef DEBUG
 printf(" : %x\n",reg08);
#endif

 return reg08;
}

unsigned short rockh_read_reg16(ROCKH_id rock_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned short reg16;
 unsigned short *aptr;

 aptr = (unsigned short *) ((unsigned char *)rock_id->map_ptr+rock_id->offs+offset);

#ifdef DEBUG
 printf("read addr: %p",aptr);
#endif

 Vme_D16READ(rock_id->map_ptr,aptr,reg16);

#ifdef DEBUG
 printf(" : %x\n",reg16);
#endif

 return reg16;
}

unsigned int   rockh_read_reg32(ROCKH_id rock_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned int reg32;
 unsigned int *aptr;

 aptr = (unsigned int *) ((unsigned char *)rock_id->map_ptr+rock_id->offs+offset);

#ifdef DEBUG
 printf("read addr: %p",aptr);
#endif

 Vme_D32READ(rock_id->map_ptr,aptr,reg32);

#ifdef DEBUG
 printf(" : %x\n",reg32);
#endif

 return reg32;
}

/*********************************************************************/
/*                       write routines                              */
/*********************************************************************/

int rockh_write_reg08(ROCKH_id rock_id,       /* IN */
		      unsigned int offset,    /* IN */
		      unsigned char reg,      /* IN */
		      unsigned char testmask) /* IN, test if >0*/
{
 unsigned char *aptr;

 aptr = (unsigned char *)rock_id->map_ptr+rock_id->offs+offset;

#ifdef DEBUG
 printf("write addr: %p : %x\n",aptr,reg);
#endif

 Vme_D08WRITE(rock_id->map_ptr,aptr,reg);

 if (testmask!=0)
   {
     unsigned char testreg;
     
     Vme_D08READ(rock_id->map_ptr,aptr,testreg);

     if ((testreg&testmask)!=(reg&testmask))
       {
	 unsigned int aread,awrite;
	 aread = testreg;
	 awrite = reg;

	 ErrorSetF(ROCKH_ERROR_TEST,"rockh_write_reg08","Error during test: written %x read %x\n",awrite,aread);
	 return ROCKH_ERROR_TEST;
       }
   }

 return ROCKH_ERROR_OK;
}

int rockh_write_reg16(ROCKH_id rock_id,       /* IN */
		      unsigned int offset,    /* IN */
		      unsigned short reg,     /* IN */
		      unsigned short testmask)/* IN, test if >0 */
{
 unsigned short *aptr;

 aptr = (unsigned short *) ((unsigned char *)rock_id->map_ptr+rock_id->offs+offset);

#ifdef DEBUG
 printf("write addr: %p : %x\n",aptr,reg);
#endif

 Vme_D16WRITE(rock_id->map_ptr,aptr,reg);

 if (testmask!=0)
   {
     unsigned short testreg;

     Vme_D16READ(rock_id->map_ptr,aptr,testreg);

     if ((testreg&testmask)!=(reg&testmask))
       {
	 unsigned int aread,awrite;
	 aread = testreg;
	 awrite = reg;

	 ErrorSetF(ROCKH_ERROR_TEST,"rockh_write_reg16","Error during test: written %x read %x\n",awrite,aread);
	 return ROCKH_ERROR_TEST;
       }
   }

 return ROCKH_ERROR_OK;
}

int rockh_write_reg32(ROCKH_id rock_id,      /* IN */
		      unsigned int offset,   /* IN */
		      unsigned int reg,      /* IN */
		      unsigned int testmask) /* IN, test if >0 */
{
 unsigned int *aptr;

 aptr = (unsigned int *) ((unsigned char *)rock_id->map_ptr+rock_id->offs+offset);

#ifdef DEBUG
 printf("write addr: %p : %x\n",aptr,reg);
#endif

 Vme_D32WRITE(rock_id->map_ptr,aptr,reg);

 if (testmask!=0)
   {
     unsigned int testreg;

     Vme_D32READ(rock_id->map_ptr,aptr,testreg);

     if ((testreg&testmask)!=(reg&testmask))
       {
	 unsigned int aread,awrite;
	 aread = testreg;
	 awrite = reg;

	 ErrorSetF(ROCKH_ERROR_TEST,"rockh_write_reg32","Error during test: written %x read %x\n",awrite,aread);
	 return ROCKH_ERROR_TEST;
       }
   }

 return ROCKH_ERROR_OK;
}

/*********************************************************************/
/*                     readpage routines                             */
/*********************************************************************/

int rockh_readpage_i_internal(ROCKH_id rock_id,                /* IN  */
		              ROCKH_INTERNAL_regs *internal)   /* OUT */
{
 internal->reset = rockh_read_reset(rock_id);
 internal->fifo = rockh_read_fifo(rock_id);
 internal->watchdog = rockh_read_watchdog(rock_id);
 internal->csr0 = rockh_read_csr0(rock_id);
 internal->csr1 = rockh_read_csr1(rock_id);
 internal->csr2 = rockh_read_csr2(rock_id);
 internal->elapsed = rockh_read_elapsed(rock_id);
 internal->trigger = rockh_read_trigger(rock_id);

 return ROCKH_ERROR_OK;
}


int rockh_readpage_i_info(ROCKH_id rock_id,        /* IN  */
			  ROCKH_INFO_regs *info)   /* OUT */
{
 info->tque = rockh_read_tque(rock_id);
 info->tnow = rockh_read_tnow(rock_id);
 info->rockinfo = rockh_read_rockinfo(rock_id);
 info->golden = rockh_read_golden(rock_id);

 return ROCKH_ERROR_OK;
}

/*********************************************************************/
/*                     readfifo routines                             */
/*********************************************************************/

int rockh_readfifo_fifo(ROCKH_id rock_id,	/* IN  */
			unsigned int offset,	/* IN  */
			unsigned int nrels,	/* IN  */
			unsigned char raw,      /* IN, if >0, raw fifo values are returned */
			unsigned int *buffer,	/* IN/OUT */
			unsigned int *count)    /* OUT, nr of els. really read */
{
 int res;
 unsigned int nrbytes;
 unsigned int recCount;

#ifdef DEBUG
 unsigned char *aptr;

 aptr = (unsigned char *)rock_id->map_ptr+rock_id->offs+offset;

 printf("readfifo addr: %p\n",aptr);
#endif


#ifdef DEBUG
 printf("readfifo size: %x\n",nrels);
#endif

 if (nrels==0)
   { /* Nothing to do */
     *count=0;
     return ROCKH_ERROR_OK;
   }

 recCount=0;
 if (nrels>ROCKH_DMA_BUFFER_SIZE)
   { /* if cannot be read in one step, recurse */
     res = rockh_readfifo_fifo(rock_id,offset,nrels-ROCKH_DMA_BUFFER_SIZE,raw,buffer,&recCount);
     if (res!=ROCKH_ERROR_OK)
       { /* if something get wrong, exit with the same error */
	 *count = recCount;
	 return res;
       }
     nrels=ROCKH_DMA_BUFFER_SIZE;
     buffer=&(buffer[recCount]);
   }

 nrbytes = nrels*sizeof(unsigned int);

  res = VmeRead(rock_id->cid,rock_id->offs+offset,(char *) rock_id->DMAbuffer,nrbytes);

 if (res==0)
   {
     ErrorSetF(ROCKH_ERROR_UNKNOWN,"rockh_readfifo_fifo","VmeRead error: %s",ErrorGetMessage());
     *count=recCount;
     return ROCKH_ERROR_UNKNOWN;
   }

 res=res>>2; /* convert in nr of elements */

#ifdef DEBUG
 printf("readfifo res size: %x\n",res);
#endif

 {
   unsigned int iDMA,iCount;

   iCount=0;
   for (iDMA=0;iDMA<res;iDMA++)
     {
       if (rockb_get_nvd(rock_id->DMAbuffer[iDMA])==ROCKB_BIT_OFF)
	 { /* the rest of the data is valid */
	   if (raw)
	     buffer[iCount++]=rock_id->DMAbuffer[iDMA];
	   else
	     buffer[iCount++]=rockb_get_fifodata(rock_id->DMAbuffer[iDMA]); 
	 }
     }

  *count = recCount+iCount;
 }

#ifdef DEBUG
 printf("readfifo count: %x\n",*count);
#endif

 return ROCKH_ERROR_OK;
}