/************************************************/
/*                                              */
/* File        : calbhard.c                     */
/* Description : CALB hardware base access      */
/*                                              */
/* Authors: G.Cabibbo, A.Passeri                */
/*                                              */
/* Created      : 10.02.1997                    */
/* Last modified: 03.04.1997                    */
/*                                              */
/************************************************/

#include <stdio.h>
#include <Error.h>
#include <Vme.h>
#include <Vme_am.h>
#include <GeoVme.h>
#include "calbhard.h"
#include "calbbits.h"


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

int calbh_open(int chain,                    /* IN  */
	       int crate,                    /* IN  */
	       int slot,                     /* IN  */
     	       CALBH_id *calb_id)            /* OUT */ 
{
  GeoBoardAddr Calb_Addr;

 if(GeoVmeOpen(chain,crate,slot,&Calb_Addr)!=GEOVME_ERROR_OK) 
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open_tmp","GeoVmeOpen error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }
  *calb_id = (CALBH_id) malloc(sizeof(CALBH_id_base));
 (*calb_id)->cid = Calb_Addr.cid;
 (*calb_id)->map_ptr = Calb_Addr.map_ptr;
 (*calb_id)->vme_addr = Calb_Addr.addr;  
 (*calb_id)->offs = Calb_Addr.offs;
 (*calb_id)->vme_am = Calb_Addr.am;

 if ((*calb_id)->map_ptr==NULL)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open_tmp","VmeMapAddress error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }

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

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

 return CALBH_ERROR_OK;
}

int calbh_close(int chain,                     /* IN  */
                int crate,                     /* IN  */
                int slot,                      /* IN  */
                CALBH_id calb_id)            /* IN */
{
 int geoc;

 free(calb_id);
 geoc=GeoVmeClose(chain,crate,slot);

 if(geoc!=GEOVME_ERROR_OK)
 {
   ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_close","VmeCloseChannel error: %s",
         ErrorGetMessage());
   return CALBH_ERROR_UNKNOWN;
 }

 return CALBH_ERROR_OK;
}


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

unsigned short calbh_read_reg16(CALBH_id calb_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned short reg16;
 unsigned short *aptr;

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

#ifdef DEBUG
 if (offset<0x4000)
   printf("read addr: %x \n",aptr);
#endif

/*  Vme_D16READ(calb_id->vme_addr,aptr,reg16);  */
  Vme_D16READ(calb_id->map_ptr,aptr,reg16);

 return reg16;
}

unsigned int   calbh_read_reg32(CALBH_id calb_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned int reg32;
 unsigned int *aptr;

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

#ifdef DEBUG
 if (offset<0x4000)
   printf("read addr: %x \n",aptr);
#endif

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

 return reg32;
}

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

int calbh_write_reg16(CALBH_id calb_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 *)calb_id->map_ptr+calb_id->offs+offset);

#ifdef DEBUG
 if (offset<0x4000)
   printf("write addr: %x \n",aptr);
#endif

/* Vme_D16WRITE(calb_id->vme_addr,aptr,reg);  */
   Vme_D16WRITE(calb_id->map_ptr,aptr,reg);

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

 /*    Vme_D16READ(calb_id->vme_addr,aptr,testreg); */
    Vme_D16READ(calb_id->map_ptr,aptr,testreg);

     if ((testreg&testmask)!=(reg&testmask))
       return CALBH_ERROR_TEST;
   }

 return CALBH_ERROR_OK;
}


/*********************************************************************/
/*                     readLatches routines                          */
/*********************************************************************/

int calbh_readlatch_i_chan(CALBH_id calb_id /* IN  */,       
                            /* OUT */ unsigned int  chan)
{
 unsigned short latch;
 unsigned short *aptr;
 unsigned int offset;

 if ( chan>29 )
   {
     ErrorSetF(CALBH_ERROR_OUTRANGE,"calbh_readlatch_i_chan","Error: channel number %d out of range",chan);
     return CALBH_ERROR_OUTRANGE;
   }
 offset = CALBH_PAGE_LATCH_ofs + chan*2 ;
 aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);

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

 Vme_D16READ(calb_id->map_ptr,aptr,latch);

 return latch&0x1fff;
}

int calbh_readlatch_i_all (CALBH_id calb_id /* IN  */,       
                            /* OUT */ CHANNELS  latch)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
      offset = CALBH_PAGE_LATCH_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      latch[chan]=reg16&0x1fff;
   }
 return CALBH_ERROR_OK;
}

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

