/*
 * Line Interupt Driver
 *
 * This driver supports the PE eight line interupt module.
 *
 * The li module has input lines that when asserted, generate
 * an interupt. The line must be enabled to do this.
 *
 * From the UNIX view, each line will be a seperate device.
 * The minor device code is used to select a specific board
 * and line. When the device is open, the corresponding line
 * is enabled. When a process performs a read, it is suspended
 * if no interupts have occured since the device was open
 * or the last read. When the li interupts, it wakes any
 * waiting processes. Finally, when the device is closed,
 * the line is disabled.
 */

#include "../h/local.h"

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)li.c    	1.1	 16:17:33 - 83/02/23 ";
#endif  SCCS_ID

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"

#define LIPRI   75      /* line interupt wakeup priority */

#define MAXLINES        16
#define LINESPERBOARD   8

extern  char            liaddr[];
extern  int             nli;

/* Controller Commands */
#define SET             0x00            /* wd's are interupt mask */
#define ENABLE          0x40            /* enable interupts */

/* State bits */
#define LIENABLE        0x01            /* Line is open and enabled */
#define LIWAITING       0x02            /* Process waiting for interupt */

struct litab {
	char            li_state;       /* State bits (see above) */
	char            li_count;       /* Interupts since last read */
};
static struct litab litab[MAXLINES];

liopen(dev, flag)
  dev_t dev;
  int   flag;
{
    int         minr;

    minr = minor(dev);
    if (0 <= minr && minr < nli)
	if (!flag)
	    if (!(litab[minr].li_state & LIENABLE)) {
		litab[minr].li_state = LIENABLE;
		lienab();
	    } else
		u.u_error = EBUSY;
	else
	    u.u_error = EACCES;
    else
	u.u_error = ENXIO;
}

liclose(dev)
  dev_t  dev;
{
    litab[minor(dev)].li_state = 0;
    lienab();
}

liread(dev)
  dev_t  dev;
{
    register struct litab   *li;

    spl4();
    li = &litab[minor(dev)];
    while (li->li_count == 0) {
	li->li_state |= LIWAITING;
	sleep (li, LIPRI);
    }
    li->li_count = 0;
    spl0();
    u.u_base += u.u_count;
    u.u_offset += u.u_count;
    u.u_count = 0;
}

liioctl(dev, cmd, addr, flag)
  dev_t   dev;
  caddr_t addr;
{
}

lienab() {
	register        board;
	register        line;
	register        i;
	char            mask;

    line = 0;
    for (board = 0; board < nli; board += LINESPERBOARD) {
	mask = 0;
	for (i = 0; i < LINESPERBOARD; i++) {
	    mask <<= 1;
	    if (litab[line++].li_state & LIENABLE)
		mask++;
	}
	oc (liaddr[board], SET);
	wd (liaddr[board], mask);
	oc (liaddr[board], ENABLE);
    }
}

liint(dev, status) {
    register struct litab  *li;

    li = &litab[dev];
    li->li_count++;
    if(li->li_state & LIWAITING) {
	li->li_state &= ~LIWAITING;
	wakeup(li);
    }
}
