/*
 * This file is a skeleton RCI interface driver.  It supports all the
 *  RCI ioctls, but does not have any of the normal system interface a
 *  driver usually has.  This is intended to be merged into an existing
 *  device driver to make that device support RCI.  This is written as
 *  a driver for a device xxx; most of the kernel interface structures
 *  are omitted; and only those driver routines which have interesting
 *  stuff in them are mentioned.
 */

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

/* This takes a unit number as an argument and tells whether it is an	 */
/*  RCI exclusive-open unit or not.  You may want to change this if the	 */
/*  unit number for your device already has magic bits.			 */

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

/* This complements the above definition.  It takes a unit number and	 */
/*  returns the "real" unit number.  This may be called regardless of	 */
/*  whether or not RCI_UNIT_P is true of the unit number.		 */

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

RCIINFO xxxrciinfo[NXXX];

xxxprobe(reg)
caddr_t reg;
{
 int i;

 for (i=0;i<NXXX;i++)
  { RCI_cleari(&xxxrciinfo[i]);
  }
 RCI_init();
 /* whatever your probe routine normally does */
}

xxxopen(dev,flag)
dev_t dev;
int flag;
{
 int unit;
 int exclusive;

 unit = minor(dev);
 exclusive = RCI_UNIT_P(unit);
 unit = RCI_REAL_UNIT(unit);
 if ((unit < 0) || (unit >= NXXX))
  { return(ENXIO);
  }
 if (exclusive)
  { int s;
    if (! RCI_alive())
     { return(ENXIO);
     }
    s = splhigh();
    if (xxxrciinfo[unit].flags & RCI_OPEN)
     { splx(s);
       return(EBUSY);
     }
    if (!RCI_openi (&rciinfo[unit]))
     { return(ENFILE);
     }
    splx(s);
    /* anything special you want to do when opening an RCI unit */
  }
 else
  { int s;
    s = splhigh();
    if (xxxrciinfo[unit].flags & RCI_BUSY)
     { splx(s);
       return(EBUSY);
     }
    xxxrciinfo[unit].flags |= RCI_OPEN;
    splx(s);
    /* anything special you want to do when opening an non-RCI unit */
  }
 /* whatever your open routine normally does */
}

xxxclose(dev,flag)
dev_t dev;
int flag;
{
 int unit;
 int s;

 unit = minor(dev);
 RCI_closei(&xxxrciinfo[RCI_REAL_UNIT(unit)]);
 /* whatever your close routine normally does */
}

xxxioctl(dev,cmd,data,flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
 int unit;
 int s;
 int error;

 error = 0;
 unit = minor(dev);
 switch (cmd)
  { /* Usually you will have something like the following default: case	 */
    /*  to catch unused ioctls						 */
    default:
       error = ENOTTY;
       break;
    /* Insert any ioctls your device has into this switch */
    /* Here are the skeleton RCI ioctls: */
    /* Confirm RCI-ness of device */
    case RCIIOC_CONFIRM:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       break;
    /* Attach a routine to the device interrupt */
    case RCIIOC_ATTACH:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ RCIINFO *rcii;
	  struct RCI_fxn_arg *ap;
	  rcii = &xxxrciinfo[RCI_REAL_UNIT(unit)];
	  s = splhigh();
	  RCI_setup_switch(rcii);
	  rcii->ifxn = ap->function;
	  rcii->iarg = ap->argument;
	  /* you may want to turn interrupts on at this point */
	  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 = &xxxrciinfo[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;
     case RCIIOC_KRNLEXEC:
      { struct RCI_fxn_arg4 *ap;

	if (! RCI_UNIT_P(unit))
	 { error = ENOTTY;
	 }
	else
	 { ap = (struct RCI_fxn_arg4 *)data;
	   ap->rval = (*ap->fxn)(ap->arg[0],ap->arg[1],
				 ap->arg[2],ap->arg[3]);
	 }
	break;
      }
     case RCIIOC_KUTEXEC:
      { extern int RCI_kutexec();

	if (! RCI_UNIT_P(unit))
	 { error = ENOTTY;
	 }
	else
	 { kut_callit (RCI_kutexec, (int)data);
	 }
	break;
      }
    /* Detach from the device interrupt */
    case RCIIOC_DETACH:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ s = splhigh();
	  xxxrciinfo[RCI_REAL_UNIT(unit)].ifxn = 0;
	  /* you may want to turn interrupts off here */
	  splx(s);
	}
       break;
    /* Request signals from the slave task */
    case RCIIOC_REQSIGS:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ s = splhigh();
	  xxxrciinfo[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 = &xxxrciinfo[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();
	  xxxrciinfo[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 = &xxxrciinfo[RCI_REAL_UNIT(unit)];
	  if (copyout ((caddr_t)&rcii->lastcrash, 
		       (caddr_t)*(CRASHINFO**)data, 
			sizeof(CRASHINFO)) == EFAULT)
	   { return (EFAULT);
	   }
	  rcii->lastcrash.type = CI_NULL;
	}
       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( &xxxrciinfo[RCI_REAL_UNIT(unit)],
			     (struct RCI_timeout *)data );
	}
       break;
  }
 return(error);
}

xxxintr(unit)
int unit;
{
 register RCIINFO *p;

 p = &xxxrciinfo[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;
	}
     }
    /* anything special that should happen on an RCI interrupt, such as	 */
    /*  a return before your code below gets executed			 */
  }
 /* Your normal interrupt handling code goes here */
}

#endif
