/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include <vax/mtpr.h>
#include <vax/psl.h>

#include "../sys/rciext/kacomm.h"
#include "../sys/rciext/kaipcr.h"
#include "../sys/rciext/kamsgs.h"

#include "sig.h"

extern char version[];
extern unsigned short int *ipcrs;
extern struct kamsgarea *toarea;
int space;
int ptr;

beginmsg(size)
int size;
{
 do
  { space = toarea->tail - toarea->head;
    if (space <= 0)
     { space += KA_MSGDATASIZE;
     }
  } while (space < size);
 ptr = toarea->head;
}

putmsgbyte(v)
int v;
{
 toarea->data[ptr++] = v;
 if (ptr >= KA_MSGDATASIZE)
  { ptr -= KA_MSGDATASIZE;
  }
}

putmsgint(v)
int v;
{
 putmsgbyte(v&0xff);
 putmsgbyte((v>>8)&0xff);
 putmsgbyte((v>>16)&0xff);
 putmsgbyte((v>>24)&0xff);
}

endmsg()
{
 toarea->head = ptr;
}

int getmsgbyte(msgarea)
struct kamsgarea *msgarea;
{
 int rv;

 rv = 0xff & msgarea->data[msgarea->tail];
 msgarea->tail = (msgarea->tail + 1) % KA_MSGDATASIZE;
 return(rv);
}

int getmsgint(msgarea)
struct kamsgarea *msgarea;
{
 register int rv;
 register int i;

 rv = 0;
 for (i=0;i<32;i+=8)
  { rv |= getmsgbyte(msgarea) << i;
  }
 return(rv);
}

getmsgblock(msgarea,addr,size)
register struct kamsgarea *msgarea;
char *addr;
int size;
{
 int n;

 n = KA_MSGDATASIZE - msgarea->tail;
 if (size < n)
  { bcopy(&msgarea->data[msgarea->tail],addr,size);
    msgarea->tail += size;
  }
 else
  { bcopy(&msgarea->data[msgarea->tail],addr,n);
    size -= n;
    if (size > 0)
     { bcopy(&msgarea->data[0],addr+n,size);
     }
    msgarea->tail = size;
  }
}

ringdoorbell(proc)
int proc;
{
 ipcrs[proc] |= KA_DBI;
}

sendack(type)
int type;
{
 beginmsg(6);
 putmsgbyte(MSG_ACK);
 putmsgint(6);
 putmsgbyte(type);
 endmsg();
 ringdoorbell(0);
}

