/* The robot communication protocol works this way:

   VAX opens device and clears interrupts. All bits in all csr's
   should be clear all round.

   Interrupts are enabled on the first vector.
  
   VAX interrupts robot to get the ball rolling.

   Robot prepares data buffer and interrupts VAX. When any side has data
   ready for the other side, it sets CSR bit 1. The other side will
   acknowledge this by setting it's bit 1, which will show up as REQB.
*/

#include "pri.h"                        /* File defines number of fifo units*/
#if NPRI > 0                            /* If there are any fifo devices .. */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../machine/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
#include "../h/conf.h"
#include "../machine/mtpr.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/uio.h"
#include "../h/vm.h"
#include "../h/vmmac.h"

#include "prireg.h"
#include "fiforeg.h"

#ifdef RCI
#define RCIDEV	1
#endif

#if RCIDEV
#include "../rciext/kacrash.h"
#include "../rciext/kacomm.h"
#include "../rciext/rci.h"
#include "../rciext/rciioctl.h"

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

RCIINFO prirciinfo[NPRI];

#endif

/* Internal driver states */

#define OFFS       0                    /* The off state */
#define OPENS      1                    /* The initial state */
#define FUNCTIONS  2                    /* Function active state */

/* timeout stuff */

#define TIMEOUT_SET     0001000         /* Timeout is active */
#define TIMEOUT_REQ     0002000         /* Timeout has been requested */
#define E_USER          0x80000         /* User specified termination */

#define clear_pri_timeout() \
        untimeout(pri_timeout, unit)
#define set_pri_timeout(t) \
        timeout(pri_timeout, unit,t)

int wakeup();

struct _pri_softc {
        caddr_t read_buffer;    /* user read buffer address             */
        caddr_t write_buffer;   /* user write buffer address            */
        int     (*function1)(); /* Address of user function1            */
        int     (*function2)(); /* Address of user function2            */
        int     *terminus;      /* Address of terminus flag             */
	struct proc *RCIp;	/* pointer to RCI process		*/
        int     nice;           /* process priority                     */
        int     state;          /* current state of the driver          */
        unsigned flags;         /* driver control flags                 */
        int     signal;         /* signal to send on bad condition      */
        long    sigpid;         /* Process to send signal to            */
        unsigned int timeout;   /* Timeout time in UNIX clock ticks     */
} pri_softc[NPRI];

int     priprobe(), priattach(), priintr(), pri_timeout();

struct  uba_device *pridinfo[NPRI];
u_short pristd[] = { 0 };

/* define entry points for driver routines */

struct  uba_driver pridriver = 
    { priprobe, 0, priattach, 0, pristd, "pri", pridinfo };

/* PRI_PROBE: Called at boot time by the system to verify the existence of 
   the device */

priprobe(reg, ui)
        caddr_t reg;
	struct uba_device *ui;
{
        register int br, cvec;
        register int i;

	if (ui->ui_flags == 0)
	 { printf("Missing vector configured on pri%d\n",ui->ui_unit);
	   return(0);
	 }
        br = 0x17;			/* Set IPL of the device */
	cvec = ui->ui_flags;

#if RCIDEV
	for (i=0;i<NPRI;i++)
	 { RCI_cleari (&prirciinfo[i]);
	 }
	RCI_init();
#endif
	/* We return 2 as the size of the structure. That's so the
	   RANYAN board can steal the obuf of the device as its
	   csr. We really should say

	   return (sizeof(struct pridevice));
	*/

	return (2);
}

/*ARGSUSED*/
priattach(ui)
register struct uba_device *ui;
{
 register int unit = ui->ui_unit;

 pri_softc [unit].state = OFFS;

 printf ("pri device %d attached at virtual address %x\n", 
	  ui->ui_unit, ui->ui_addr);
}

/* PRIOPEN: Called whenever the user does an open on the device */

