#include "ka.h"

#if NKA > 0

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/ioctl.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/buf.h"
#include "../h/map.h"
#include "../h/uio.h"
#include "../h/tty.h"
#include "../h/kernel.h"
#include "../h/proc.h"
#include "../h/vmmac.h"

#include "../vax/vmparam.h"
#include "../vax/mtpr.h"
#include "../vax/pte.h"
#include "../vax/cpu.h"

#include "../vaxuba/ubavar.h"

#include "../rciext/kareg.h"
#include "../rciext/kaioctl.h"
#include "../rciext/kamsgs.h"
#include "../rciext/crashinfo.h"

#include "../rciext/mchk_frame.h"

#ifdef RCI
#include "../rciext/rci.h"
#endif

#define KA_UFLAG_MULTOPEN 0x80

#define KA_ALLUFLAGS (KA_UFLAG_MULTOPEN)
#define KAFLAGS(dev) (minor(dev)&KA_ALLUFLAGS)
#define KAUNIT(dev) (minor(dev)&~KA_ALLUFLAGS)

struct uba_device *kainfo[NKA];
int kaprobe();
int kaattach();
u_short kastd[] = { 0 };
struct uba_driver kadriver = { kaprobe, 0, kaattach, 0, kastd, "ka", kainfo };

/*
 * Everything indexed [4] is per-processor; 0 is arbiter, 1-3 are slaves.
 *
 * kapresent is true/false saying whether a processor is present.
 * kaexpalloc is allocated watermark of export area
 * kawhoowns indicates who owns memory, per page
 * kafrom is virtual address of message area, it to us
 * kato is virtual address of message area, us to it
 * kamapregs is virtual address of Q-bus mapping registers
 * kasigprocs is processes to receive MSG_SIGNAL signals
 * kaflags is assorted flag bits
 * kacrashprocs is processes to receive MSG_CRASH signals
 * kacrashsig is signal to deliver on MSG_CRASH
 * kacrash is the last crash for each cpu
 * kasleepers is just a cell to sleep on
 * ka_rci_timeouts is the timeout chains for RCI
 * ka_rci_clocks is the latest recognized clock values for RCI
 * ka_wanttick is true if ka_rttick needs to examine that clock's timer queue
 * RCIclockDevice is true if there is a separate device available for
      providing the RCI clock.
 */
int kapresent[4];
int kaexpalloc[4];
char kawhoowns[KA_EXPORTSIZE>>9];
struct kamsgarea *kafrom[4];
struct kamsgarea *kato[4];
struct kabbblock *kabboards[4];
int *kamapregs;
int kasigprocs[4];
int kaflags[4];
#define KA_BUSY   0x00000001 /* sb has it open */
#define KA_CLEAR  0x00000002 /* proc has requested clear-on-close */
#define KA_STOP   0x00000004 /* proc has requested stop-on-close */
#define KA_MBIOTO 0x00000008 /* ka_msgbufio timed out */
int kacrashprocs[4];
int kacrashsig[4];
CRASHINFO kacrash[4];
int kasleepers;
#ifdef RCI
struct rcitimeout *ka_rci_timeouts[4] = { 0, 0, 0, 0 };
int ka_rci_clocks[4] = { -1, -1, -1, -1 };
int ka_wanttick[4] = { 0, 0, 0, 0 };
extern int RCIclockDevice;
#endif

/*
 * Allocate kernel virtual space with no physical space, ie, just get
 *  the page tables.  Physical space for it is assumed to be gotten
 *  elsewhere.  Argument is number of clicks (512 byte pages) to get.
 */
caddr_t ka_kvall(npg)
int npg;
{
 int a;

 a = rmalloc(kernelmap,(long int)clrnd(npg));
 if (a == 0)
  { return(0);
  }
 return((caddr_t)kmxtob(a));
}

/*
 * Free virtual space allocated with ka_kvall.  It is assumed that the
 *  physical space, if any, does not need freeing.  Ka is the kernel
 *  virtual address of the space, npg is the number of clicks to free.
 */
ka_kvfree(ka,npg)
caddr_t ka;
int npg;
{
 rmfree(kernelmap,(long int)clrnd(npg),(long int)btokmx((struct pte *)ka));
}

/*
 * Take kernel virtual address space and map it to the indicated
 *  physical addresses.  Ka is the kernel virtual address, npg is the
 *  number of clicks, and phys is the starting physical address (only
 *  the page number is used).
 */
ka_kmapphys(ka,npg,phys)
caddr_t ka;
int npg;
int phys;
{
 struct pte *ptep;
 int pf;

 ptep = (struct pte *) &Usrptmap[btokmx((struct pte *)ka)];
 npg = clrnd(npg);
 pf = btop(phys);
 for (;npg>0;npg--)
  { mapin(ptep,btop(ka),(unsigned int)pf,1,(int)(PG_V|PG_KW));
    ptep ++;
    ka += NBPG;
    pf ++;
  }
}

/*
 * Make memory accessible via the Q-bus mapping mechanism.  Memory is
 *  specified by a kernel virtual address; the page frames are made
 *  accessible to Q-bus addresses starting with phys.  Npg is the
 *  number of clicks to map.
 */
ka_qbaccess(ka,npg,phys)
caddr_t ka;
int npg;
int phys;
{
 int *maprptr;
 int *ptep;

 maprptr = &kamapregs[(phys-KA_QBUSBASE)>>9];
 ptep = (int *) &Usrptmap[btokmx((struct pte *)ka)];
 for (;npg>0;npg--)
  { *maprptr++ = KA_QMAP_V | (PG_PFNUM & *ptep++);
  }
}

/*
 * Just like ka_qbaccess except the memory is specified by a *user*
 *  virtual address instead of a *kernel* virtual address.  It is
 *  assumed that u.u_procp owns the memory.
 */
ka_uqbaccess(ua,npg,phys)
caddr_t ua;
int npg;
int phys;
{
 int *maprptr;
 int *ptep;

 maprptr = &kamapregs[(phys-KA_QBUSBASE)>>9];
 ptep = (int *) vtopte(u.u_procp,btop(ua));
 for (;npg>0;npg--)
  { *maprptr++ = KA_QMAP_V | (PG_PFNUM & *ptep++);
  }
}

/*
 * Revoke Q-bus access, starting at Q-bus address phys, for npg clicks.
 */
ka_qbnoaccess(npg,phys)
int npg;
int phys;
{
 int *maprptr;

 maprptr = &kamapregs[(phys-KA_QBUSBASE)>>9];
 for (;npg>0;npg--)
  { *maprptr++ = 0; /* in particular, ~KA_QMAP_V */
  }
}

