/*
 * This is the driver for an RCI DRV11-J device. It basically just 
 * provides a connect to interrupt for the port.
 *
 * This driver is cloned directly from the template UNIX RCI driver
 * in ../rciext/rci_template.c.
 */

#include "drvj.h"
#if NDRVJ > 0

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/errno.h"
#include "../rciext/crashinfo.h"
#include "../rciext/kacomm.h"
#include "../rciext/rci.h"
#include "../rciext/rciioctl.h"

#include "../machine/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"

#include "../rciext/drvjreg.h"

#define RCI_UNIT_P(unit) ((unit)&0x80)
#define RCI_REAL_UNIT(unit) ((unit)&0x7f)

RCIINFO drvjrciinfo[NDRVJ];
int     drvjprobe(), drvjattach(), drvjintr(), drvj_timeout();
struct  uba_device *drvjdinfo[NDRVJ];
u_short drvjstd[] = { 0 };
struct  uba_driver drvjdriver = 
    { drvjprobe, 0, drvjattach, 0, drvjstd, "drvj", drvjdinfo };

drvjprobe(reg, ui)
caddr_t reg;
struct uba_device *ui;
{
	register int br, cvec;
	register int i;
	struct drvjdevice *drvj = (struct drvjdevice *)ui->ui_addr;

	for (i=0;i<NDRVJ;i++)
	 { RCI_cleari(&drvjrciinfo[i]);
	 }
	RCI_init();
	if (ui->ui_flags == 0)
	 { printf("Missing vector configured on drvj%d\n",ui->ui_unit);
	   return(0);
	 }
	/* reset the device and setup 1 interrupt vector on group 1 */

	drvj->icmd0 = ICMD_RESET;
	drvj->icmd1 = ICMD_RESET;

	drvj->icmd0 = ICMD_VECTOR | (0x08);
	drvj->ibuf0 = 0xff & ((ui->ui_flags) >> 2);

        br = 0x17;			/* Set IPL of the device */
	cvec = ui->ui_flags;
	return (sizeof(struct drvjdevice));
}

drvjattach()
{	
	return(0);	
}

drvjopen(dev,flag)
dev_t dev;
int flag;
{
	int unit = RCI_REAL_UNIT(minor(dev));
	int exclusive = RCI_UNIT_P(minor(dev));
	struct uba_device *ui = drvjdinfo[unit];

	if (unit < 0 || unit >= NDRVJ || !ui || !ui->ui_alive)
	 { return(ENXIO);
	 }
	if (exclusive)
	 { int s;
	   if (! RCI_alive())
	    { return(ENXIO);
	    }
	   s = splhigh();
	   if (drvjrciinfo[unit].flags & RCI_OPEN)
	    { splx(s);
	      return(EBUSY);
	    }
	   drvjrciinfo[unit].flags |= RCI_OPEN|RCI_BUSY;
	   drvjrciinfo[unit].pid = u.u_procp->p_pid;
	   splx(s);
	 }
	else
	 { int s;
	   s = splhigh();
	   if (drvjrciinfo[unit].flags & RCI_BUSY)
	    { splx(s);
	      return(EBUSY);
	    }
	   drvjrciinfo[unit].flags |= RCI_OPEN;
	   splx(s);
	 }
	return(0);
}

drvjclose(dev,flag)
dev_t dev;
int flag;
{
	int unit = RCI_REAL_UNIT(minor(dev));
	struct uba_device *ui = drvjdinfo[unit];
	struct drvjdevice *drvj;

	if (unit < 0 || unit >= NDRVJ || !ui || !ui->ui_alive)
	 { return(ENXIO);
	 }
	drvj = (struct drvjdevice *)ui->ui_addr;

	/* turn interrupts off */
	drvj->csra &= ~(DRJCSR_IE);

	RCI_closei(&drvjrciinfo[unit]);
	return (0);
}