priopen(dev, flag)
dev_t dev;
int flag;
{
 register struct _pri_softc *fc;
 register struct uba_device *ui;
 int unit = minor(dev);

#if RCIDEV
	int exclusive;

	exclusive = RCI_UNIT_P(unit);
	unit = RCI_REAL_UNIT(unit);
	if ((unit < 0) || (unit >= NPRI))
	 { return(ENXIO);
	 }
	if (exclusive)
	 { int s;
	   if (! RCI_alive())
	    { return(ENXIO);
	    }
	   s = splhigh();
	   if (prirciinfo[unit].flags & RCI_OPEN)
	    { splx(s);
	      return(EBUSY);
	    }
	   prirciinfo[unit].flags |= RCI_OPEN|RCI_BUSY;
	   prirciinfo[unit].pid = u.u_procp->p_pid;
	   splx(s);

	 }
	else
	 { int s;
	   s = splhigh();
	   if (prirciinfo[unit].flags & RCI_BUSY)
	    { splx(s);
	      return(EBUSY);
	    }
	   prirciinfo[unit].flags |= RCI_OPEN;
	   splx(s);
	 }
#endif

 fc = &pri_softc[unit];
 ui = pridinfo[unit];

 /* Check validity of device */

 if (unit >= NPRI ||                 /* if device # isn't valid */
     (ui = pridinfo[unit]) == 0 ||           /* or ui points nowhere */
     ui->ui_alive == 0 )                        /* or device wasn't found, */
  {                                             /* then exit with error */
    return (ENXIO);
  }
 if (fc->state != OFFS)                         /* Make sure device not busy */
  {
    return (EBUSY);
  }

 fc->state = OPENS;                             /* Enter initial state */
 fc->sigpid = u.u_procp->p_pid;                 /* get process pid */
 fc->flags = 0;                                 /* clear all flags */

 return (0);
}

/* PRICLOSE: Called when the process closes the unit */

priclose(dev, flag)
dev_t dev;
int flag;
{
 register struct _pri_softc *fc;
 register struct pridevice *priaddr;            /* pointer to device regs */
 int unit;
 int s;

 unit = RCI_REAL_UNIT(minor(dev));

#if RCIDEV
	RCI_closei(&prirciinfo[unit]);
#endif

 priaddr = (struct pridevice *) pridinfo[unit]->ui_addr;
 fc = &pri_softc[unit];

 priaddr->csr = 0;                              /* disable interrupts       */

 if (fc->flags & TIMEOUT_SET)                   /* remove any timeouts      */
  { clear_pri_timeout();
  }

 if (fc->state == FUNCTIONS)                    /* Unset FUNCTIONS mode */
  { u.u_procp->p_nice = fc->nice;
  }
 fc->state = OFFS;                              /* no process using unit now */

 return (0);
}

priread(dev,uio)
dev_t dev;
register struct uio *uio;                       /* user io structure */
{
 return (ENXIO);
}

priwrite(dev, uio)
dev_t dev;
register struct uio *uio;
{
 return (ENXIO);
}

pri_timeout (unit)
int unit;
{
 /* Called when the VAX has not heard from the LSI-11 for a while */

 register struct _pri_softc *fc;                /* Software control block */
 register struct pridevice *priaddr;            /* pointer to device regs */
 register old_spl;                              /* old ipl                */
 unsigned short dev_req;                        /* Any device requests    */
 int sigpid;					/* PID to signal	  */
 int sigsig;					/* signal to send	  */
 struct proc *sigproc;                          /* process to signal      */

 old_spl = splhigh();
 priaddr = (struct pridevice *) pridinfo[unit]->ui_addr;
 priaddr->csr = 0;
 fc = &pri_softc[unit]; 
 
 fc->flags &= ~TIMEOUT_SET;

 switch_to_RCI_process (fc->RCIp);

 if (dev_req)
  { *(fc->terminus) = (FIFO_ERROR | dev_req);
  }
 else
  { *(fc->terminus) = FIFO_TIMEOUT;
  }
 sigpid = fc->sigpid;
 sigsig = fc->signal;

 restore_old_process();

 sigproc = pfind(sigpid);
 if (sigproc)
  { psignal (sigproc, sigsig);
  }
 splx (old_spl);
}

