/* devXxBugRac.c  */

/* Device Support Routines for BUF RAC commands */
/*
 *      Original Author: Ned Arnold (based on work by Jim Kowalkowski)
 *      Date: 02/01/93
 *
 *      Experimental Physics and Industrial Control System (EPICS)
 *
 *      Copyright 1991, the Regents of the University of California,
 *      and the University of Chicago Board of Governors.
 *
 *      This software was produced under  U.S. Government contracts:
 *      (W-7405-ENG-36) at the Los Alamos National Laboratory,
 *      and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *      Initial development by:
 *              The Controls and Automation Group (AT-8)
 *              Ground Test Accelerator
 *              Accelerator Technology Division
 *              Los Alamos National Laboratory
 *
 *      Co-developed with
 *              The Controls and Computing Group
 *              Accelerator Systems Division
 *              Advanced Photon Source
 *              Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 * .01  02-01093        nda     initialized
 * .02  06-03-94        nda     removed automatic retry on a TIMEOUT
 * .03  06-03-94        nda     removed drvBitBus.qReq at init time 
 *      ...
 */



#include	<vxWorks.h>
#include	<vme.h>
#include	<types.h>
#include	<stdioLib.h>
#include	<string.h>
#include	<math.h>
#include	<iv.h>

#include	<alarm.h>
#include        <callback.h>
#include	<dbRecType.h>
#include	<dbDefs.h>
#include	<dbAccess.h>
#include	<dbCommon.h>
#include	<fast_lock.h>
#include    <recSup.h>
#include	<devSup.h>
#include	<drvSup.h>
#include	<dbScan.h>
#include	<special.h>
#include	<module_types.h>
#include	<eventRecord.h>
#include	<drvBitBusInterface.h>
#include	<aiRecord.h>
#include	<aoRecord.h>
#include	<choiceAo.h>
#include	<biRecord.h>
#include	<boRecord.h>
#include	<longinRecord.h>
#include	<longoutRecord.h>
#include	<mbboRecord.h>
#include	<mbbiRecord.h>

/* types */
#define AI		0x01
#define AO		0x02
#define BI		0x03
#define BO		0x04
#define LI		0x05
#define LO		0x06
#define MBBI	0x07
#define MBBO	0x08


/* Define forward references     */
long report();
long init();
static long init_ai();
static long init_ao();
static long init_bo();
static long init_bi();
static long init_li();
long init_lo();
static long init_mbbi();
static long init_mbbo();
long get_ioint_info();
static long bug_rac();

/* Create the dsets for devXxBugRac */
typedef struct {
	long		number;
	DEVSUPFUN	report;
	DEVSUPFUN	init;
	DEVSUPFUN	init_record;
	DEVSUPFUN	get_ioint_info;
	DEVSUPFUN	read_write;
	DEVSUPFUN	special_linconv;
} IODSET;

/* DEFINE SUPPORTED RAC COMMANDS
#define RESET_STATION	0x00	/* BO record */
#define GET_FUNCTION_ID	0x03	/* LI record */

/* xxDSET devXxxx={ 5,report,init,init_rec,get_ioint_info,bug_rac}; */

IODSET devBoBugRac = { 5, NULL, NULL, init_bo, NULL, bug_rac };
IODSET devLiBugRac = { 5, NULL, NULL, init_li, NULL, bug_rac };

/* forward references			*/

static void get_data();
static void send_cntl_trans();
extern struct drvBitBusEt drvBitBus;

volatile int bugRacDebug=0;

#define OFF		0
#define ON		1

#ifdef NODEBUG
#define Debug(FMT,V) ;
#else
#define Debug(FMT,V) {	if(bugRacDebug) \
			{\
				printf("%s(%d):",__FILE__,__LINE__); \
				printf(FMT,V); \
			} \
		     }
#endif



struct dprivate {
	struct dpvtBitBusHead bitbus;
	char type;
	unsigned char cmd;
	struct dbCommon	*precord; /* at end for callback to get it */
};
 
static int io_callback(struct dprivate *pcallback)
{
	struct dbCommon *precord;
	struct rset     *prset;

	precord=pcallback->precord;
	prset=(struct rset *)(precord->rset);

	dbScanLock(precord);
	(*prset->process)(precord);
	dbScanUnlock(precord);
}

static struct dprivate * set_bitbus(int prio,unsigned char cmd,struct bitbusio *pbitbusio)
{
	struct dprivate *my_dpvt;

        my_dpvt=(struct dprivate *)(malloc(sizeof(struct dprivate)));

	my_dpvt->bitbus.finishProc=io_callback;
	my_dpvt->bitbus.psyncSem=(SEM_ID *)NULL;
	my_dpvt->bitbus.priority=prio;
	my_dpvt->bitbus.link=pbitbusio->link;
	my_dpvt->bitbus.rxMaxLen=9;
	my_dpvt->bitbus.ageLimit=240;