int calbh_readpage_i_csr(CALBH_id calb_id,                /* IN  */
		         CALBH_CSR_regs *csr)   /* OUT */
{
 csr->creg = calbh_read_creg(calb_id);
 csr->sreg = calbh_read_sreg(calb_id);

 return CALBH_ERROR_OK;
}

int calbh_readpage_i_ped (CALBH_id calb_id /* IN  */,       
                            /* OUT */ CALBH_PEDS_val *peds)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
 /*  section A peds  */
      offset = CALBH_PAGE_PED_A_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      peds->ped_a[chan]=reg16&0xfff;

 /*  section B peds  */
      offset = CALBH_PAGE_PED_B_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      peds->ped_b[chan]=reg16&0xfff;
   }
 return CALBH_ERROR_OK;
}

int calbh_readpage_i_zsup(CALBH_id calb_id /* IN  */,       
                            /* OUT */ CALBH_ZSUP_val *zsup)
{
  int chret;

 chret=calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_A_ofs,zsup->zlow1_a,zsup->zhig1_a);
 if (chret != -1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_writepage_i_zsup_elem",
           "WARNING: more then 1 suppressed window for channel %d ",chret);

 chret=calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_B_ofs,zsup->zlow1_b,zsup->zhig1_b);
 if (chret != -1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_writepage_i_zsup_elem",
           "WARNING: more then 1 suppressed window for channel %d ",chret+0xF);

 chret=calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_A_ofs,zsup->zlow2_a,zsup->zhig2_a);
 if (chret != -1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_writepage_i_zsup_elem",
           "WARNING: more then 1 suppressed window for channel %d ",chret);

 chret=calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_B_ofs,zsup->zlow2_b,zsup->zhig2_b);
 if (chret != -1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_writepage_i_zsup_elem",
           "WARNING: more then 1 suppressed window for channel %d ",chret+0xF);

 return CALBH_ERROR_OK;
}

int calbh_readpage_i_zsup_elem(CALBH_id calb_id /* IN  */,/* IN */ int zofs,
                            /* OUT */ CHALF zlow, /* OUT */ CHALF zhig)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int i,chan,bit,prev;
 int chret=-1;

 for(chan=0;chan<15;chan++)
   {
      zlow[chan]=0;
      zhig[chan]=0x1000;
   }
 reg16 = 0xffff;
 for(i=0;i<0x1000;i++)
   {
      offset = zofs+i*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
/*  #ifdef DEBUG
    printf("read addr: %x \n",aptr);
    #endif   
*/
      prev = reg16;
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      for(chan=0;chan<15;chan++)
        {
          bit = bits_get_bit(reg16,chan);
          if(bit != bits_get_bit(prev,chan) )
	    {
              if( bit )
		{
           /*     if(zhig[chan]!=0x1000) chret=chan; */
                  zhig[chan] = i-1;
                }
              else
                {
                  if(zlow[chan]!=0) chret=chan;
                  zlow[chan]=i;
                }
            }
        }

   }
 return chret;
}
 
/*********************************************************************/
/*                     writepage routines                             */
/*********************************************************************/

int calbh_writepage_i_ped (CALBH_id calb_id /* IN  */,       
                            /* IN */ CALBH_PEDS_val *peds)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
 /*  section A peds  */
      offset = CALBH_PAGE_PED_A_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
      reg16=peds->ped_a[chan];
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);

 /*  section B peds  */
      offset = CALBH_PAGE_PED_B_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
      reg16=peds->ped_b[chan];
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);
   }
 return CALBH_ERROR_OK;
}

int calbh_writepage_i_zsup (CALBH_id calb_id /* IN  */,       
                            /* IN */ CALBH_ZSUP_val *zsup)
{
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_A_ofs,zsup->zlow1_a,zsup->zhig1_a);
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_B_ofs,zsup->zlow1_b,zsup->zhig1_b);
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_A_ofs,zsup->zlow2_a,zsup->zhig2_a);
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_B_ofs,zsup->zlow2_b,zsup->zhig2_b);

 return CALBH_ERROR_OK;
}