/* PRIINTR: Called when the pri interrpts; either wakes up any read or
   write calls, or signals the process if the call was unsolicited */


priintr (unit)
int unit;
{
 struct _pri_softc *fc;                 /* Software control block */
 struct pridevice *priaddr;             /* pointer to device regs */
 struct proc *sigproc;                          /* process to signal      */
 int i;

#if RCIDEV
	register RCIINFO *p;
	 
	p = &prirciinfo[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;
	 }
#endif

 splhigh();                                        /* Maximum priority */

 priaddr = (struct pridevice *) pridinfo[unit]->ui_addr;    

 fc = &pri_softc[unit];

 switch (fc->state)
  { 
    case OPENS:                                 /* Else, is unit open? */
     {                                          /* Then signal if desired */
       if (fc->flags & I_SIGNAL && (sigproc = pfind (fc->sigpid)) != 0)
        { psignal(sigproc, fc->signal);
        }
       break;
     }
    case FUNCTIONS:                             /* Notorious function mode! */
     {
       extern kernel_user_traps;
       extern label_t kernel_user_catch;
       extern struct proc *kernel_user_proc;
       int wantsig;
       int sigpid;
       int sigsig;

       wantsig = 0;

       if ((priaddr->csr & PRI_IEA) == 0)    /* must be a bogon.... */
        { return;
        }

       if (fc->flags & TIMEOUT_SET)
        { clear_pri_timeout();
          fc->flags &= ~TIMEOUT_SET;
        }
    
       switch_to_RCI_process (fc->RCIp);

       kernel_user_proc = u.u_procp;    /* set up trap handler hack to */
       kernel_user_traps = 1;           /*  allow exceptions in kernel mode */
                                        /*  and stay alive */

       if (setjmp (&kernel_user_catch) != 0)
        { *(fc->terminus) = FIFO_KERNERROR;
          goto interrupt_end;
        }

       if (!priaddr->csr & PRI_REQB)
        { *(fc->terminus) = FIFO_NOT_EMPTY;
          goto interrupt_end;
        }

       /* Read in state information and then execute function1 */

#define SPINOUT	10000

        { register int count;                   /* Scratch counter        */
          register short *pri_buf;              /* PRI data register      */
          register short *pri_csr;              /* PRI csr register       */
          register short *data_ptr;             /* Points into data buffer*/
          register short data;
          register short bitTest;
          short checksum;                       /* computed checksum      */
	  short spinout;	

#define WAIT_FOR_SIGNAL()	spinout = SPINOUT; \
				while (*pri_csr == bitTest && --spinout) \
				   ; \
				if (!spinout) \
				 { *(fc->terminus) = FIFO_TIMEOUT; \
				   goto interrupt_end; \
				 }

          pri_buf = (short *)&priaddr->ibuf;
          pri_csr = (short *)&priaddr->csr;
          data_ptr = (short *)fc->read_buffer;
	  
	  bitTest = *pri_csr;			/* get initial csr value     */
	  bitTest ^= PRI_REQB;			/* and pre-toggle signal bit */

          count = (int) (short) *pri_buf;

          if (count < 0)
           { int i;
             *(fc->terminus) = FIFO_BAD_RCOUNT;
             goto interrupt_end;                /* Hangup the process   */
           }
          *data_ptr++ = count;

          if (count)
           { checksum = 0;
             while (count--)
              { 
		*pri_csr ^= PRI_CSR1;		/* signal controller	     */
		bitTest ^= (PRI_CSR1 | PRI_REQB);

		WAIT_FOR_SIGNAL();
	  	
                data = *pri_buf;                /* Get data and compute the  */
                checksum += data;		/* checksum		     */
                *data_ptr++ = data;       
              }

	     *pri_csr ^= PRI_CSR1;		/* get checksum from the     */
	     bitTest ^= (PRI_CSR1 | PRI_REQB);  /* controller and compare it */
	     					/* with computed checksum    */
	     WAIT_FOR_SIGNAL();

             if (checksum != *pri_buf)
              { *(fc->terminus) = FIFO_CHECKSUM;
                goto interrupt_end;
              }
           }

          if (fc->function1)                    /* Execute function1 if there is one */
           { (*fc->function1)();
           }



          pri_buf = (short *)&priaddr->obuf;
          data_ptr = (short *) fc->write_buffer;
          count = *data_ptr++;
          if (count < 0)
           { *(fc->terminus) = FIFO_BAD_WCOUNT;
             goto interrupt_end;                /* Hangup the process   */
           }

          *pri_buf = count;
          *pri_csr ^= PRI_CSR1;			/* signal controller	     */
	  bitTest ^= (PRI_CSR1 | PRI_REQB);

          if (count)
           { checksum = 0;
             while (count--)
              { WAIT_FOR_SIGNAL();

                data = *data_ptr++;		/* Load the pri */
                checksum += data;
                *pri_buf = data;

		*pri_csr ^= PRI_CSR1;		/* signal controller	     */
		bitTest ^= (PRI_CSR1 | PRI_REQB);
              }

	     WAIT_FOR_SIGNAL();
	     					/* send checksum to the      */
	 					/* controller		     */
             *pri_buf = checksum;
	     *pri_csr ^= PRI_CSR1;

 	     /* hack for oscilliscope: wait for the robot controller to clear*/
 	     /* the VAX interrupt bit, and then set the signal bit high for  */
 	     /* the rest of the interrupt so we can time function 2.         */
 
	     spinout = SPINOUT;
 	     while ((*pri_csr) & PRI_REQA && --spinout)
 	        ;
	     if (!spinout)
	      { *(fc->terminus) = FIFO_TIMEOUT;
		goto interrupt_end;
	      }
 	     *pri_csr |= PRI_CSR1;		/* set signal bit */

           }
        }

       if (*(fc->terminus))
        { priaddr->csr = 0;     /* Disable interrupts   */
          if (!(*(fc->terminus) & E_USER))
           { wantsig = 1;
           }
          goto interrupt_end;
        }

       if (fc->function2)       /* Execute function2 if there is one */
        { (*fc->function2)();
        }

interrupt_end:

       if (*(fc->terminus) & FIFO_ERROR)
        { priaddr->csr = 0;     /* Disable interrupts */
          wantsig = 1;
        }
       sigpid = fc->sigpid;
       sigsig = fc->signal;
       kernel_user_traps = 0;

       if ((fc->flags & TIMEOUT_REQ) && !*(fc->terminus))
        { 
          set_pri_timeout (fc->timeout);
          fc->flags |= TIMEOUT_SET;
        }

       restore_old_process();

       if (wantsig)
	{ struct proc *p;
	  p = pfind(sigpid);
	  if (p)
	   { psignal(p,sigsig);
	   }
	}
       break;
     }
    default:
     { 
       printf ("  PRI-INTR stray interrupt\n");
     }
  }
 priaddr->csr &= ~(PRI_CSR1 | PRI_CSR0);
}