	my_dpvt->bitbus.txMsg.length=9;
	my_dpvt->bitbus.txMsg.route=0x40;
	my_dpvt->bitbus.txMsg.node=pbitbusio->node;
	my_dpvt->bitbus.txMsg.tasks=0;
	my_dpvt->bitbus.txMsg.cmd=cmd;
	my_dpvt->bitbus.txMsg.data=(unsigned char *)malloc(2);
	my_dpvt->bitbus.rxMsg.data=(unsigned char *)malloc(BB_MAX_DAT_LEN);

	my_dpvt->bitbus.txMsg.data[0]='\0';
	my_dpvt->bitbus.txMsg.data[1]='\0';

	Debug("command sent:(%02.2x)\n", my_dpvt->bitbus.txMsg.cmd);

	return(my_dpvt);
}


static long init_ai(struct aiRecord *ai)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&ai->inp.value);
	struct dprivate *my_dpvt;
	int ret;

	Debug("init ai invoked\n",0);

	/* type must be an BITBUS_IO */
	if(ai->inp.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)ai,
		    "devXxBugRac (init_record) Illegal IN Bus Type");
		return(S_dev_badBus);
	}


	my_dpvt=set_bitbus(ai->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)ai;
        my_dpvt->type = AI;

        ai->dpvt=(void *)my_dpvt; 
	return(0);
}


static long init_ao(struct aoRecord *ao)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&ao->out.value);
	struct dprivate *my_dpvt;

	Debug("init ao invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(ao->out.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)ao,
		    "devXxBugRac (init_record) Illegal OUT Bus Type");
		return(S_dev_badBus);
	}

	my_dpvt=set_bitbus(ao->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)ao;
        my_dpvt->type = AO;

	ao->dpvt=(void *)my_dpvt;

	return(0);
}


static long init_bi(struct biRecord *bi)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&bi->inp.value);
	struct dprivate *my_dpvt;
	unsigned char cmd = 0;

	Debug("init bi invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(bi->inp.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)bi,
		    "devXxBugRac (init_record) Illegal IN Bus Type");
		return(S_dev_badBus);
	}

 
 	my_dpvt=set_bitbus(bi->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)bi;
        my_dpvt->type = BI;
        my_dpvt->cmd = cmd;
	bi->dpvt=(void *)my_dpvt;

	return(0);
}


static long init_bo(struct boRecord *bo)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&bo->out.value);
	struct dprivate *my_dpvt;

	Debug("init bo invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(bo->out.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)bo,
		    "devXxBugRac (init_record) Illegal IN Bus Type");
		return(S_dev_badBus);
	}

	switch(pbitbusio->signal)
	{
	default:
		recGblRecordError(S_dev_badBus,(void *)bo,
		    "devXxBugRac (init_record) Illegal IN signal number");
		return(S_dev_badBus);
	case 0:  /* signal 0 is a bo record */
 		break;
	}
 
	my_dpvt=set_bitbus(bo->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)bo;
        my_dpvt->type = BO;
        my_dpvt->cmd = pbitbusio->signal;

	bo->dpvt=(void *)my_dpvt;

	return(0);
}


static long init_li(struct longinRecord *li)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&li->inp.value);
	struct dprivate *my_dpvt;
	unsigned char cmd = 0;

	Debug("init li invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(li->inp.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)li,
		    "devXxBugRac (init_record) Illegal IN Bus Type");
		return(S_dev_badBus);
	}

	switch(pbitbusio->signal)
	{
	default:
		recGblRecordError(S_dev_badBus,(void *)li,
		    "devXxBugRac (init_record) Illegal IN signal number");
		return(S_dev_badBus);
	case 3:  /* signal 3 is a li record */
 		break;
	}

 	my_dpvt=set_bitbus(li->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)li;
        my_dpvt->type = LI;
        my_dpvt->cmd = pbitbusio->signal;
	li->dpvt=(void *)my_dpvt;

	return(0);
}


static long init_mbbi(struct mbbiRecord *mbbi)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&mbbi->inp.value);
	struct dprivate *my_dpvt;
	unsigned char cmd = 0;

	Debug("init mbbi invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(mbbi->inp.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)mbbi,
		    "devXxBugRac (init_record) Illegal IN Bus Type");
		return(S_dev_badBus);
	}

 	my_dpvt=set_bitbus(mbbi->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)mbbi;
        my_dpvt->type = MBBI;
        my_dpvt->cmd = cmd;
	mbbi->dpvt=(void *)my_dpvt;

	return(0);
}