doorbell()
{
 extern struct kamsgarea *fromarea;
 extern struct kamsgarea *toarea;
 int bits;
 int type;
 int msgsize;
 int didsome;

 didsome = 0;
 while (fromarea->head != fromarea->tail)
  { didsome = 1;
    type = getmsgbyte(fromarea);
    bits = type & MSG_BITS;
    type &= MSG_TYPE;
    msgsize = getmsgint(fromarea);
#define CHKMSGSIZE(siz) if(msgsize!=(siz))goto badmsgsiz;
    if (0)
     {
badmsgsiz:
       splhigh();
       printf("message of type %d claims %d bytes\n",type,msgsize);
       panic("bad message size");
     }
    switch (type)
     { default:
	  splhigh();
	  printf("doorbell: bad message type %d");
	  panic("bad message type");
	  break;
       case MSG_BOOTDATA:
       case MSG_BOOTEXEC:
	  panic("boot protocol message to minikernel");
	  break;
       case MSG_DEBUG:
	  CHKMSGSIZE(5);
	  debugger();
	  if (! (bits & MSG_NOACK)) sendack(MSG_DEBUG);
	  break;
       case MSG_CLEAR:
	  CHKMSGSIZE(5);
	  clearslave();
	  if (! (bits & MSG_NOACK)) sendack(MSG_CLEAR);
	  break;
       case MSG_DOWNLOAD:
	   { int addr;
	     int size;
	     addr = getmsgint(fromarea);
	     size = getmsgint(fromarea);
	     CHKMSGSIZE(13+size);
	     if ((addr & 0xc0000000) || ((addr+size-1) & 0xc0000000))
	      { panic("protocol error: DOWNLOAD to non-p0 address");
	      }
	     makeaddrrange(addr,size);
	     getmsgblock(fromarea,(char *)addr,size);
	     force_exit();
	     if (! (bits & MSG_NOACK)) sendack(MSG_DOWNLOAD);
	   }
	  break;
       case MSG_START:
	   { extern int uentry;
	     extern int uarg;
	     CHKMSGSIZE(13);
	     uentry = getmsgint(fromarea);
	     uarg = getmsgint(fromarea);
	     resetuserstack();
	     if (! (bits & MSG_NOACK)) sendack(MSG_START);
	   }
	  break;
       case MSG_STOP:
	  CHKMSGSIZE(5);
	  force_exit();
	  if (! (bits & MSG_NOACK)) sendack(MSG_STOP);
	  break;
       case MSG_ALLOC:
	   { int addr;
	     int size;
	     CHKMSGSIZE(13);
	     addr = getmsgint(fromarea);
	     size = getmsgint(fromarea);
	     if ((addr & 0xc0000000) || ((addr+size-1) & 0xc0000000))
	      { panic("protocol error: ALLOC to non-p0 address");
	      }
	     makeaddrrange(addr,size);
	     bzero((char *)addr,size);
	     force_exit();
	     if (! (bits & MSG_NOACK)) sendack(MSG_ALLOC);
	   }
	  break;
       case MSG_READONLY:
	   { int addr;
	     int size;
	     CHKMSGSIZE(13);
	     addr = getmsgint(fromarea);
	     size = getmsgint(fromarea);
	     if ((addr & 0xc0000000) || ((addr+size-1) & 0xc0000000))
	      { panic("protocol error: READONLY to non-p0 address");
	      }
	     readonlyrange(addr,size);
	     force_exit();
	     if (! (bits & MSG_NOACK)) sendack(MSG_READONLY);
	   }
	  break;
       case MSG_IMPORT:
	   { int addr;
	     int size;
	     int phys;
	     CHKMSGSIZE(17);
	     addr = getmsgint(fromarea);
	     size = getmsgint(fromarea);
	     phys = getmsgint(fromarea);
	     if ((addr & 0xc0000000) || ((addr+size-1) & 0xc0000000))
	      { panic("protocol error: IMPORT to non-p0 address");
	      }
	     if ( ((unsigned int)(phys-KA_QBUSBASE) > KA_QBUSSIZE) ||
		  ((unsigned int)(phys+size-1-KA_QBUSBASE) > KA_QBUSSIZE) )
	      { panic("protocol error: IMPORT from non-Qbus physical");
	      }
	     importmem(addr,size,phys);
	     if (! (bits & MSG_NOACK)) sendack(MSG_IMPORT);
	   }
	  break;
       case MSG_EXPORT:
	   { int addr;
	     int size;
	     int phys;
	     CHKMSGSIZE(17);
	     addr = getmsgint(fromarea);
	     size = getmsgint(fromarea);
	     phys = getmsgint(fromarea);
	     if ((addr & 0xc0000000) || ((addr+size-1) & 0xc0000000))
	      { panic("protocol error: EXPORT from non-p0 address");
	      }
	     if ( ((unsigned int)(phys-KA_QBUSBASE) > KA_QBUSSIZE) ||
		  ((unsigned int)(phys+size-1-KA_QBUSBASE) > KA_QBUSSIZE) )
	      { panic("protocol error: EXPORT to non-Qbus physical");
	      }
	     exportmem(addr,size,phys);
	     if (! (bits & MSG_NOACK)) sendack(MSG_EXPORT);
	   }
	  break;
       case MSG_SIGNAL:
	   { int sig;
	     CHKMSGSIZE(9);
	     sig = getmsgint(fromarea);
	     if ((sig >= 0) && (sig < NSIGNALS))
	      { sendsig(sig);
	      }
	     if (! (bits & MSG_NOACK)) sendack(MSG_SIGNAL);
	   }
	  break;
       case MSG_VERSION:
	   { int i;
	     char *cp;
	     i = strlen(version);
	     beginmsg(6+i);
	     putmsgbyte(MSG_VERSION);
	     putmsgint(6+i);
	     for (cp=version;*cp;cp++)
	      { putmsgbyte(*cp);
	      }
	     putmsgbyte('\0');
	     endmsg();
	   }
	  break;
       case MSG_SBRK:
	   { int n;
	     int a;
	     CHKMSGSIZE(9);
	     n = getmsgint(fromarea);
	     a = sbrk(n,(n&511)==0);
	     beginmsg(9);
	     putmsgbyte(MSG_SBRK);
	     putmsgint(9);
	     putmsgint(a);
	     endmsg();
	   }
	  break;
       case MSG_AST:
	   { int ra;
	     int arg;
	     CHKMSGSIZE(13);
	     ra = getmsgint(fromarea);
	     arg = getmsgint(fromarea);
	     ast((int (*)())ra,arg);
	     if (! (bits & MSG_NOACK)) sendack(MSG_AST);
	   }
	  break;
     }
#undef CHKMSGSIZE
  }
 if (didsome)
  { ringdoorbell(0);
  }
}