/*
 * Allocate size bytes of Q-bus space.  Caller must check that size is
 *  a multiple of the pagesize.  Return value is physical address of
 *  allocated space.  Cpu is the cpu number in whose space the
 *  allocation should be made.  Unit is the unit number if cpu is 0; if
 *  cpu is not 0, unit is ignored.
 */
int ka_qballoc(size,cpu,unit)
int size;
int cpu;
int unit;
{
 int pgs;
 int found;
 int i;

 pgs = size >> 9;
 if (cpu != KA_ARBITER)
  { kaexpalloc[cpu] += pgs;
    if (kaexpalloc[cpu] > KA_EXPORTSIZE>>9)
     { return(0);
     }
    return(((kaexpalloc[cpu]-pgs)<<9)+KA_EXPORT(cpu));
  }
 found = 0;
 for (i=0;i<KA_EXPORTSIZE>>9;i++)
  { if (kawhoowns[i] != 0)
     { found = 0;
     }
    else
     { found ++;
       if (found >= pgs)
	{ for (;pgs>0;pgs--)
	   { kawhoowns[i--] = unit;
	   }
	  i ++;
	  return((i<<9)+KA_EXPORT(KA_ARBITER));
	}
     }
  }
 return(0);
}

/*
 * Returns number of free bytes in message area.
 */
int ka_msgfree(msgarea)
register struct kamsgarea *msgarea;
{
 register int n;

 n = msgarea->tail - msgarea->head;
 if (n <= 0)
  { n += KA_MSGDATASIZE;
  }
 return(n);
}

/*
 * Returns number of bytes available in message area.  Intlvl=1 asks
 *  about interrupt-level queue, intlvl=0 asks about non-interrupt
 *  queue.  Must be called from mutex ipl.
 */
int ka_msgavail(msgarea,intlvl)
register struct kamsgarea *msgarea;
int intlvl;
{
 register int n;

 n = intlvl ? (msgarea->head - msgarea->seen)
	    : (msgarea->seen - msgarea->tail) ;
 if (n < 0)
  { n += KA_MSGDATASIZE;
  }
 return(n);
}

/*
 * Read from or write to a message area.  This is the lowest level
 *  routine for doing things to the message areas.  At this level, the
 *  message area is just a circular buffer of bytes.  Imposition of
 *  message structure, with types and all that, is done at a higher
 *  level.  Doorbell ringing is also done at a higher level.
 *
 * ka_msgbufio(msgarea,size,addr,seg,cantwait,code)
 * msgarea  is a pointer to the struct kamsgarea to be acted upon
 * size     is the number of bytes to be read or written
 * addr     is a pointer to the buffer for the I/O
 * seg      is UIO_USERSPACE or UIO_SYSSPACE for user or kernel I/O
 * cantwait is KA_CANWAIT or KA_CANTWAIT, as appropriate
 * code     is a code describing the operation to be performed
 *
 * A code of KA_IREAD or KA_ISKIP implies a cantwait of KA_CANTWAIT.
 *
 * If code is KA_SKIP or KA_ISKIP then addr and seg need not be valid.
 *
 * Return value is an errno:
 *
 *	0 - success
 *	EMSGSIZE - request is bigger than the message area can hold
 *	EWOULDBLOCK - KA_CANTWAIT and would have to sleep() to do it
 *	ENXIO - the message area does not exist (only for KA_WRITE)
 *	EFAULT - uiomove() failed (only for UIO_USERSPACE)
 *
 * When this routine sleep()s, it does so interruptably.
 */
