/* pseudo-device for RCI. This device responds to certain RCI ioctls:

	RCIIOC_CONFIRM   _IO(R,1)
	RCIIOC_EXECUTE   _IOW(R,3,RCI_fxn_arg)
	RCIIOC_HEARTBEAT _IOW(R,7,RCI_heartbeat)
	RCIIOC_ONCRASH   _IOW(R,7,int)
	RCIIOC_GETCRASH  _IOR(R,8,CRASHINFO)
	RCIIOC_INTERFACE _IOR(R,9,RCI_interface)
	RCIIOC_TIMEOUT   _IOR(R,10,RCI_timeout)
*/

#include "genrci.h"

#if defined(RCI) && (NGENRCI > 0)

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <rciext/rci.h>
#include <rciext/rciioctl.h>
#include <rciext/spl.h>

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

RCIINFO genrcirciinfo[NGENRCI];

genrciprobe(reg)
caddr_t reg;
{
	int i;

	for (i=0;i<NGENRCI;i++)
	 { RCI_cleari(&genrcirciinfo[i]);
	 }
}

genrciopen(dev,flag)
dev_t dev;
int flag;
{
	int unit;
	int exclusive;
	static int didprobe = 0;

	if (! didprobe)
	 { genrciprobe((caddr_t)0);
	   didprobe = 1;
	 }
	RCI_init();
	unit = minor(dev);
	exclusive = RCI_UNIT_P(unit);
	unit = RCI_REAL_UNIT(unit);
	if ((unit < 0) || (unit >= NGENRCI))
	 { return(ENXIO);
	 }
	if (exclusive)
	 { int s;
	   if (! RCI_alive())
	    { return(ENXIO);
	    }
	   s = splhigh();
	   if (genrcirciinfo[unit].flags & RCI_OPEN)
	    { splx(s);
	      return(EBUSY);
	    }
	   if (!RCI_openi (&genrcirciinfo[unit]))
	    { splx(s);
	      return(ENFILE);
	    }
	   splx(s);
	 }
	else
	 { int s;
	   s = splhigh();
	   if (genrcirciinfo[unit].flags & RCI_BUSY)
	    { splx(s);
	      return(EBUSY);
	    }
	   genrcirciinfo[unit].flags |= RCI_OPEN;
	   splx(s);
	 }
	return (0);
}

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

 unit = minor(dev);
 RCI_closei(&genrcirciinfo[RCI_REAL_UNIT(unit)]);
 return (0);
}

genrciioctl(dev,cmd,data,flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
	int unit;
	int s;
	int error = 0;

	unit = minor(dev);
	switch (cmd)
	 { default:
	    { error = ENOTTY;
	      break;
	    }
	   case RCIIOC_CONFIRM:
	    { if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      break;
	    }
	   case RCIIOC_ATTACH:
	    { error = ENOTTY;
	      break;
	    }
	   case RCIIOC_EXECUTE:
	    { if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCIINFO *rcii;
		 struct RCI_fxn_arg *ap;
		 rcii = &genrcirciinfo[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;
	    }
	   case RCIIOC_DETACH:
	    { error = ENOTTY;
	      break;
	    }
	   case RCIIOC_REQSIGS:
	    { error = ENOTTY;
	      break;
	    }
	   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 = &genrcirciinfo[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;
	    }
	   case RCIIOC_ONCRASH:
	    { if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { s = splhigh();
		 genrcirciinfo[RCI_REAL_UNIT(unit)].crashsig = * (int *) data;
		 splx(s);
	       }
	      break;
	    }
	   case RCIIOC_GETCRASH:
	    { if (! RCI_UNIT_P(unit))
	       { error = ENOTTY;
	       }
	      else
	       { RCIINFO *rcii;
		 rcii = &genrcirciinfo[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;
	    }
	   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( &genrcirciinfo[RCI_REAL_UNIT(unit)],
				    (struct RCI_timeout *)data );
	       }
	      break;
	    }
	 }
	return(error);
}

#endif
