/* devPVME331.c */
/* devPVME331.c - Device Support Routines for  VME PVME-331 */
/*     16 bit low cost A/D board (16ch) */
/*
 *      Original Author: M. Tobiyama
 *      Current Author:  M. Tobiyama
 *      Date:            
 *
 *      Experimental Physics and Industrial Control System (EPICS)
 *
 *      Copyright 1997, KEKB.
 *
 *
 * Modification Log:
 * -----------------
 *	ver0.0 19/Aug/98	M.Tobiyama 
 *      ver0.1  7/Sep/98        M.Tobiyama
 */

#include	<vxWorks.h>
#include	<types.h>
#include	<stdioLib.h>
#include	<string.h>
#include        <iv.h>
#include        <vme.h>
#include	<dbDefs.h>
#include	<dbAccess.h>
#include        <recSup.h>
#include	<devSup.h>
#include	<devCamac.h>
#include	<link.h>
#include	<module_types.h>
#include        <fast_lock.h>

#include	<longinRecord.h>
#include	<longoutRecord.h>
#include        <boRecord.h>
#include        <biRecord.h>
#include        <mbbiDirectRecord.h>
#include        <mbboDirectRecord.h>
#include        <waveformRecord.h>

#include        <dbScan.h>

/* #define	Base_IO		0x20000 */ /* Std. I/O Address */
#define UNI10      0x30
#define UNI05      0x20
#define BIP10      0x40
#define BIP05      0x10
#define EXTRIG     0x81
#define SINGLE     0x00
#define DIFFER     0x01
#define FIFORESET  0x04
#define INTTIMER   0xffff

#define INT_LEVEL  0x05

struct DPvme331 {
  unsigned char   dummy0;
  unsigned char   scan_file0;
  unsigned char   dummy1;
  unsigned char   scan_file1;
  unsigned char   dummy2;
  unsigned char   scan_file2;
  unsigned char   dummy3;
  unsigned char   scan_file3;
  unsigned char   dummy4;
  unsigned char   scan_file4;
  unsigned char   dummy5;
  unsigned char   scan_file5;
  unsigned char   dummy6;
  unsigned char   scan_file6;
  unsigned char   dummy7;
  unsigned char   scan_file7;
  unsigned char   dummy8;
  unsigned char   scan_file8;
  unsigned char   dummy9;
  unsigned char   scan_file9;
  unsigned char   dummy10;
  unsigned char   scan_file10;
  unsigned char   dummy11;
  unsigned char   scan_file11;
  unsigned char   dummy12;
  unsigned char   scan_file12;
  unsigned char   dummy13;
  unsigned char   scan_file13;
  unsigned char   dummy14;
  unsigned char   scan_file14;
  unsigned char   dummy15;
  unsigned char   scan_file15;
  unsigned char   dummy16;
  unsigned char   trg_cntrl_reg;
  unsigned char   dummy17;
  unsigned char   trg_strt_reg;
  unsigned char   dummy18;
  unsigned char   sts_reg;
  unsigned char   dummy19;
  unsigned char   scn_cnt_reg;
  unsigned char   dummy20;
  unsigned char   mode_reg;
  unsigned char   dummy21;
  unsigned char   rst_reg;
  unsigned char   dummy22;
  unsigned char   int_cntrl_reg;
  unsigned char   dummy23;
  unsigned char   int_vct_reg;
  unsigned char   dummy24;
  unsigned char   int_sts_reg;
  unsigned short  dummy25;
  unsigned short  intvl_tm_reg;
  unsigned short  data_reg;
  unsigned short  dummys[100];
};    

#define	Status	Enable 

int devPvme331Config();

static long init_all();

static long init_wf_record();
static long init_bi_record();
static long init_bo_record();

static long read_wf_record();
static long get_wf_int_info();
static long read_bi_record();
static long write_bo_record();
static void pvme331_isr();

static int  checkLink();

struct {
    long        number;
    DEVSUPFUN   report;
    DEVSUPFUN   init;
    DEVSUPFUN   init_record;
    DEVSUPFUN   get_ioint_info;
    DEVSUPFUN   read_write;
    DEVSUPFUN   conv;
}devWfPvme331 ={
  6,
  NULL,
  NULL,
  init_wf_record,
  get_wf_int_info,
  read_wf_record,
  NULL
};