int ka_msgbufio(msgarea,size,addr,seg,cantwait,code)
struct kamsgarea *msgarea;
int size;
caddr_t addr;
int seg;
int cantwait;
int code;
{
 struct iovec iov;
 struct uio uio;
 int error;
 int n;
 int space;
 int s;

 if (size >= KA_MSGDATASIZE)
  { return(EMSGSIZE);
  }
 if (((code&KA_OP) == KA_WRITE) && badaddr((caddr_t)msgarea,4))
  { return(ENXIO);
  }
 uio.uio_iov = &iov;
 uio.uio_iovcnt = 1;
 uio.uio_offset = 0;
 uio.uio_segflg = seg;
 uio.uio_resid = size;
 iov.iov_base = addr;
 iov.iov_len = size;
 error = 0;
 s = splhigh();
top:
 switch (code & KA_OP)
  { default:
       printf("bad code %x to ka_msgbufio\n",code);
       panic("ka_msgbufio");
       break;
    case KA_WRITE:
       space = ka_msgfree(msgarea);
       if (size >= space)
	{ if (cantwait == KA_CANTWAIT)
	   { splx(s);
	     return(EWOULDBLOCK);
	   }
	  sleep((caddr_t)&kasleepers,PZERO+1);
	  goto top;
	}
       n = KA_MSGDATASIZE - msgarea->head;
       if (size >= n)
	{ error = uiomove(&msgarea->data[msgarea->head],n,UIO_WRITE,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  n = size - n;
	  if (n > 0)
	   { error = uiomove(&msgarea->data[0],n,UIO_WRITE,&uio);
	     if (error)
	      { splx(s);
		return(error);
	      }
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->head = n;
	   }
	}
       else
	{ error = uiomove(&msgarea->data[msgarea->head],size,UIO_WRITE,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->head += size;
	   }
	}
       break;
    case KA_IREAD:
       space = ka_msgavail(msgarea,1);
       if (size > space)
	{ splx(s);
	  return(EWOULDBLOCK);
	}
       n = KA_MSGDATASIZE - msgarea->seen;
       if (size >= n)
	{ error = uiomove(&msgarea->data[msgarea->seen],n,UIO_READ,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  n = size - n;
	  if (n > 0)
	   { error = uiomove(&msgarea->data[0],n,UIO_READ,&uio);
	     if (error)
	      { splx(s);
		return(error);
	      }
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->seen = n;
	   }
	}
       else
	{ error = uiomove(&msgarea->data[msgarea->seen],size,UIO_READ,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->seen += size;
	   }
	}
       break;
    case KA_READ:
       space = ka_msgavail(msgarea,0);
       if (size > space)
	{ if (cantwait == KA_CANTWAIT)
	   { splx(s);
	     return(EWOULDBLOCK);
	   }
	  sleep((caddr_t)&kasleepers,PZERO+1);
	  goto top;
	}
       n = KA_MSGDATASIZE - msgarea->tail;
       if (size >= n)
	{ error = uiomove(&msgarea->data[msgarea->tail],n,UIO_READ,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  n = size - n;
	  if (n > 0)
	   { error = uiomove(&msgarea->data[0],n,UIO_READ,&uio);
	     if (error)
	      { splx(s);
		return(error);
	      }
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->tail = n;
	   }
	}
       else
	{ error = uiomove(&msgarea->data[msgarea->tail],size,UIO_READ,&uio);
	  if (error)
	   { splx(s);
	     return(error);
	   }
	  if (! (code & KA_PEEK))
	   { msgarea->tail += size;
	   }
	}
       break;
    case KA_ISKIP:
       space = ka_msgavail(msgarea,1);
       if (size > space)
	{ splx(s);
	  return(EWOULDBLOCK);
	}
       n = KA_MSGDATASIZE - msgarea->seen;
       if (size >= n)
	{ if (! (code & KA_PEEK))
	   { msgarea->seen = size - n;
	   }
	}
       else
	{ if (! (code & KA_PEEK))
	   { msgarea->seen += size;
	   }
	}
       break;
    case KA_SKIP:
       space = ka_msgavail(msgarea,0);
       if (size > space)
	{ if (cantwait == KA_CANTWAIT)
	   { splx(s);
	     return(EWOULDBLOCK);
	   }
	  sleep((caddr_t)&kasleepers,PZERO+1);
	  goto top;
	}
       n = KA_MSGDATASIZE - msgarea->tail;
       if (size >= n)
	{ if (! (code & KA_PEEK))
	   { msgarea->tail = size - n;
	   }
	}
       else
	{ if (! (code & KA_PEEK))
	   { msgarea->tail += size;
	   }
	}
       break;
  }
 splx(s);
 return(0);
}

/* Helper function for ka_waitformsg, just below */
kambiowakeup(unitca)
caddr_t unitca;
{
 int unit;

 unit = (int) unitca;
 if ((unit < 0) || (unit > 3))
  { return;
  }
 kaflags[unit] |= KA_MBIOTO;
 wakeup((caddr_t)&kasleepers);
}

/*
 * Wait for a message to appear in the message area.  Must be called at
 *  mutex ipl to be useful.  Skips MSG_CONSUMED messages.  Timeout is
 *  in clock ticks, 0 -> forever.  Return value is 0 on success,
 *  ETIMEDOUT on timeout.
 */
int ka_waitformsg(area,unit,tmo)
struct kamsgarea *area;
int unit;
int tmo;
{
 char buf[5];
 register int i;

 while (1)
  {
#if 1
    if (tmo == 0)
     {
#endif
       ka_msgbufio(area,5,buf,UIO_SYSSPACE,KA_CANWAIT,KA_READ|KA_PEEK);
#if 1
     }
    else
     { timeout(kambiowakeup,(caddr_t)unit,tmo);
       while (1)
	{ i = ka_msgbufio(area,5,buf,UIO_SYSSPACE,KA_CANTWAIT,KA_READ|KA_PEEK);
	  if (i == 0)
	   { break;
	   }
	  if (i == EWOULDBLOCK)
	   { kaflags[unit] &= ~KA_MBIOTO;
	     sleep((caddr_t)&kasleepers,PZERO+1);
	     if (kaflags[unit] & KA_MBIOTO)
	      { return(ETIMEDOUT);
	      }
	   }
	}
       untimeout(kambiowakeup,(caddr_t)unit);
     }
#endif
    if (buf[0] == MSG_CONSUMED)
     { i = ka_msgbufio(area,*(int *)&buf[1],(char *)0,0,KA_CANWAIT,KA_SKIP);
       ka_ring_doorbell(unit);
       if (i)
	{ printf("ka: can't skip %d-byte consumed msg in waitformsg: error %d\n",*(int *)&buf[1],i);
	  printf("    head %d seen %d tail %d\n",area->head,area->seen,area->tail);
	  area->seen = area->head; /* summarily discard all pending messages */
	  area->tail = area->head;
	}
       continue;
     }
    break;
  }
 return(0);
}

/*
 * Consume a message by stamping on the message type at the head of the
 *  indicated queue (intlvl=1: interrupt, intlvl=0: non-interrupt).
 */
ka_consume(area,intlvl)
struct kamsgarea *area;
int intlvl;
{
 area->data[intlvl?area->seen:area->tail] = MSG_CONSUMED;
}

/*
 * Wait for a MSG_ACK message for the specified type of message.  On
 *  silence for timeout ticks, return ETIMEDOUT, otherwise 0.
 */
ka_getack(unit,type,timeout)
int unit;
int type;
int timeout;
{
 struct kamsgarea *area;
 char buf[6];
 int s;
 int i;

 s = splhigh();
 area = kafrom[unit];
 while (1)
  { i = ka_waitformsg(area,unit,timeout);
    if (i == ETIMEDOUT)
     { return(ETIMEDOUT);
     }
    ka_msgbufio(area,5,buf,UIO_SYSSPACE,KA_CANWAIT,KA_READ|KA_PEEK);
    if (buf[0] != MSG_ACK)
     { printf("non-ACK (type %d) from ka %d\n",0xff&(int)buf[0],unit);
       ka_consume(area,0);
       continue;
     }
    else if (*(int *)&buf[1] != 6)
     { printf("wrong-length ACK (size %d) from ka %d\n",*(int *)&buf[1],unit);
       ka_consume(area,0);
       continue;
     }
    ka_msgbufio(area,6,buf,UIO_SYSSPACE,KA_CANWAIT,KA_READ);
    ka_ring_doorbell(unit);
    if (buf[5] != type)
     { printf("wrong-type ACK (type %d, want %d) from ka %d\n",
					0xff&(int)buf[5],type,unit);
       continue;
     }
    break;
  }
 return(0);
}

/*
 * Ring the doorbell of another processor.
 */
ka_ring_doorbell(unit)
int unit;
{
 if (((unsigned int)unit > 3) || !kapresent[unit])
  { panic("ka_ring_doorbell");
  }
 ((struct kadevice *)kainfo[0]->ui_addr)->ipcr[unit] |= KA_DBI;
}

/*
 * Clear the processor.  Send it a clear message and reinitialize our
 *  idea of its resources.  Returns an error number.
 */
int kaclear(unit,timeout)
int unit;
int timeout;
{
 char msg[5];
 int error;

 msg[0] = MSG_CLEAR;
 * (int *) &msg[1] = 5;
 error = ka_msgbufio(kato[unit],5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
 ka_ring_doorbell(unit);
 if (error) return(error);
 error = ka_getack(unit,MSG_CLEAR,timeout);
 kaexpalloc[unit] = 0;
 return(error);
}

/*
 * Stop the processor.  Send it a stop message.  Returns an error number.
 */
int kastop(unit,timeout)
int unit;
int timeout;
{
 char msg[5];
 int error;

 msg[0] = MSG_STOP;
 * (int *) &msg[1] = 5;
 error = ka_msgbufio(kato[unit],5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
 ka_ring_doorbell(unit);
 if (error) return(error);
 error = ka_getack(unit,MSG_STOP,timeout);
 return(error);
}

/*
 * Probe routine.  Not only probe the device, but also initialize
 *  things like the message areas and the virtual space to access the
 *  map registers.
 */
kaprobe(reg)
caddr_t reg;
{
 register int br, cvec;
 struct kadevice *addr;
 int trash;
 int i;
 static char *kanames[4] = { "arbiter",
			     "slave 1",
			     "slave 2",
			     "slave 3" };
 caddr_t ca;

 addr = (struct kadevice *) reg;
#if !(VAX630||VAX620)
 return(0);
#endif
 if ((cpu != VAX_630) && (cpu != VAX_620))
  { printf("kaprobe: cpu not KA\n");
    return(0);
  }
#ifdef lint
 br = 0;
 cvec = br;
 br = cvec;
 kaintr(0);
#endif
 if (KA_MSGBUFSIZE != sizeof(struct kamsgarea))
  { printf("ka: KA_MSGBUFSIZE (%d) != sizeof(struct kamsgarea) (%d), fix\n",
	KA_MSGBUFSIZE, sizeof(struct kamsgarea) );
    return(0);
  }
 /* Check to see whether our IPCR is there - if not, must not be on a	 */
 /*  KA630/620, so this device is utter nonsense anyway.		 */
 if (badaddr((caddr_t)&addr->ipcr[0],2))
  { printf("kaprobe: our ipcr isn't there\n");
    return(0);
  }
 /* Ring our own doorbell, for UNIX to think the device is up */
 addr->ipcr[0] = KA_LMEAE|KA_DBIE;
 addr->ipcr[0] = KA_LMEAE|KA_DBIE|KA_DBI;
 DELAY(10);
 /* Determine which processors are present: just badaddr their ipcrs */
 kapresent[0] = 1; /* always: 0 is the arbiter */
 for (i=1;i<4;i++)
  { kapresent[i] = !badaddr(&addr->ipcr[i],2);
  }
 /* Map the Q-bus mapping registers into our virtual space */
 kamapregs = (int *) ka_kvall(btoc((KA_QBUSSIZE>>9)<<2));
 if (kamapregs == 0)
  { printf("ka: can't get Q-bus map area, disabling all slaves\n");
    for (i=1;i<4;i++)
     { kapresent[i] = 0;
     }
  }
 else
  { ka_kmapphys((caddr_t)kamapregs,btoc((KA_QBUSSIZE>>9)<<2),KA_QMAPBASE);
  }
 /* Announce who's present */
 for (i=0;i<4;i++)
  { if (kapresent[i])
     { printf("%s%s",i?", ":"ka processors present: ",kanames[i]);
     }
  }
 printf("\n");
 /* Set up message areas for each slave. */
 for (i=1;i<4;i++)
  { if (kapresent[i])
     { caddr_t kaddr;
       kaddr = wmemall(memall,KA_MSGBUFSIZE);
       if (kaddr == 0)
	{ printf("ka %s: can't get message buffer, disabling\n",kanames[i]);
	  kapresent[i] = 0;
	}
       kafrom[i] = (struct kamsgarea *) kaddr;
       kafrom[i]->head = 0;
       kafrom[i]->seen = 0;
       kafrom[i]->tail = 0;
       ka_qbaccess(kaddr,btoc(KA_MSGBUFSIZE),KA_X_TO_Y(KA_SLAVE(i),KA_ARBITER));
     }
    if (kapresent[i])
     { caddr_t kaddr;
       kaddr = ka_kvall(btoc(KA_MSGBUFSIZE));
       if (kaddr == 0)
	{ printf("ka %s: can't get message buffer, disabling\n",kanames[i]);
	  ka_qbnoaccess(btoc(KA_MSGBUFSIZE),KA_X_TO_Y(KA_SLAVE(i),KA_ARBITER));
	  wmemfree((caddr_t)kafrom[i]);
	  kapresent[i] = 0;
	}
       ka_kmapphys(kaddr,btoc(KA_MSGBUFSIZE),KA_X_TO_Y(KA_ARBITER,KA_SLAVE(i)));
       kato[i] = (struct kamsgarea *) kaddr;
     }
  }
 /* Set up the blackboards */
 ca = wmemall(memall,KA_BBOARDSIZE);
 if (ca == 0)
  { printf("ka %s: can't get blackboard, disabling entire driver\n",kanames[0]);
    kapresent[0] = 0;
    kapresent[1] = 0;
    kapresent[2] = 0;
    kapresent[3] = 0;
    return(0);
  }
 kabboards[0] = (struct kabbblock *) ca;
 ka_qbaccess(ca,btoc(KA_BBOARDSIZE),KA_BBOARD(KA_ARBITER));
 for (i=1;i<4;i++)
  { ca = ka_kvall(btoc(KA_BBOARDSIZE));
    if (ca == 0)
     { printf("ka %s: can't get blackboard, disabling\n",kanames[i]);
       kapresent[i] = 0;
     }
    else
     { kabboards[i] = (struct kabbblock *) ca;
       ka_kmapphys(ca,btoc(KA_MSGBUFSIZE),KA_BBOARDBASE+(KA_BBOARDSIZE*i));
     }
  }
 /* Make the blackboards URKW */
 for (i=0;i<4;i++)
  { int *ptep;
    int j;
    if (kapresent[i])
     { ptep = (int *) &Usrptmap[btokmx((struct pte *)kabboards[i])];
       for (j=btoc(KA_BBOARDSIZE)-1;j>=0;j--)
	{ ptep[j] &=~ PG_PROT;
	  ptep[j] |= PG_URKW;
	  mtpr(TBIS,(int)&((char *)kabboards[i])[ctob(j)]);
	}
     }
  }
 /* Initialize the real-time clocks */
 for (i=0;i<4;i++)
  { kabboards[0]->common.time_rt[i] = 0;
  }
 /* Initialize the Q-bus allocation data */
 for (i=0;i<4;i++)
  { kaexpalloc[i] = 0;
  }
 /* Clear the processes to receive signals */
 for (i=0;i<4;i++)
  { kasigprocs[i] = -1;
    kacrashprocs[i] = -1;
  }
 /* Clear the flags */
 for (i=0;i<4;i++)
  { kaflags[i] = 0;
  }
 bzero(kawhoowns,sizeof(kawhoowns));
 bzero(&kamapregs[(KA_EXPORT(KA_ARBITER)-KA_QBUSBASE)>>9],(KA_EXPORTSIZE>>9)<<2);
 return(sizeof(struct kadevice));
}

/*
 * Attach routine.  Just a stub at this point.
 */
kaattach(ui)
struct uba_device *ui;
{
}

/*ARGSUSED*/
kaopen(dev,flag)
dev_t dev;
int flag;
{
 int unit;
 int uflags;
 struct uba_device *ui;
 int exclusive;
 int s;

 unit = KAUNIT(dev);
 uflags = KAFLAGS(dev);
 exclusive = ! (uflags & KA_UFLAG_MULTOPEN);
 if ((unit < 0) || (unit >= 4))
  { return(ENXIO);
  }
 ui = kainfo[0];
 if (!ui || !ui->ui_alive)
  { return(ENXIO);
  }
 if (! kapresent[unit])
  { return(ENXIO);
  }
 if (exclusive)
  { s = splhigh();
    if (kaflags[unit] & KA_BUSY)
     { splx(s);
       return(EBUSY);
     }
    kaflags[unit] |= KA_BUSY;
  }
 splx(s);
 return(0);
}

/*ARGSUSED*/
kaclose(dev,flag)
dev_t dev;
int flag;
{
 int i;
 int unit;

 unit = KAUNIT(dev);
 i = splhigh();
 if (unit != 0)
  { for (i=0;i<KA_EXPORTSIZE>>9;i++)
     { if (kawhoowns[i] == unit)
	{ kawhoowns[i] = 0;
	  kamapregs[i+((KA_EXPORT(KA_ARBITER)-KA_QBUSBASE)>>9)] = 0;
	}
     }
  }
 kasigprocs[unit] = -1;
 kacrashprocs[unit] = -1;
 if (kaflags[unit] & KA_CLEAR)
  { kaclear(unit,30*hz);
  }
 else if (kaflags[unit] & KA_STOP)
  { kastop(unit,30*hz);
  }
 kaflags[unit] = 0;
 splx(i);
 return(0);
}

/*ARGSUSED*/
karead(dev,uio)
dev_t dev;
struct uio *uio;
{
 return(EIO);
}

/*ARGSUSED*/
kawrite(dev,uio)
dev_t dev;
struct uio *uio;
{
 return(EIO);
}

kaioctl(dev,cmd,data,flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
 struct kadevice *addr;
 int unit;
 int s;
 int error;

 unit = KAUNIT(dev);
 addr = (struct kadevice *) kainfo[0]->ui_addr;
 error = 0;
 switch (cmd)
  { default:
       error = ENOTTY;
       break;
    case KAIOC_GETIPCR:
       * (unsigned short int *) data = addr->ipcr[unit];
       break;
    case KAIOC_SETIPCR:
       addr->ipcr[unit] = * (unsigned short int *) data;
       break;
    case KAIOC_BISIPCR:
       addr->ipcr[unit] |= * (unsigned short int *) data;
       break;
    case KAIOC_BICIPCR:
       addr->ipcr[unit] &= ~ * (unsigned short int *) data;
       break;
    case KAIOC_BOOT:
	{ struct blkdesc *d;
	  char *kbuf;
	  struct kaboothdr *kbh;
	  int s;
	  static int kabooting;
	  int npgs;
	  int i;
	  /* Only one process in the boot code at a time, please.  This	 */
	  /*  is necessary 'cause there's only one boot area.		 */
	  s = splhigh();
	  while (kabooting)
	   { sleep((caddr_t)&kabooting,PZERO+1);
	   }
	  kabooting = 1;
	  splx(s);
	  /* Allocate a kernel buffer and move the boot program into it. */
	  d = (struct blkdesc *) data;
	  npgs = btoc(d->size+sizeof(struct kaboothdr));
	  if (npgs > btoc(KA_BOOTSIZE))
	   { error = EMSGSIZE;
	     goto kabootout;
	   }
	  kbuf = wmemall(memall,ctob(npgs));
	  if (kbuf == 0)
	   { error = ENOBUFS;
	     goto kabootout;
	   }
	  if (error=copyin(d->addr,kbuf+sizeof(struct kaboothdr),d->size))
	   { goto kabootfree;
	   }
	  /* Halt the slave in case it's running */
	  addr->ipcr[unit] |= KA_HLT;
	  /* Set up the memory boot signature block */
	  kbh = (struct kaboothdr *) &kbuf[0];
	  kbh->eighteen = 0x18;
	  kbh->zero1 = 0;
	  /* kbh->any1 = anything */
	  kbh->check1 = ~ (kbh->eighteen+kbh->zero1+kbh->any1);
	  kbh->zero2 = 0;
	  kbh->one = 1;
	  /* kbh->any2 = anything */
	  kbh->sizeinpgs = npgs;
	  kbh->zero3 = 0;
	  kbh->offset = sizeof(struct kaboothdr);
	  kbh->check2 = kbh->sizeinpgs + kbh->zero3 + kbh->offset;
	  /* Clear Q-bus allocation for this slave */
	  kaexpalloc[unit] = 0;
	  /* Make the boot area accessible on the Q-bus */
	  ka_qbaccess(kbuf,npgs,KA_BOOTBASE);
	   { static char bootwakeup;
	     int wakeup();
	     /* Wait 1/5 second for the slave to print its messages */
	     timeout(wakeup,(caddr_t)&bootwakeup,hz/5);
	     sleep((caddr_t)&bootwakeup,PZERO);
	     /* Unhalt it and let it boot */
	     addr->ipcr[unit] &= ~KA_HLT;
	     /* Wait 1/2 second for it to copy the boot */
	     timeout(wakeup,(caddr_t)&bootwakeup,hz/2);
	     sleep((caddr_t)&bootwakeup,PZERO);
	   }
kabootfree:
	  /* Revoke Q-bus access to the boot area */
	  ka_qbnoaccess(btoc(KA_BOOTSIZE),KA_BOOTBASE);
	  /* Free the kernel buffer */
	  wmemfree(kbuf,d->size+sizeof(struct kaboothdr));
kabootout:
	  /* Clear the boot-in-progress flag */
	  s = splhigh();
	  kabooting = 0;
	  wakeup((caddr_t)&kabooting);
	  splx(s);
	}
       break;
    case KAIOC_RMSGBUF:
	{ register struct blkdesc *d;
	  d = (struct blkdesc *) data;
	  error = ka_msgbufio( kafrom[unit],
			       d->size,
			       d->addr,
			       UIO_USERSPACE,
			       KA_CANWAIT,
			       KA_READ );
	  ka_ring_doorbell(unit);
	}
       break;
    case KAIOC_WMSGBUF:
	{ register struct blkdesc *d;
	  d = (struct blkdesc *) data;
	  error = ka_msgbufio( kato[unit],
			       d->size,
			       d->addr,
			       UIO_USERSPACE,
			       KA_CANWAIT,
			       KA_WRITE );
	  ka_ring_doorbell(unit);
	}
       break;
    case KAIOC_CLEAR:
       error = kaclear(unit,0);
       break;
    case KAIOC_DOWNLOAD:
	{ struct blkadesc *d;
	  static char msg[1024];
	  int s;
	  int n;
	  d = (struct blkadesc *) data;
	  if ( (0xc0000000 & (int)d->saddr) ||
	       (0xc0000000 & (d->size+(int)d->saddr)) )
	   { error = EINVAL;
	   }
	  while ((d->size > 0) && !error)
	   { n = (d->size < sizeof(msg)-13) ? d->size : sizeof(msg)-13;
	     do
	      { s = splhigh();
		msg[0] = MSG_DOWNLOAD;
		*(int *)&msg[1] = n + 13;
		*(int *)&msg[5] = (int) d->saddr;
		*(int *)&msg[9] = n;
		error = copyin(d->haddr,(caddr_t)&msg[13],n);
		if (error)
		 { splx(s);
		   break;
		 }
		error = ka_msgbufio(kato[unit],n+13,msg,UIO_SYSSPACE,KA_CANTWAIT,KA_WRITE);
		ka_ring_doorbell(unit);
		splx(s);
	      } while ((error == EWOULDBLOCK) && (sleep((caddr_t)&kasleepers,PZERO+1),1));
	     if (error)
	      { break;
	      }
	     ka_getack(unit,MSG_DOWNLOAD,0);
	     d->size -= n;
	     d->saddr += n;
	     d->haddr += n;
	   }
	}
       break;
    case KAIOC_START:
	{ char msg[13];
	  msg[0] = MSG_START;
	  *(int *)&msg[1] = 13;
	  *(int *)&msg[5] = ((int *)data)[0];
	  *(int *)&msg[9] = ((int *)data)[1];
	  error = ka_msgbufio(kato[unit],13,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_START,0);
	}
       kaexpalloc[unit] = 0;
       break;
    case KAIOC_STOP:
	{ char msg[5];
	  msg[0] = MSG_STOP;
	  *(int *)&msg[1] = 5;
	  error = ka_msgbufio(kato[unit],5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_STOP,0);
	}
       break;
    case KAIOC_ALLOC:
	{ register struct blkdesc *d;
	  char msg[13];
	  d = (struct blkdesc *) data;
	  msg[0] = MSG_ALLOC;
	  *(int *)&msg[1] = 13;
	  *(int *)&msg[5] = (int) d->addr;
	  *(int *)&msg[9] = d->size;
	  error = ka_msgbufio(kato[unit],13,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_ALLOC,0);
	}
       break;
    case KAIOC_READONLY:
	{ register struct blkdesc *d;
	  char msg[13];
	  d = (struct blkdesc *) data;
	  msg[0] = MSG_READONLY;
	  *(int *)&msg[1] = 13;
	  *(int *)&msg[5] = (int) d->addr;
	  *(int *)&msg[9] = d->size;
	  error = ka_msgbufio(kato[unit],13,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_READONLY,0);
	}
       break;
    case KAIOC_QBALLOC:
	{ struct qballocreq *req;
	  req = (struct qballocreq *) data;
	  req->qbhandle = ka_qballoc(req->size,req->cpu,unit);
	  if (req->qbhandle == 0)
	   { error = ENOMEM;
	   }
	}
       break;
    case KAIOC_IMPORT:
	{ register struct sharedesc *d;
	  int a;
	  int e;
	  d = (struct sharedesc *) data;
	  if ((d->size & CLOFSET) || !claligned(d->addr))
	   { error = EALIGN;
	     break;
	   }
	  if (!useracc(d->addr,d->size,B_WRITE))
	   { error = EFAULT;
	     break;
	   }
	  if ( !isadsv(u.u_procp,btop(d->addr          )) ||
	       !isadsv(u.u_procp,btop(d->addr+d->size-1)) )
	   { error = EINVAL;
	     break;
	   }
	  d = (struct sharedesc *) data;
	  a = d->qbhandle;
	  e = a + d->size - 1;
	  if ((a < KA_EXPORTBASE) || (e < KA_EXPORTBASE))
	   { error = EINVAL;
	     break;
	   }
	  a = (a - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  e = (e - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  if ((a < 1) || (a > 3) || (a != e))
	   { error = EINVAL;
	     break;
	   }
	  u.u_procp->p_flag |= SULOCK;
	  vslock(d->addr,d->size);
	  u.u_error = 0;
	  section_lock();
	  map_physical(u.u_procp,d->qbhandle>>9,d->size/CLBYTES,d->addr);
	  section_unlock();
	  error = u.u_error;
	}
       break;
    case KAIOC_EXPORT:
	{ register struct sharedesc *d;
	  int a;
	  int e;
	  d = (struct sharedesc *) data;
	  if ((d->size & CLOFSET) || !claligned(d->addr))
	   { error = EALIGN;
	     break;
	   }
	  if (!useracc(d->addr,d->size,B_WRITE))
	   { error = EFAULT;
	     break;
	   }
	  if ( !isadsv(u.u_procp,btop(d->addr          )) ||
	       !isadsv(u.u_procp,btop(d->addr+d->size-1)) )
	   { error = EINVAL;
	     break;
	   }
	  d = (struct sharedesc *) data;
	  a = d->qbhandle;
	  e = a + d->size - 1;
	  if ((a < KA_EXPORTBASE) || (e < KA_EXPORTBASE))
	   { error = EINVAL;
	     break;
	   }
	  a = (a - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  e = (e - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  if ((a != 0) || (e != 0))
	   { error = EINVAL;
	     break;
	   }
	  lock_memory(d->addr,d->size);
	  ka_uqbaccess(d->addr,d->size>>9,d->qbhandle);
	}
       break;
    case KAIOC_SIMPORT:
	{ register struct sharedesc *d;
	  char msg[17];
	  int a;
	  d = (struct sharedesc *) data;
	  if (d->qbhandle < KA_EXPORTBASE)
	   { error = EINVAL;
	     break;
	   }
	  a = (d->qbhandle - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  if ((a == unit) || (a < 0) || (a > 3))
	   { error = EINVAL;
	     break;
	   }
	  msg[0] = MSG_IMPORT;
	  *(int *)&msg[1] = 17;
	  *(int *)&msg[5] = (int)d->addr;
	  *(int *)&msg[9] = d->size;
	  *(int *)&msg[13] = d->qbhandle;
	  error = ka_msgbufio(kato[unit],17,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_IMPORT,0);
	}
       break;
    case KAIOC_SEXPORT:
	{ register struct sharedesc *d;
	  char msg[17];
	  int a;
	  d = (struct sharedesc *) data;
	  if (d->qbhandle < KA_EXPORTBASE)
	   { error = EINVAL;
	     break;
	   }
	  a = (d->qbhandle - KA_EXPORTBASE) / KA_EXPORTSIZE;
	  if (a != unit)
	   { error = EINVAL;
	     break;
	   }
	  msg[0] = MSG_EXPORT;
	  *(int *)&msg[1] = 17;
	  *(int *)&msg[5] = (int)d->addr;
	  *(int *)&msg[9] = d->size;
	  *(int *)&msg[13] = d->qbhandle;
	  error = ka_msgbufio(kato[unit],17,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_EXPORT,0);
	}
       break;
    case KAIOC_REQSIGS:
       kasigprocs[unit] = u.u_procp->p_pid;
       break;
    case KAIOC_GETSKV:
	{ char msg[5];
	  int len;
	  int n;
	  struct kamsgarea *area;
	  int s;
	  struct blkdescto *d;
	  d = (struct blkdescto *) data;
	  msg[0] = MSG_VERSION;
	  *(int *)&msg[1] = 5;
	  error = ka_msgbufio(kato[unit],5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  s = splhigh();
	  area = kafrom[unit];
	  while (1)
	   { if (ka_waitformsg(area,unit,d->timeout))
	      { error = ETIMEDOUT;
		break;
	      }
	     ka_msgbufio(area,5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_READ|KA_PEEK);
	     if (msg[0] != MSG_VERSION)
	      { printf("non-VERSION (type %d) from ka %d\n",0xff&(int)msg[0],unit);
		ka_consume(area,0);
		continue;
	      }
	     ka_msgbufio(area,5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_READ);
	     ka_ring_doorbell(unit);
	     len = (*(int *)&msg[1]) - 5;
	     n = (len > d->size) ? d->size : len;
	     error = ka_msgbufio(area,n,d->addr,UIO_USERSPACE,KA_CANWAIT,KA_READ|KA_PEEK);
	     ka_msgbufio(area,len,(char *)0,0,KA_CANWAIT,KA_SKIP);
	     ka_ring_doorbell(unit);
	     break;
	   }
	  splx(s);
	}
       break;
    case KAIOC_SBRK:
	{ char msg[9];
	  int len;
	  int n;
	  struct kamsgarea *area;
	  int s;
	  msg[0] = MSG_SBRK;
	  *(int *)&msg[1] = 9;
	  *(int *)&msg[5] = * (int *) data;
	  error = ka_msgbufio(kato[unit],9,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  s = splhigh();
	  area = kafrom[unit];
	  while (1)
	   { ka_waitformsg(area,unit,0);
	     ka_msgbufio(area,5,msg,UIO_SYSSPACE,KA_CANWAIT,KA_READ|KA_PEEK);
	     if (msg[0] != MSG_SBRK)
	      { printf("non-SBRK (type %d) from ka %d\n",0xff&(int)msg[0],unit);
		ka_consume(area,0);
		continue;
	      }
	     ka_msgbufio(area,9,msg,UIO_SYSSPACE,KA_CANWAIT,KA_READ);
	     ka_ring_doorbell(unit);
	     * (int *) data = * (int *) &msg[5];
	     break;
	   }
	  splx(s);
	}
       break;
    case KAIOC_SIGNAL:
	{ char msg[9];
	  msg[0] = MSG_SIGNAL;
	  *(int *)&msg[1] = 9;
	  *(int *)&msg[5] = *(int *)data;
	  error = ka_msgbufio(kato[unit],9,msg,UIO_SYSSPACE,KA_CANWAIT,KA_WRITE);
	  ka_ring_doorbell(unit);
	  if (error) break;
	  ka_getack(unit,MSG_SIGNAL,0);
	}
       break;
    case KAIOC_ONCRASH:
	{ kacrashprocs[unit] = u.u_procp->p_pid;
	  kacrashsig[unit] = * (int *) data;
	}
       break;
    case KAIOC_GETCRASH:
	{ error = copyout((caddr_t)&kacrash[unit],
			  (caddr_t)*(CRASHINFO**)data, sizeof(CRASHINFO));
	  kacrash[unit].type = CI_NULL;
	}
       break;
    case KAIOC_SETCLEAR:
       s = splhigh();
       if (*(int *)data)
	{ kaflags[unit] |= KA_CLEAR;
	}
       else
	{ kaflags[unit] &= ~KA_CLEAR;
	}
       splx(s);
       break;
    case KAIOC_SETSTOP:
       s = splhigh();
       if (*(int *)data)
	{ kaflags[unit] |= KA_STOP;
	}
       else
	{ kaflags[unit] &= ~KA_STOP;
	}
       splx(s);
       break;
  }
 return(error);
}

kaintr(unit)
int unit;
{
 register int cpuno;
 char buf[5];
 int err;
 register struct kamsgarea *area;
 int wantwakeup;
 static int lasthead[4] = { -1, -1, -1, -1 };
 int rtclk;
 int s;
 int wanttick;

 s = splhigh();

#ifdef RCI
 if (unit) /* ...then this is a fake call from the hardclock interrupt */
  { if (!RCIclockDevice)
     { kabboards[0]->common.time_rt[0] ++;
     }
  }
 wanttick = 0;
 for (cpuno=0;cpuno<4;cpuno++)
  { rtclk = kabboards[0]->common.time_rt[cpuno];
    if (rtclk != ka_rci_clocks[cpuno])
     { ka_rci_clocks[cpuno] = rtclk;
       ka_wanttick[cpuno] = 1;
       wanttick = 1;
     }
  }
 if (wanttick)
  { ka_rttick();
  }
#endif

 wantwakeup = 0;
 for (cpuno=1;cpuno<4;cpuno++)
  { if (kapresent[cpuno])
     { int head;
       area = kafrom[cpuno];
       head = area->head; /* read this only once */
       if (head != lasthead[cpuno])
	{ while (1)
	   { err = ka_msgbufio(area,5,buf,UIO_SYSSPACE,KA_CANTWAIT,KA_IREAD|KA_PEEK);
	     if (err)
	      { break;
	      }
	     switch (buf[0])
	      { default:
		   err = ka_msgbufio(area,*(int *)&buf[1],(char *)0,0,KA_CANTWAIT,KA_ISKIP);
		   wantwakeup = 1;
		   break;
		case MSG_SIGNAL:
		    { char msg[9];
		      if (*(int *)&buf[1] != 9)
		       { printf("ka error: MSG_SIGNAL with size != 9 (ignored)\n");
			 err = ka_msgbufio(area,*(int *)&buf[1],(char *)0,0,KA_CANTWAIT,KA_ISKIP);
		       }
		      else
		       { err = ka_msgbufio(area,9,msg,UIO_SYSSPACE,KA_CANTWAIT,KA_IREAD|KA_PEEK);
			 if (! err)
			  { if (kasigprocs[cpuno] >= 0)
			     { register struct proc *p;
			       p = pfind(kasigprocs[cpuno]);
			       if (p)
				{ psignal(p,*(int *)&msg[5]);
				}
			     }
			  }
			 ka_consume(area,1);
			 err = ka_msgbufio(area,9,(char *)0,0,KA_CANTWAIT,KA_ISKIP);
		       }
		    }
		   break;
		case MSG_CRASH:
		    { char msg[sizeof(CRASHINFO)+5];
		      if (*(int *)&buf[1] != sizeof(CRASHINFO)+5)
		       { printf("ka error: MSG_CRASH with wrong size (ignored)\n");
			 err = ka_msgbufio(area,*(int *)&buf[1],(char *)0,0,KA_CANTWAIT,KA_ISKIP);
		       }
		      else
		       { err = ka_msgbufio(area,sizeof(CRASHINFO)+5,msg,UIO_SYSSPACE,KA_CANTWAIT,KA_IREAD|KA_PEEK);
			 if (! err)
			  { if (kacrashprocs[cpuno] >= 0)
			     { register struct proc *p;
			       p = pfind(kacrashprocs[cpuno]);
			       if (p)
				{ psignal(p,kacrashsig[cpuno]);
				}
			       kacrash[cpuno] = * (CRASHINFO *) &msg[5];
			     }
			  }
			 ka_consume(area,1);
			 err = ka_msgbufio(area,sizeof(CRASHINFO)+5,(char *)0,0,KA_CANTWAIT,KA_ISKIP);
		       }
		    }
		   break;
	      }
	     if (err)
	      { printf("ka error: size %d can't be skipped in kaintr: error %d\n",*(int *)&buf[1],err);
		printf("          head %d seen %d tail %d\n",head,area->seen,area->tail);
		area->seen = head; /* summarily discard all pending messages */
		area->tail = head;
		wantwakeup = 1;
	      }
	   }
	  lasthead[cpuno] = head;
	  ka_ring_doorbell(cpuno);
	}
     }
  }
 if (wantwakeup)
  { wakeup((caddr_t)&kasleepers);
  }
 splx(s);
}

/* Called when a clock ticks.						 */
#ifndef RCI
ka_rttick() { }
#else
ka_rttick()
{
 int cpu;
 register int curtime;
 int any;
 register struct rcitimeout **tp;
 struct rcitimeout *tofire;
 register struct rcitimeout *t;
 register int n;

 do
  { any = 0;
    tofire = 0;
    for (cpu=3;cpu>=0;cpu--)
     { if (! ka_wanttick[cpu]) continue;
       ka_wanttick[cpu] = 0;
       any ++;
       curtime = ka_rci_clocks[cpu];
       n = 0;
       for ( tp = &ka_rci_timeouts[cpu];
	     *tp && ((*tp)->when<=curtime);
	     tp = &(*tp)->link )
	{ if (++n > 100)
	   { qcirc(cpu,0);
	     tp = &ka_rci_timeouts[cpu];
	     break;
	   }
	}
       if (tp != &ka_rci_timeouts[cpu])
	{ t = ka_rci_timeouts[cpu];
	  ka_rci_timeouts[cpu] = *tp;
	  *tp = tofire;
	  tofire = t;
	}
     }
    for (t=tofire;t;t=t->link)
     { t->explink = t->link;
       t->exphandler = t->handler;
       t->expargument = t->argument;
       t->handler = 0;
     }
    for (t=tofire;t;t=t->explink)
     { if (RCI_switch(t->info,t->exphandler,t->expargument))
	{ RCI_signal(t->info->pid,t->info->crashsig);
	}
     }
  } while (any);
}
#endif

#ifdef RCI
ka_timeout(to)
register struct rcitimeout *to;
{
 register struct rcitimeout **tp;
 register int n;

 n = 0;
 for ( tp = &ka_rci_timeouts[to->clockno];
       *tp && ((*tp)->when<=to->when);
       tp = &(*tp)->link )
  { if (++n > 100)
     { qcirc(to->clockno,1);
       /* NOTREACHED */
     }
  }
 to->link = *tp;
 *tp = to;
 if (to->when <= ka_rci_clocks[to->clockno])
  { ka_wanttick[to->clockno] = 1;
  }
}
#endif

#ifdef RCI
ka_untimeout(to)
register struct rcitimeout *to;
{
 register struct rcitimeout **tp;
 register int n;

 n = 0;
 for ( tp = &ka_rci_timeouts[to->clockno];
       *tp && (*tp != to);
       tp = &(*tp)->link )
  { if (++n > 100)
     { qcirc(to->clockno,1);
       /* NOTREACHED */
     }
  }
 if (*tp) *tp = to->link;
 to->handler = 0;
}
#endif

#ifdef RCI
static qcirc(clockno,crash)
int clockno;
int crash;
{
 register struct rcitimeout *t1;
 register struct rcitimeout *t2;

 printf("More than 100 timeouts on queue %d\n",clockno);
 t1 = ka_rci_timeouts[clockno];
 t2 = t1;
 do
  { printf("%x\n",(int)t1);
    t1 = t1->link;
    t1->handler = 0;
    t2 = t2->link->link;
  } while (t1 != t2);
 printf("Clearing queue%s\n",crash?" and forcing crash":"");
 ka_rci_timeouts[clockno] = 0;
 if (crash)
  { *(char *)0xc0000000 = 0;
    /* NOTREACHED */
  }
}
#endif

ka_asyncsig(cpu,sig)
int cpu;
int sig;
{
 char msg[9];
 int error;

 if ((cpu < 0) || (cpu > 3)) panic("ka_asyncsig");
 msg[0] = MSG_SIGNAL | MSG_NOACK;
 *(int *)&msg[1] = 9;
 *(int *)&msg[5] = sig;
 error = ka_msgbufio(kato[cpu],9,msg,UIO_SYSSPACE,KA_CANTWAIT,KA_WRITE);
 ka_ring_doorbell(cpu);
 return(error?-1:0);
}

/* called on a machine check due to Q-bus timeout */
/* return 1 to ignore machine check, 0 to panic normally */
/* works due to gross hack in ../vax/machdep.c, machinecheck() */
kaqbmchk(mcf)
struct mc630frame *mcf;
{
 return(RCI_kaqbcrash(mcf));
}

/* called from RCI code to get blackboard addresses */
/* arg is cpu #, 0 for arbiter or 1-3 for slave */
struct kabboard *kabbaddr(cpu)
int cpu;
{
 return(((cpu<0)||(cpu>3))?0:&kabboards[cpu]->common);
}

#endif
