/*
 * Megatek Whizzard 7200 PE Parallel Interface Driver
 *
 * Joel Carter/Dan Ladermann (TWG)
 */

#include "../h/param.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/selch.h"
#include "../h/meg.h"
#undef trace
/* Various manifests */
#define MAXMG   2               /* leave it at 2 for now */
#define MGTRACE (01<<31)

/* Status bit definitions */

#define BTO     0x04        /* Bus Timeout- Graphics Bus Interrupt */
#define BUSY    0x08
#define PREQ    0x10    /* Peripheral Request- Peripheral Bus Interrupt */
#define SIN02   0x20
#define IFBT0   0x40
#define IDR     0x80

/* ULI commands */
/* All data transfers with these commands are halfword transfers */

#define RESETB          0x2A
#define STOPREAD        0x29
#define RESET           0x28
#define WRPERDATA       0x27
#define WRPERADDR       0x26
#define WRGRDATA        0x25
#define WRGRADDR        0x24
#define RDPERDATA       0x23
#define RDPERADDR       0x22
#define RDGRDATA        0x21
#define RDGRADD         0x20

/* Condition Codes for Peripheral Address */
/* Peripheral Address is of the form CCPPPPPP
 * where PPPPPP is the address and CC is one
 * of the following:
 */

#define LOADADD         00
#define INTACK          01
#define PERREAD         02
#define PERPULSE        03

/* ULI Interrupt Control definitions */
#define DISABLE 0x80
#define ENABLE  0x40
#define DISARM  0xC0

/* Peripheral Command Flags */
#define P_READ 0
#define P_WRITE 1
/* Peripheral Interrupt Codes */
#define IPCU 0x34
#define FSCD 0x18
#define PICK 0x10
#define RASTER 0x08

/* Vector Memory Reserved Locations */
#define INTMASK 0x0E
typedef int Ulicommand;

int      nmeg = 1;
extern char    periaddr[];

extern int      megselch;
extern char     megaddr[];
int megstart(), megscintr(), megstrategy();
#define uliaddr megaddr[0]       /* address of the ULI */

struct buf  megbuf;

struct selchq megscq {
	&megstart,
	&megscintr,
        0
};

/* State bits for software model */
#define MGISOPEN        0x01

struct megtab {
	char    m_state;
	unsigned short m_data;      /* data returned from device */
	unsigned short m_bto_data[2]; /* data returned from interrupt */
} megtab[MAXMG];

struct buf meghead;             /* header of queue of activation records */

unsigned short graphaddr;       /* address into 64K graphics memory */
/* I assume that an assignment of an off_t into an unsigned short
 * gives me the least significant 16 bits
 */





megopen (dev, flag)
  dev_t    dev;
  int      flag;
{
    register int     min;

    trace(MGTRACE, "megopen", dev);
    min = minor(dev);
    if(0 <= min && min < nmeg)
        if (flag)
	    if (! (megtab[min].m_state & MGISOPEN) ) {
                /* initialize (maybe) */
		megtab[min].m_state = MGISOPEN;
	    } else
                u.u_error = EACCES;
        else
            u.u_error = EPERM;
    else
        u.u_error = ENXIO;
}

megclose(dev)
  dev_t   dev;
{
    trace(MGTRACE, "megclose", dev);
    megtab[minor(dev)].m_state = 0;
}

megread(dev)
  dev_t dev;
{
	trace(MGTRACE,"megread",dev);

	if (minor(dev) == 0)    /* graphics memory */
	{
	trace(MGTRACE,"bpio",dev);

	physio(megstrategy, &megbuf, dev, B_READ);
	trace(MGTRACE,"apio",dev);

	}
	else if (minor(dev) == 1)       /* peripheral bus */
	{
	trace(MGTRACE,"bpcm",dev);
	percommand(dev,P_READ);
	trace(MGTRACE,"apcm",dev);
	}
}

megwrite(dev)
  dev_t    dev;
{
	trace(MGTRACE, "megwrite", dev);


	if (minor(dev) == 0)    /* graphics memory */
	physio (megstrategy, &megbuf, dev, B_WRITE);
	else if (minor(dev) == 1)       /* peripheral bus */
	percommand(dev,P_WRITE);
}

percommand(dev,cmd)
   dev_t   dev;
{
	short addr;
	trace(MGTRACE,"percom",dev);

	selchreq(megselch,&megscq);
	addr = periaddr[minor(dev)];
	oc(uliaddr,WRPERADDR);          /* give ULI the peripheral address */

	whilebusy(uliaddr);
	wh(uliaddr,addr);              /* write the peripheral address */
	oc(uliaddr, cmd? RDPERDATA : WRPERDATA);
	whilebusy(uliaddr);
	if (cmd == P_READ)
		megtab[minor(dev)].m_data = rdh(uliaddr);    /* get peripeheral
							   value for user */
}