static long init_mbbo(struct mbboRecord *mbbo)
{
	struct bitbusio *pbitbusio = (struct bitbusio *)(&mbbo->out.value);
	struct dprivate *my_dpvt;
	unsigned char cmd = 0;

	Debug("init mbbo invoked\n",0);
 
	/* out must be an BITBUS_IO */
	if(mbbo->out.type!=BITBUS_IO)
	{
		recGblRecordError(S_dev_badBus,(void *)mbbo,
		    "devXxBugRac (init_record) Illegal OUT Bus Type");
		return(S_dev_badBus);
	}

	if(pbitbusio->signal >= 8 )
	{
		recGblRecordError(S_dev_badBus,(void *)mbbo,
	    	"devXxBugRac (init_record) Illegal OUT signal number");
		return(S_dev_badBus);
	}
 
 	my_dpvt=set_bitbus(mbbo->prio,pbitbusio->signal,pbitbusio);
        my_dpvt->precord = (struct dbCommon *)mbbo;
        my_dpvt->type = MBBO;
        my_dpvt->cmd = cmd;

	mbbo->dpvt=(void *)my_dpvt;

	return(0);
}


static long bug_rac(struct dbCommon *io)
{
	struct dprivate *my_dpvt=(struct dprivate *)io->dpvt;
	struct dpvtBitBusHead *pbitbus;

	if(!io->dpvt) return(S_dev_NoInit);

	pbitbus=&(my_dpvt->bitbus);

	if(io->pact==TRUE)
	{
		Debug("Bitbus message completed \n",0);
 
		/* a transaction to bitbus has completed */
		switch(pbitbus->status)
		{
		case BB_OK:
			Debug("Getting data \n",0);
			get_data(io);
			return(0);
		default:
			recGblSetSevr(io,WRITE_ALARM,MAJOR_ALARM);
			recGblRecordError(S_dev_badBus,(void *)io,
	    			"devXxBugRac (iobug_rdwr) BitBus Error!");
			return(S_dev_badBus);
		}
	}
	else
	{
		/* data needs to be sent to bitbus */
		Debug("Sending transaction \n",0);
		send_cntl_trans(io);
	}

	io->pact=TRUE;

	/* queue the command */
	if((*drvBitBus.qReq)(pbitbus,BB_Q_LOW)<0)
	{
		recGblSetSevr(io,WRITE_ALARM,MAJOR_ALARM);
		recGblRecordError(S_dev_badBus,(void *)io,
	    	"devXxBugRac (init_record) Initial BitBus message failed");
		return(S_dev_badBus);
	}
	Debug("Queued transaction \n",0);

	return(0);
}

static void get_data(struct dbCommon *io)
{
        struct dprivate *my_dpvt=(struct dprivate *)io->dpvt;
    struct dpvtBitBusHead *pbitbus;
	struct boRecord *bo;
	struct biRecord *bi;
	struct aoRecord *ao;
	struct aiRecord *ai;
	struct longoutRecord *lo;
	struct longinRecord *li;
	struct mbboRecord *mbbo;
	struct mbbiRecord *mbbi;
	long value;
	unsigned long uvalue;
 
    pbitbus=&(my_dpvt->bitbus);

	Debug("Command return: 0x%04.4X\n",pbitbus->rxMsg.cmd);
	Debug("byte 1 of return: 0x%04.4X\n",pbitbus->rxMsg.data[0]);
	Debug("byte 2 of return: 0x%04.4X\n",pbitbus->rxMsg.data[1]);

	switch(my_dpvt->type)
	{
	case LI:
		li=(struct longinRecord *)io;
	    switch(my_dpvt->bitbus.txMsg.cmd)
		{
		case GET_FUNCTION_ID:
			/* extract the second ID code (task 1) */
 			li->val = pbitbus->rxMsg.data[1];
			li->udf = 0;
			if ((pbitbus->rxMsg.cmd) != 0) 
			    {
			    recGblSetSevr(li, READ_ALARM, INVALID_ALARM);
			    }
			break;
		default:
			break;
	    }
 	default:
		break;
	}

	return;
}

static void send_cntl_trans(struct dbCommon *io)
{
        struct dprivate *my_dpvt=(struct dprivate *)io->dpvt;
        struct dpvtBitBusHead *pbitbus;
	struct boRecord *bo;
	struct aoRecord *ao;
	struct mbboRecord *mbbo;
	short lsb,msb;
 
        pbitbus=&(my_dpvt->bitbus);

	pbitbus->finishProc=io_callback;
	pbitbus->psyncSem=(SEM_ID *)NULL;
	pbitbus->priority=io->prio;
	pbitbus->txMsg.cmd=my_dpvt->cmd;
	 	
	switch(my_dpvt->type)
	{
	case BO:
		bo=(struct boRecord *)io;
  		break;
	case MBBO:
		mbbo=(struct mbboRecord *)io;
 		break;
	case AO:
		ao=(struct aoRecord *)io;
		pbitbus->txMsg.data[0]=0;
		pbitbus->txMsg.data[1]=0;
 		break;
	default:
		break;
	}

	my_dpvt->precord=(struct dbCommon *)io;

	Debug("Command send: 0x%02.2X\n",pbitbus->txMsg.cmd);
	Debug("byte 1 sent: 0x%02.2X\n",(unsigned char)pbitbus->txMsg.data[0]);
	Debug("byte 2 sent: 0x%02.2X\n",(unsigned char)pbitbus->txMsg.data[1]);

	return;
}