/* Create the dset for devBiPvme331 */
struct {
        long            number;
        DEVSUPFUN       report;         /* used by dbior */
        DEVSUPFUN       init;           /* called 1 time before & after all reco
rds */
        DEVSUPFUN       init_record;    /* called 1 time for each record */
        DEVSUPFUN       get_ioint_info; /* used for COS processing */
        DEVSUPFUN       read_bi;        /* input command goes here */
}devBiPvme331={
        5,
        NULL,
        NULL,
        init_bi_record,
        NULL,
        read_bi_record
};

/* Create the dset for devBoPvme331 */
struct {
        long            number;
        DEVSUPFUN       report;         /* used by dbior */
        DEVSUPFUN       init;           /* called 1 time before & after all reco
rds */
        DEVSUPFUN       init_record;    /* called 1 time for each record */
        DEVSUPFUN       get_ioint_info; /* used for COS processing (not used for
 outputs)*/
        DEVSUPFUN       write_bo;       /* output command goes here */
}devBoPvme331={
        5,
        NULL,
        NULL,
        init_bo_record,
        NULL,
        write_bo_record
};


struct ioCard {
  volatile struct DPvme331  *card;    /* address of this card */
  FAST_LOCK                 lock;     /* semaphore */
  IOSCANPVT                 ioscanpvt;/* list or records processed upon interrupt */
      };

#define CONST_NUM_LINKS 10
/* #define STATIC  */
#define DEBUG_ON

static int   debug_flag = 1;
static unsigned long Base_IO;
static int     pvme331_num_links, pvme_adc;
static unsigned short INT_VEC_BASE;

static struct ioCard cards[CONST_NUM_LINKS];
static int           init_flag = 0;
static int int_level;

static unsigned char tg_mode = 0x80, tg_enable=0x01;

int devPvme331Config(ncards,a24base,intvecbase,Nchannel)
int ncards;
long a24base;
int intvecbase;
int Nchannel;
{
  pvme331_num_links = ncards;
  Base_IO = a24base;
  INT_VEC_BASE = intvecbase;
  pvme_adc = Nchannel;
  logMsg("Pvme331 NumLink= %d BaseIO= %x INTVECBASE= %x ADC= %d Ch\n",pvme331_num_links,Base_IO,INT_VEC_BASE,pvme_adc);
  init_all(0);
}

