#include <sys/fcntl.h>
#include <sys/open.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/edt.h>
#include <sys/sysmacros.h>
#include <sys/param.h>

#include "rciioctl.h"
#include "bitreg.h"

/* the idiots the idiots the *idiots*!  This is perfectly legal!
   I had hoped nobody but Sun was shipping a compiler this backward..... */
/* static unsigned char idcheck[20] = "VMEIDXYC240    1 11 "; */
static unsigned char idcheck[] = "VMEIDXYC240    1 11 ";

extern int bit_internal_major;
extern BitSoftc biti[];

static int goodid();

#define REMOTE_BOARD_REGS	0xB380A000

bitintr(unit)
int unit;
{
	/* Called in response to an interrupt from the BIT */

	register BitSoftc *info;
	register volatile Bit *bit;
	register volatile Bit *remoteBit;
	int s;

	info = &biti[unit];
	bit = info->bit;

	s = splhi();	/* just a bit paranoid */

	/* clear all programmed interrupts */
	if (bit->lstatus & BIT_LS_PSI_RECEIVED)
	 { bit->lcmd = BIT_LC_PSI_CLEAR;
	 }
	if (!(bit->lstatus & BIT_LS_NO_REMOTE))
	 { if (!badaddr(&bit->rcsr,1))
	    { if (bit->rcsr & BIT_RS_PMI_RECEIVED)
	       { bit->rcsr = (BIT_RC_PMI_CLEAR | 0x1);
	       }
	    }
	   else
	    { cmn_err (CE_WARN, "bit0: bitintr: can't access remote side\n");
	    }

	   /* clear programmed interrupts on the other bus ... */

#if 0
	   /* this doesn't seem to be necessary */
	   if (!badaddr(&bit->iack,1))
	    { volatile unsigned char iack;
	      iack = bit->iack;
	    }
	   else
	    { cmn_err (CE_WARN, "bit0: bitintr: couldn't do iack\n");
	    }
#endif

	   /* set remote address modify to 0x2d so we can access
	      bit3 board registers on the remote chassis */
	   bit->addrModify = AMOD_A16_PRIV;
	   remoteBit = (Bit*)REMOTE_BOARD_REGS;
	   DELAY(10);	/* paranoid */
	   if (!badaddr(&remoteBit->lcmd,1) && 
	       !badaddr(&remoteBit->lstatus,1))
	    { if (remoteBit->lstatus & BIT_LS_PSI_RECEIVED)
	       { remoteBit->lcmd = BIT_LC_PSI_CLEAR;
		 DELAY(10);
		 if (remoteBit->lstatus & BIT_LS_PSI_RECEIVED)
		  { cmn_err (CE_WARN, "bit0: bitintr: couldn't clear PSI\n");
		  }
	       }
	    }
	   else
	    { cmn_err (CE_WARN, "bit0: bitintr: couldn't access far board\n");
	    }
	   bit->addrModify = AMOD_A24_NOPRIV;
	   DELAY(10);	/* paranoid */
	 }
	
	splx(s);

	if (! (info->flags & BIT_OPEN))
	 { /* Hmmm ... shouldn't happen */
	   cmn_err (CE_CONT, "bit%d: stray interrupt\n", unit);
	 }
	else
	 { if (info->intrCnt < info->qsize)
	    { info->intrCnt++;		/* bump the counter */
	      pollwakeup (&info->inpq);	/* wakeup any pending selects */
	    }
	 }
}

static int boardinit(bit)
volatile Bit *bit;
{
	/* Init the board registers */

	/* do this only if the remote board is connected */
	if (!(bit->lstatus & BIT_LS_NO_REMOTE))
	 { bit->rcsr = (BIT_RC_PMI_CLEAR | 0x1);
	   bit->lcmd = (BIT_LC_ERR_RESET|BIT_LC_PSI_CLEAR);
	   bit->addrModify = AMOD_A24_NOPRIV;
	   DELAY(10);		/* wait 10 usec; paranoid */	      
	   if (bit->lstatus)
	    { printf ("bit0: lstatus=0x%x\n", bit->lstatus);
	    }
	 }
}