drvjioctl(dev,cmd,data,flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
	int unit = RCI_REAL_UNIT(minor(dev));
	struct uba_device *ui = drvjdinfo[unit];
	int error = 0;
	int s;

	if (unit < 0 || unit >= NDRVJ || !ui || !ui->ui_alive)
	 { return(ENXIO);
	 }
	switch (cmd)
	 {
	   default:
	      error = ENOTTY;
	      break;
	   case RCIIOC_CONFIRM:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      break;
	   case RCIIOC_ATTACH:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCIINFO *rcii;
		 struct RCI_fxn_arg *ap;
		 struct drvjdevice *drvj = (struct drvjdevice *)ui->ui_addr;

		 rcii = &drvjrciinfo[RCI_REAL_UNIT(unit)];
		 ap = (struct RCI_fxn_arg *) data;
		 s = splhigh();
		 RCI_setup_switch(rcii);
		 rcii->ifxn = ap->function;
		 rcii->iarg = ap->argument;

		 /* turn interrupts on */
		
		 drvj->icmd0 = ICMD_SELW_AC;	/* set autoACK for  */
		 drvj->ibuf0 = 0xff;		/*    all bits */
		 drvj->icmd0 = ICMD_CLEAR_MR;	/* clear mask bit 0 */
		 drvj->icmd0 = ICMD_SELR_ARM;	/* arm interrupts */
		 drvj->icmd0 = ICMD_MODE | DRJMODE_HIGHEDGE;
		 drvj->csra |= DRJCSR_IE;

		 splx(s);
	       }
	      break;
    /* Execute a routine */
	   case RCIIOC_EXECUTE:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCIINFO *rcii;
		 struct RCI_fxn_arg *ap;
		 rcii = &drvjrciinfo[RCI_REAL_UNIT(unit)];
		 ap = (struct RCI_fxn_arg *) data;
		 if (RCI_kut_call(ap->function,ap->argument,rcii))
		  { RCI_signal(rcii->pid,rcii->crashsig);
		  }
	       }
	      break;
    /* Detach from the device interrupt */
	    case RCIIOC_DETACH:
	       if (! RCI_UNIT_P(unit))
		{ error = ENOTTY;
		} 
	       else
		{ struct drvjdevice *drvj = (struct drvjdevice *)ui->ui_addr;

		  s = splhigh();
		  drvjrciinfo[RCI_REAL_UNIT(unit)].ifxn = 0;

		  /* turn interrupts off */
		  drvj->csra &= ~(DRJCSR_IE);

		  splx(s);
		}
	       break;
	   /* Request signals from the slave task */
	   case RCIIOC_REQSIGS:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { s = splhigh();
		 drvjrciinfo[RCI_REAL_UNIT(unit)].flags |= RCI_SIGS;
		 splx(s);
	       }
	      break;
	  /* Request a heartbeat */
	   case RCIIOC_HEARTBEAT:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { register RCIINFO *rcii;
		 register struct RCI_heartbeat *ap;
		 int s;
		 int RCI_heartbeat();
		 rcii = &drvjrciinfo[RCI_REAL_UNIT(unit)];
		 ap = (struct RCI_heartbeat *) data;
		 s = splhigh();
		 RCI_setup_switch(rcii);
		 untimeout(RCI_heartbeat,(caddr_t)rcii);
		 rcii->hbticks = ap->howoften;
		 rcii->hbfxn = ap->function;
		 rcii->hbarg = ap->argument;
		 if ((rcii->hbticks > 0) && rcii->hbfxn)
		  { timeout(RCI_heartbeat,(caddr_t)rcii,rcii->hbticks);
		  }
		 splx(s);
	       }
	      break;
	   /* Set up crash signal */
	   case RCIIOC_ONCRASH:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { s = splhigh();
		 drvjrciinfo[RCI_REAL_UNIT(unit)].crashsig = * (int *) data;
		 splx(s);
	       }
	      break;
	   /* Get crash info */
	   case RCIIOC_GETCRASH:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCIINFO *rcii;
		 rcii = &drvjrciinfo[RCI_REAL_UNIT(unit)];
		 error = copyout((caddr_t)&rcii->lastcrash,
			   	 (caddr_t)*(CRASHINFO**)data, 
				  sizeof(CRASHINFO));
		 rcii->lastcrash.type = CI_NULL;
		 splx(s);
	       }
	      break;
	   /* Get interface information */
	   case RCIIOC_INTERFACE:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCI_get_interface((struct RCI_interface *)data);
	       }
	      break;
	   case RCIIOC_TIMEOUT:
	      if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCI_setup_timeout( &drvjrciinfo[RCI_REAL_UNIT(unit)],
				    (struct RCI_timeout *)data );
	       }
	      break;
	 }
	return(error);
 }
 
drvjintr(unit)
int unit;
{
	register RCIINFO *p;
     	struct drvjdevice *drvjaddr = (struct drvjdevice*)drvjdinfo[unit]->ui_addr;
	int s;

	/* translate the hard unit number into a soft unit no. */

	p = &drvjrciinfo[unit];
	if (p->flags & RCI_BUSY)
	 { if (p->ifxn)
	    { if (RCI_switch(p,p->ifxn,p->iarg))
	       { RCI_signal(p->pid,p->crashsig);
		 p->ifxn = 0;
	       }
	    }
	   return(1);
	 }
	printf ("drvj%d, stray interrupt\n", unit);
}

#endif