static long init_all(after)
int after;
{
  int                          cardNum, chanNum;
  int                          i,j;
  unsigned char                probeVal;
  unsigned short               p1;
  volatile  struct DPvme331    *p;

  if (init_flag != 0 ) 
   return(OK);

  init_flag = 1;

  if (sysBusToLocalAdrs(VME_AM_STD_SUP_DATA,(char *)Base_IO,(char **)&p) == ERROR)
	{
		logMsg("VmeP331: cannot find A24 address space\n");
		return(ERROR);
	}

  for (cardNum=0; cardNum< pvme331_num_links; cardNum++)
   {
     if (vxMemProbe((char*) &(p->rst_reg), READ, 1, &probeVal)!=OK)
       {
	 if (debug_flag >0 ) 
         logMsg("No PVME331 with cardNum= %d\n probe= %x\n",cardNum,p);
	 cards[cardNum].card = NULL;
       }
     else
       {
	 probeVal=0x0f;
	 if ((vxMemProbe((char*) &(p->rst_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("System Reset for card %d\n",cardNum);
	 nanosleep(4000000);
	 probeVal=BIP10 | 0x00;
	 if ((vxMemProbe((char*) &(p->scan_file0), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 0 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x01;
	 if ((vxMemProbe((char*) &(p->scan_file1), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 1 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x02;
	 if ((vxMemProbe((char*) &(p->scan_file2), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 2 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x03;
	 if ((vxMemProbe((char*) &(p->scan_file3), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 3 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x04;
	 if ((vxMemProbe((char*) &(p->scan_file4), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 4 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x05;
	 if ((vxMemProbe((char*) &(p->scan_file5), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 5 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x06;
	 if ((vxMemProbe((char*) &(p->scan_file6), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 6 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x07;
	 if ((vxMemProbe((char*) &(p->scan_file7), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 7 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x08;
	 if ((vxMemProbe((char*) &(p->scan_file8), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 8 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x09;
	 if ((vxMemProbe((char*) &(p->scan_file9), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 9 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0A;
	 if ((vxMemProbe((char*) &(p->scan_file10), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 10 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0B;
	 if ((vxMemProbe((char*) &(p->scan_file11), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 11 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0C;
	 if ((vxMemProbe((char*) &(p->scan_file12), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 12 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0D;
	 if ((vxMemProbe((char*) &(p->scan_file13), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 13 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0E;
	 if ((vxMemProbe((char*) &(p->scan_file14), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 14 set  %d\n",cardNum);
	 probeVal=BIP10 | 0x0F;
	 if ((vxMemProbe((char*) &(p->scan_file15), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Scan file 15 set  %d\n",cardNum);
	 probeVal=pvme_adc-1;
	 if ((vxMemProbe((char*) &(p->scn_cnt_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Number of ADC channel for  %d = %d\n",cardNum,probeVal);
	 probeVal=0x00;
	 if ((vxMemProbe((char*) &(p->mode_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("ADC mode for  %d = Single\n",cardNum);
	 /*	 probeVal=FIFORESET;
	 if ((vxMemProbe((char*) &(p->rst_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("FIFO counter reset for card %d\n",cardNum);
	 */	 
	 p->intvl_tm_reg = INTTIMER;

	 probeVal=INT_VEC_BASE + cardNum;
	 if ((vxMemProbe((char*) &(p->int_vct_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Int vec base for card %d = %x\n",cardNum,probeVal);

	 probeVal=0x08 | INT_LEVEL;/* Int enable , IRQ=3 */
	 int_level = INT_LEVEL;
	 if ((vxMemProbe((char*) &(p->int_cntrl_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5)) 
	   logMsg("Int number %d = %x\n",cardNum,int_level);
	 if (debug_flag >0)
	   logMsg("Found PVME331 with cardNum= %d address= %x\n",cardNum,p);
	 cards[cardNum].card = p;  /* Remember address of the board */

	 scanIoInit(&cards[cardNum].ioscanpvt);
	 logMsg("pvme331 ioInit. 0x%X\n",cards[cardNum].ioscanpvt);

	 FASTLOCKINIT(&(cards[cardNum].lock));
	 FASTUNLOCK(&(cards[cardNum].lock)); /* Init the board lock */
       }
     p++;
   }
  return(OK);
}
/******************************************************************
*
* Interrupt service routine
*
*******************************************************************/
static void pvme331_isr(pwf)
struct waveformRecord *pwf;
{
  unsigned char mode;
  short cardN;

  cardN = pwf->inp.value.vmeio.card;
  
  /*  logMsg("pvme331_isr int called \n");*/
  mode = cards[cardN].card->int_sts_reg; 
  
  scanIoRequest(cards[cardN].ioscanpvt);
  
  /* logMsg("pvme331 int called \n");*/
  return;
}

/**************************************************************************
 *
 * BO Initialization (Called one time for each BO PowerUT card record)
 *
 **************************************************************************/
static long init_bo_record(pbo)
struct boRecord *pbo;
{

        pbo->mask = 1;
		return(0);
}

/**************************************************************************
 *
 * Perform a write operation from a BO record
 *
 **************************************************************************/
static long write_bo_record(pbo)
struct boRecord *pbo;
{

  short cardN;
  unsigned char tgreg;

  cardN = pbo->out.value.vmeio.card;
  if (checkLink(cardN) == ERROR)
    {
      logMsg("Error--- No PVME331 for card %d\n",cardN);
      return(ERROR);
    }
  FASTLOCK(&(cards[cardN].lock));
  switch(pbo->out.value.vmeio.signal){
  case 0:
    cards[cardN].card->trg_strt_reg = 0x01;
    break;
  case 1:
    if (((pbo->rval)&(pbo->mask)) == 0x01)
      tg_mode = 0x00;
    else 
      tg_mode = 0x80;

    cards[cardN].card->trg_cntrl_reg = (tg_mode) | (tg_enable);
    break;
  case 2:
    if (((pbo->rval)&(pbo->mask)) == 0x01)
      tg_enable = 0x00;
    else
      tg_enable = 0x01;
    cards[cardN].card->trg_cntrl_reg =(tg_mode) |(tg_enable);
    break;
  case 3:
    cards[cardN].card->rst_reg = ((pbo->rval))<<1 & 0x02; /* ADC calib */
    nanosleep(4000000);
    break;
  }
  FASTUNLOCK(&(cards[cardN].lock));
  return(0);
}

/**************************************************************************
 *
 * BI Initialization (Called one time for each BI PowerUT card record)
 *
 **************************************************************************/
static long 
init_bi_record(pbi)
struct biRecord *pbi;
{

        pbi->mask = 1;
		return(0);
}

/**************************************************************************
 *
 * Perform a read operation from a BI record
 *
 **************************************************************************/
static long 
read_bi_record(pbi)
struct biRecord *pbi;
{
  short cardN;

  cardN = pbi->inp.value.vmeio.card;
  if (checkLink(cardN) == ERROR)
    return(ERROR);
  pbi->rval = cards[cardN].card->sts_reg & pbi->mask;
  if (debug_flag >0)
    logMsg("read_bi signal 0 status %d\n",pbi->rval);
  return(0);
}

static long init_wf_record(pwf)
struct waveformRecord	*pwf;
{
  short cardNum;
  cardNum = pwf->inp.value.vmeio.card;

  if (intConnect(INUM_TO_IVEC(INT_VEC_BASE + cardNum),
		 (VOIDFUNCPTR)pvme331_isr, 
		 (int)pwf) != OK)
     logMsg("devPVME331: Interrupt connect failed for card %d\n",pwf->inp.value.vmeio.card);
  logMsg("intConnect 0x%X\n",pwf);
	 
  sysIntEnable(int_level);
  logMsg("pvme331 int set for int 0x%x\n",int_level);
  /*  cards[cardNum].card->trg_cntrl_reg = 0x81;*/
  tg_mode = 0x80;
  tg_enable = 0x01;
  return(0);

}

static long read_wf_record(pwf)
struct waveformRecord	*pwf;
{
  short cardN;
  int i;
  unsigned short samples;

  unsigned short* us_thing = (unsigned short*)pwf->bptr;
  short* s_thing = (short*)pwf->bptr;
	
  if (debug_flag >5)
    logMsg("read_wf_record called...\n");

  cardN = pwf->inp.value.vmeio.card;
  if (debug_flag >5)
    logMsg("read_wf_record called with card number of %d\n",cardN);

  if (checkLink(cardN) == ERROR)
    return(ERROR);
  cards[cardN].card->trg_cntrl_reg = 0x80;

  switch(pwf->ftvl)
    {
    case DBF_USHORT:
      /*	unsigned short* us_thing = (unsigned short*)pwf->bptr; */
	for (i=0; i<pwf->nelm;i++)
	  {
	    samples=cards[cardN].card->data_reg;
	    us_thing[i]=(unsigned short)(samples);
	  }
	pwf->nord=i;
	cards[cardN].card->trg_cntrl_reg = (tg_mode | tg_enable);
	break;
    case DBF_SHORT:
      /*	short* s_thing = (short*)pwf->bptr; */
	for (i=0; i<pwf->nelm;i++)
	  {
	    samples=cards[cardN].card->data_reg;
	    s_thing[i]=(short)(samples);
	  }
	pwf->nord=i;
	cards[cardN].card->trg_cntrl_reg = (tg_mode|tg_enable);
	break;
    default:
      logMsg("devPVME331: Invalid data type\n");
    }
  cards[cardN].card->rst_reg=0x04;
  cards[cardN].card->trg_cntrl_reg = (tg_mode|tg_enable);
  return(0);
}

#define	ADDING  	0
#define DELETING	1

static long get_wf_int_info(cmd,pwf,ppvt)
int                    cmd;
struct waveformRecord  *pwf;
IOSCANPVT              *ppvt;
{
  short                  cardN;

  cardN = pwf->inp.value.vmeio.card;
  *ppvt = cards[cardN].ioscanpvt;
  logMsg("pvme331 intInfo. 0x%X\n",*ppvt);
  cards[cardN].card->trg_cntrl_reg = (unsigned char)((tg_mode)|(0x01));
  if (debug_flag >5) logMsg("WF_INT CALLED\n");
  return(0);
}

/**************************************************************************
 *
 * Make sure card number is valid
 *
 **************************************************************************/
static int checkLink(cardN)
short   cardN;
{
  if (cardN >= pvme331_num_links)
    return(ERROR);
  if (cards[cardN].card == NULL)
    {
      logMsg("No PVME331 with this number = %d\n",cardN);
      return(ERROR);
    }

  if (debug_flag >10)
    logMsg("Yes you have PVME331 with card No= %d\n",cardN);
  return(OK);
}