int calbh_writepage_i_zsup_elem(CALBH_id calb_id /* IN  */,/* IN */ int zofs,
                            /* IN */ CHALF zlow, /* IN */ CHALF zhig)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int i,chan,min,max;

 min = 0x1000;
 max = 0;
 for(chan=0;chan<15;chan++)
   { 
     if(zlow[chan]<min) min=zlow[chan];
     if(zhig[chan]>max) max=zhig[chan];
   }
 for(i=0;i<0x1000;i++)
   {
      reg16 = 0xffff;
      if( (i>=min) && (i<=max) )  
	{
          for(chan=0;chan<15;chan++)
	    {
              if( (i>=zlow[chan]) && (i<=zhig[chan]) )
               reg16=bits_set_bit(reg16,chan,0);
            }
	}
      offset = zofs+i*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
/*  #ifdef DEBUG
    printf("read addr: %x \n",aptr);
    #endif   */
      Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);

   }
 return CALBH_ERROR_OK;
}

/*********************************************************************/
/*                      Readfifo routines                            */
/*********************************************************************/

int calbh_read_pio_i_hfifo (CALBH_id calb_id,	/* IN  */
			    unsigned int nels,	/* IN  */
			    unsigned int *buffer,	/* OUT */
			    unsigned int *count)	/* OUT, nr of els really read */
{
  int i,iCount,tempbuf;
  CALBB_HFIFO_bits bits;   
  
#ifdef DEBUG
 printf("readfifo size: %x \n",nels);
#endif
  
  if (nels==0)
    { /* Nothing to do */
      *count=0;
      return CALBH_ERROR_OK;
    }
  
  iCount=0;
  for (i=0;i<nels;i++)
    {
      tempbuf=calbh_read_hfifo(calb_id);
      calbb_nrtohfifo(tempbuf,bits);
      
#ifdef DEBUG
      printf("readfifo:  el %x fifodata %x \n",i,tempbuf);
#endif
      
      buffer[iCount++]=bits.fifodata;

      if (bits.nvd!=CALBB_BIT_OFF) break;

    }
   
  *count = iCount;

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


int calbh_read_pio_i_dfifo (CALBH_id calb_id,	/* IN  */
			unsigned int nels,	/* IN  */
			unsigned int *buffer,	/* OUT */
			unsigned int *count)	/* OUT, nr of els really read */
{
 int i,iCount,tempbuf;
 CALBB_DFIFO_bits bits;   

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

 if (nels==0)
   { /* Nothing to do */
     *count=0;
     return CALBH_ERROR_OK;
   }
   
 iCount=0;
 for (i=0;i<nels;i++)
     {
       tempbuf=calbh_read_dfifo(calb_id);
       calbb_nrtodfifo(tempbuf,bits);

#ifdef DEBUG
       printf("readfifo:  el %x fifodata %x \n",i,tempbuf);
#endif
       
       buffer[iCount++]=bits.fifodata;

       if (bits.nvd!=CALBB_BIT_OFF) break;

     }
   
   *count = iCount;
 
#ifdef DEBUG
 printf("readfifo count: %x \n",*count);
#endif
 
 return CALBH_ERROR_OK;
}



int calbh_read_blt_fifo(CALBH_id calb_id,	/* IN  */
			unsigned int offset,	/* IN  */
			unsigned int nrels,	/* IN  */
			unsigned int *buffer,	/* IN/OUT */
			unsigned int *count)    /* OUT, nr of els. really read */
{
 int res;
 unsigned int *DMAbuffer;
 unsigned int nrbytes;

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

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

 nrbytes = nrels*4;

 /* alloc a buffer for DMA */
 /* DMAbuffer = (unsigned int *) VmeAllocateMemory(calb_id->cid,NULL,nrbytes,Vme_MEMORY_FASTEST);*/
 DMAbuffer = (unsigned int *) malloc(nrbytes);

 if (DMAbuffer==NULL)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_readfifo_fifo","VmeAllocateBuffer error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }
 
/*  set VME channel AM to block transfer  */
 VmeSetProperty(calb_id->cid,Vme_SET_COPY_AM,0xB);
 res = VmeRead(calb_id->cid,calb_id->offs+offset,(char *) DMAbuffer,nrbytes);

 if (res==0)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_readfifo_fifo","VmeRead error: %s",ErrorGetMessage());
     free(DMAbuffer);
     *count=0;
     return CALBH_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++)
     {
       CALBB_EDFIFO_bits bits;

       calbb_nr2edfifo(DMAbuffer[iDMA],bits);

      #ifdef DEBUG
       printf("readfifo %x el: %x fifodata %x \n",iDMA,DMAbuffer[iDMA],bits.fifodata);
      #endif

       buffer[iCount++]=bits.fifodata;

       if (bits.nvd!=CALBB_BIT_OFF) break;

     }

  *count = iCount;
 }
***/

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

 free(DMAbuffer);
 return CALBH_ERROR_OK;
}