bitedtinit(e)
struct edt *e;
{
	/* Initialization routine. Verify that the board is there, and
	   initialize its registers to something reasonable. */

	volatile Bit *bit;
	int i;

	biti[e->e_intr_info->v_unit].bit = 0;
	bit = (volatile Bit *) e->e_base;
	for (i=0;i<8;i++)
	 { if (badaddr(e->e_base+i,1))
	    { printf ("bit0: not present");
	      return(0);
	    }
	 }
	boardinit(bit);
	biti[e->e_intr_info->v_unit].bit = bit;
	printf("bitedtinit: unit %d\n",e->e_intr_info->v_unit);
}

bitopen(dev,flag,otyp)
dev_t dev;
int flag;
int otyp;
{
	/* This device is an exclusive open device: only one process
	   may open it at a time. This prevents contention problems
	   and makes the driver software easier to implement. */

	BitSoftc *info;
	int s;

	info = &biti[minor(dev)];

	if (! info->bit)
	 { u.u_error = ENXIO;
	   return;
	 }
	s = splhi();
	if (info->flags & BIT_OPEN)
	 { splx(s);
	   u.u_error = EBUSY;
	   return;
	 }
	else
	 { info->flags |= BIT_OPEN;
	 }
	splx(s);

	info->qsize = TQ_DEFAULT_SIZE;
	boardinit(info->bit);
	info->intrCnt = 0;
}

bitclose(dev,flag,otyp)
dev_t dev;
int flag;
int otyp;
{
	/* When closing the device, reset the registers to what
	   they were when the device was openned. */

	BitSoftc *info;
	int s;

	info = &biti[minor(dev)];

	s = splhi();
	info->flags &= ~(BIT_OPEN);
	splx(s);
}

bitread(dev)
dev_t dev;
{
	/* The read routine returns a single byte (set to 1) if an interrupt
	   arrived. */

	BitSoftc *info;
	int cnt, s;
	char ack = 1;

	info = &biti[minor(dev)];
	s = splhi();
	cnt = info->intrCnt;
	if (cnt)
	 { info->intrCnt--;
	 }
	splx(s);
	if (cnt)
	 { if (copyout (&ack, u.u_base, 1))
	    { u.u_error = EFAULT;
	      return;
	    }
	   u.u_count--;
	 }
}

bitwrite(dev)
dev_t dev;
{
	u.u_error = ENXIO;
}

bitpoll(dev, events)
dev_t dev;
int *events;
{
	/* Check to see if any interrupt events have arrived. If not,
	   then queue the process for a possible select wakeup. */

	BitSoftc *info;
	int ready;
	int cnt, s;

	info = &biti[minor(dev)];

	ready = *events;
	if (ready & POLLIN)
	 { s = splhi();
	   if (info->intrCnt == 0)
	    { pollqueue (&info->inpq, u.u_procp);
	      *events &= ~POLLIN;
	    }
	   splx(s);
	 }
}

bitioctl(dev,cmd,arg)
dev_t dev;
int cmd, arg;
{
	BitSoftc *info;

	info = &biti[minor(dev)];

	switch (cmd)
	 { default:
	    { u.u_error = ENOTTY;
	      break;
	    }
	   case RCIIOC_CONFIRM:
	    { break;
	    }
	   case RCIIOC_INTR_ENABLE:
	    { int mask = arg;
	      volatile Bit *bit = info->bit;
	      int s;

	      s = splhi();
	      /* interrupt enable/disbale code would normally go
		 here, but I/O device doesn't have any */
	      splx(s);
	      break;
	    }
	   case RCIIOC_SET_INTRQ:
	    { int size = arg, s;

	      if (size <= 0)
	       { u.u_error = EINVAL;
	       }
	      else
	       { s = splhi();
		 info->qsize = size;
		 info->intrCnt = 0;
		 splx(s);
	       }
	      break;
	    }
	   case RCIIOC_GET_INTRQ:
	    { int size;

	      size = info->qsize;
	      if (copyout ((char*)&size, (char*)arg, sizeof(int)))
	       { u.u_error = EFAULT;
		 break;
	       }
	      break;
	    }
	 }
}