/* PRIRESET: Not used */

prireset()
{
}

priioctl (dev, cmd, data, flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
 register struct _pri_softc *fc;
 register struct pridevice *priaddr;
 register int count = 0;
 int j;
 int runit, unit, s;
 int error = 0;
 struct fifoblock *fb = (struct fifoblock *)data;
 struct priblock *pb = (struct priblock *)data;

 unit = minor(dev);
 runit = RCI_REAL_UNIT(unit);


 priaddr = (struct pridevice *) pridinfo[runit]->ui_addr;
 fc = &pri_softc[runit];

 if (fc->state == FUNCTIONS)
  { switch (cmd)                        /* Certain ioctls are not allowed */
     {                                  /* while in FUNCTIONS mode        */
       case LIOCF_FLUSH:
       case LIOCF_SET_CTRL:
       case LIOCF_FUNC_ON:
       case LIOCF_WRITE:
       case LIOCF_READ:
       case PRIIOCF_READ:
       case PRIIOCF_WRITE:
        { return (ELOCK);
          break;
        }
     }
  }
       
 switch (cmd)
  { 
    default:
       error = ENOTTY;
       break;

    case LIOCF_FLUSH:
       break;
       
    case LIOCF_READ_CSR:
       fb->csr = priaddr->csr;
       break;
       
    case LIOCF_SET_CSR:
       priaddr->csr = fb->csr;
       fb->csr = priaddr->csr;
       break;

    case LIOCF_SET_CTRL:
       fc->flags &= ~0377;                /* Clear first eight flags */
       fc->flags |= (fb->flags & 0377); /* Set first eight flags   */
       
       if (fb->signal > NSIG || fb->signal < 1)
        { return (EINVAL);
        }
       
       fc->signal = fb->signal;
       break;

    case LIOCF_FUNC_ON:
     { 
       /* Verify the user's information as well as possible */
       
       struct proc *sigproc;
       int kill_status;

       if (!isatsv(u.u_procp, btop(fb->function1)) ||
           !isatsv(u.u_procp, btop(fb->function2)))
        { return (EFAULT);
        }
    
       if (!useracc(fb->read_buffer, FIFOBUF_SIZE, B_WRITE) ||
           !useracc(fb->write_buffer, FIFOBUF_SIZE, B_WRITE) ||
           !useracc(fb->terminus, sizeof(int), B_WRITE))
        { return (EFAULT);
        }

       if ((fb->signal < 1 || fb->signal > NSIG) ||
           (fb->timeout < 0))
        { return (EINVAL);
        }

       sigproc = pfind(fb->sigpid);
       if (sigproc == 0)
	{ return (ESRCH);
	}
       if (u.u_uid && u.u_uid != sigproc->p_uid)
	{ return (EPERM);
	}

       fc->function1 = fb->function1;
       fc->function2 = fb->function2;
       fc->read_buffer = fb->read_buffer;
       fc->write_buffer = fb->write_buffer;
       fc->terminus = fb->terminus;
       fc->signal = fb->signal;
       fc->sigpid = fb->sigpid;
       fc->timeout = fb->timeout;

       if (fc->timeout > 0)
        { fc->flags |= TIMEOUT_REQ;
        }
       fc->state = FUNCTIONS;

       fc->nice = u.u_procp->p_nice;            /* Fix CPU usage */
       u.u_procp->p_nice = 1;

       fc->RCIp = u.u_procp;
       init_RCI_process();
       priaddr->csr |= PRI_IEA;         /* Enable interrupts        */
       break;
     }
    case LIOCF_FUNC_OFF:       
       if (fc->state == FUNCTIONS)
        {
          priaddr->csr = 0;     /* Disable interrupts       */
          fc->state = OPENS;       

          if (fc->flags & TIMEOUT_SET)
           { clear_pri_timeout();
             fc->flags &= ~(TIMEOUT_SET | TIMEOUT_REQ);
           }
          u.u_procp->p_nice = fc->nice;
        }
       else
        { return (EBADMODE);
        }
       break;

    case LIOCF_FUNC_START:
       if (fc->state == FUNCTIONS)
        {
          if (fc->flags & TIMEOUT_REQ)
           { set_pri_timeout ( fc->timeout);
             fc->flags |= TIMEOUT_SET;
           }
          priaddr->csr = (PRI_IEA | PRI_CSR0);
        }
       else
        { return (EBADMODE);
        }
       break;

    case LIOCF_WRITE:
       priaddr->obuf = fb->data;
       break;

    case LIOCF_READ:
       fb->data = priaddr->ibuf;
       break;

    case PRIIOCF_READ:
       pb->csr =  priget (runit, 0);
       pb->obuf = priget (runit, 1);
       pb->ibuf = priget (runit, 2);
       break;

    case PRIIOCF_WRITE:
       priput (runit, pb->writeWord, pb->writeValue);
       pb->csr =  priget (runit, 0);
       pb->obuf = priget (runit, 1);
       pb->ibuf = priget (runit, 2);
       break;

#ifdef RCI
    /* 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 = &prirciinfo[RCI_REAL_UNIT(unit)];
	  ap = (struct RCI_fxn_arg *) data;
	  s = splhigh();
	  RCI_setup_switch(rcii);
	  rcii->ifxn = ap->function;
	  rcii->iarg = ap->argument;
	  splx(s);
	  
	  priaddr->csr |= PRI_IEA;	/* enable interrupts */
	}
       break;
    /* Execute a routine */
    case RCIIOC_EXECUTE:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ RCIINFO *rcii;
	  struct RCI_fxn_arg *ap;
	  rcii = &prirciinfo[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:
     { struct RCI_fxn_arg4 *ap;
       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();
	  prirciinfo[RCI_REAL_UNIT(unit)].ifxn = 0;
	  splx(s);
	}
       priaddr->csr &= ~PRI_IEA;	/* disable interrupts */

       break;
    /* Request signals from the slave task */
    case RCIIOC_REQSIGS:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ s = splhigh();
	  prirciinfo[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 = &prirciinfo[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();
	  prirciinfo[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 = &prirciinfo[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;
    /* Set up a timeout */
    case RCIIOC_TIMEOUT:
       if (! RCI_UNIT_P(unit))
	{ error = ENOTTY;
	}
       else
	{ RCI_setup_timeout( &prirciinfo[RCI_REAL_UNIT(unit)],
			     (struct RCI_timeout *) data );
	}
       break;
#endif
  }
 return (error);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

	Routines to save and restore RCI process context. These do
	a few mean and nasty things with the virtual memory mapping
	mechanism.

 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static char kernel_pgbuf[2048];
static struct pte *kernel_pte;
static struct pcb *RCI_pcb = 0;
static int old_pfn;
static int pcbpage;
static int remap;

static int oldP0BR;
static int oldP0LR;
static int oldP1BR;
static int oldP1LR;
static int rciP0BR;
static int rciP0LR;
static int rciP1BR;
static int rciP1LR;

/* This code assumes that each process' struct pcb
   is page-aligned in physical memory! */

extern struct pte Sysmap[];

static init_RCI_process()
{
	/* Remember memory context associated with RCI process */

	rciP0BR = mfpr(P0BR);
	rciP0LR = mfpr(P0LR);
	rciP1BR = mfpr(P1BR);
	rciP1LR = mfpr(P1LR);

	/* Initialize page to be used for locating memory context 
	   during run time */

#define SYSPFN(v)	((((int)(v))>>9) & ~0xffe00000)

        if (RCI_pcb == 0)
 	 { RCI_pcb = (struct pcb *) ((511+(int)&kernel_pgbuf[0]) & ~511);
	   kernel_pte = &Sysmap[ SYSPFN (RCI_pcb)];
	   old_pfn = kernel_pte->pg_pfnum;
	   pcbpage = old_pfn;
	 }
}

static switch_to_RCI_process(p)
struct proc *p;
{
	struct proc *mp;

	/* The idea here is that we have the RCI process memory context,
	   so we switch to it. The problem is, the memory context might
	   change dynamically. So we first get the most recent memory
	   context associated with p. Unfortunately, p does not necessarily 
	   contain the memory we want:

	   If p is in the middle of a vfork(), then the child may in fact have
	   the memory (temporarily) and we have to look for the memory
	   there. If the child has vforked as well, then IT'S child may have
	   the memory, ... etc. If a process does not have SNOVM set in its
	   flags field, then everything is cool. We follow down the xlink
	   list until we find a process that has memory. One final problem:
	   If we are in the middle of vpassvm(), which returns the memory to
	   the parent, then the parent will not yet have the memory but the
	   child will have been taken off the xlink list. So we lose. We
	   must then fall back on the memory context we already have. We can

	   we also don't refresh the memory context if the current process
	   is the one that has the memory. This is done (1) for efficiency,
	   since remapping won't be necessary, and (2) as a safegaurd in
	   case the process is in the middle of CHANGING it's memory.
	*/

	for (mp = p; mp != NULL; mp = mp->p_xlink )
	 { if (mp->p_flag & SLOSTVM)
	    { mp = 0;
	      break;
	    }
	   if (!(mp->p_flag & SNOVM))
	    { break;
	    }
	 }
	if (mp != NULL && mp != u.u_procp)	/* refresh memory context */
	 { 
	   kernel_pte->pg_pfnum = pcbb(mp);	/* map to mp's pcb */

	   asm ("mtpr $0, $0x39");		/* flush translation buffer */

	   asm ("movl _RCI_pcb, r0");
	   asm ("movl 80(r0), _rciP0BR");
	   asm ("movl 84(r0), _rciP0LR");
	   asm ("movl 88(r0), _rciP1BR");
	   asm ("movl 92(r0), _rciP1LR");

	   kernel_pte->pg_pfnum = old_pfn;	/* unmap from pcb */
	 }

	if (mp != u.u_procp)
	 { 
	   asm ("mfpr $0x8, _oldP0BR");
	   asm ("mfpr $0x9, _oldP0LR");
	   asm ("mfpr $0xa, _oldP1BR");
	   asm ("mfpr $0xb, _oldP1LR");
   
	   asm ("mtpr _rciP0BR, $0x8");
	   asm ("mtpr _rciP0LR, $0x9");
	   asm ("mtpr _rciP1BR, $0xa");
	   asm ("mtpr _rciP1LR, $0xb");
   
	   asm ("mtpr $0, $0x39");	/* flush cache */

	   remap = 1;
	 }
	else				/* No need to switch memory context */
	 { remap = 0;
	 }
}

static restore_old_process()
{
	if (remap)
	 { 
	   asm ("mtpr _oldP0BR, $0x8");
	   asm ("mtpr _oldP0LR, $0x9");
	   asm ("mtpr _oldP1BR, $0xa");
	   asm ("mtpr _oldP1LR, $0xb");
	   asm ("mtpr $0, $0x39");
	 }
}

#endif

/* ----- secret access to parallel port ----- */

priget (u, n)
int u, n;
{
	register struct pridevice *priaddr =
	 (struct pridevice *) pridinfo[u]->ui_addr;
	switch (n)
	 { case 0:
	        return (priaddr->csr);
	        break;
	   case 1:
 		return (priaddr->obuf);
		break;
	   case 2:
		return (priaddr->ibuf);
		break;
	   default:
		return (priaddr->csr);
	        break;
	 }
}

priput (u, n, v)
int u, n, v;
{
	register struct pridevice *priaddr =
	 (struct pridevice *) pridinfo[u]->ui_addr;
	switch (n)
	 { case 0:
		priaddr->csr = v & 0xffff;
	        return (priaddr->csr);
	        break;
	   case 1:
		priaddr->obuf = v & 0xffff;
 		return (priaddr->obuf);
		break;
	   case 2:
		priaddr->ibuf = v & 0xffff;
		return (priaddr->ibuf);
		break;
	   default:
		priaddr->csr = v & 0xffff;
		return (priaddr->csr);
	        break;
	 }
}