megstrategy (bp)
  register struct buf  *bp;
{

    trace(MGTRACE, "megstrat, bytes=", megbuf.b_bcount);
    graphaddr = u.u_offset;             /* get file location from seek */
    selchreq (megselch, &megscq);
}

megstart()
{

    trace(MGTRACE, "megstart", 0);
    oc(megselch,STOP);
    oc (uliaddr, WRGRADDR);
/*  while (ss(uliaddr) == BUSY);        not necessary or wrong because
					interface is waiting for a write */
    wh(uliaddr,graphaddr);
    while (ss(uliaddr) == BUSY);


    wdh (megselch, megbuf.b_un.b_addr);
    wdh (megselch, megbuf.b_un.b_addr+megbuf.b_bcount-1);
    oc (uliaddr,(megbuf.b_flags&B_READ)?(RDGRDATA):(WRGRDATA));
    oc (megselch, (megbuf.b_flags&B_READ)?READ_GO:GO);
}
 /* this routine processes interrupts that are internally generated by the
    selch */
megscintr()
{
    register int     endadd;

    trace(MGTRACE, "megscintr", 0);
    oc (megselch, STOP);
    if (megbuf.b_flags & B_READ)
    {
	oc(uliaddr,STOPREAD);
    }
    selchfree (megselch);

    trace(MGTRACE, "megscintr, stat=", ss(uliaddr));
    endadd = rh (megselch);
    if (endadd < megbuf.b_un.b_addr+megbuf.b_bcount-1)
	megbuf.b_flags |= B_ERROR;
    else
	megbuf.b_resid = 0;
    trace(MGTRACE, "megscintr1, stat=", ss(uliaddr));
    iodone (&megbuf);





}
/* this routine processes interrupts from the ULI
 * there are two types of possible interrupts from the ULI
 * one is a peripheral bus interrupt signalled by PREQ in the ULI
 * status register
 * the other is a graphics bus interrupt  signalled by BTO in the ULI
 * status register
 * BTO is automatically reset by the next command to the ULI
 * megintr is guaranteed to have the selch ( otherwise the interrupt )
 * would never have been seen
 * this code will run to completion
 */
megintr(dev)
  int    dev;    /* from devmap[] in c.c */
{
    trace(MGTRACE, "megintr", dev);


/* handle peripheral interrupts */

    if (ss(uliaddr) == PREQ)            /* peripheral request*/
    {
	register int peraddr;

	oc (uliaddr, (INTACK << 6) );         /* acknowledge interrupt */
	while (ss(uliaddr) != IDR);  /* wait for the peripheral's address
					to be latched */
	peraddr = rh(uliaddr);                /* read the address */
	switch (peraddr) {

		case IPCU: megtab[minor(dev)].m_data = rh(IPCU); /* return data half
							* word to user
							*/
		case FSCD:

		case PICK:

		case RASTER:
		default:
			break;
	}
     }
     else
     if (ss(uliaddr) == BTO)            /* graphics bus interrupt */
     {
    trace(MGTRACE, "megbtointr", 0);

	oc(uliaddr,WRGRADDR);
	wh(uliaddr,INTMASK);
	whilebusy(uliaddr);
	oc(uliaddr, RDGRDATA);
	megbuf.m_bto_data[1] = rh(uliaddr);     /* least sign. 16 bits */
	megbuf.m_bto_data[2] = rh(uliaddr);     /* most sign. 16 bits */
/* A valid read from Vector Memory clears the BTO bit */
/*      megbuf.b_flags |= B_ERROR;      /* return an error to user program
					 * can't use u.u_error
					 */
     }
     else printf("spurious int %d\n",dev);  /* spurious interrupt */
}

megioctl(dev,cmd,addr,flag)
caddr_t addr;
{
	trace(MGTRACE,"megioctl",cmd);

	if (cmd = RESET)
	{
	    selchreq(megselch,&megscq);
	    oc(uliaddr,RESET);
	    whilebusy(uliaddr);
	    sleep(2);       /* delay to make sure the IPCU has done its job */
	    selchfree(megselch);
	 }
	 else
	 if (cmd = DISABLE)
	 {
	    selchreq(megselch,&megscq);
	    oc(uliaddr,DISABLE);
	    whilebusy(uliaddr);
	    selchfree(megselch);
	 }
	 else
	 if (cmd = ENABLE)
	 {
	    selchreq(megselch,&megscq);
	    oc(uliaddr,ENABLE);
	    whilebusy(uliaddr);
	    selchfree(megselch);
	 }
	 else
	 if (cmd = DISARM)
	 {
	    selchreq(megselch,&megscq);
	    oc(uliaddr,DISARM);
	    whilebusy(uliaddr);
	    selchfree(megselch);
	 }




}

whilebusy(addr)
int addr;
{
    trace(MGTRACE, "bwhilebusy addr = ", addr);

    while (ss(addr) == BUSY) ;
    trace(MGTRACE, "awhilebusy addr = ", addr);

}
