#
/*
 */

/*
 *   KL/DL-11 driver
 */
#include "../head/param.h"
#include "../head/conf.h"
#include "../head/user.h"
#include "../head/userx.h"
#include "../head/tty.h"

/* base address */
#define	KLADDR	0177560	/* console */
#define	KLBASE	0176500	/* kl and dl11-a */
#define	DLBASE	0175610	/* dl-e */
#define	KLTYPE	0
#define	DLTYPE	01	/* dl-e */
#define	NKL11	1
#define	NDL11	0
#define	RDRENB	01
#define	IENABLE	0100	/* interrupt enable */
#define	DTR	02	/* data teminal ready */
#define	MENABLE	040	/* modem enable */

int	nkl11	NKL11+NDL11;
struct	tty kl11[NKL11+NDL11];

struct klregs {
	int klrcsr;
	int klrbuf;
	int kltcsr;
	int kltbuf;
}

klopen(dev, flag)
{
	register char *addr;
	register struct tty *tp;

	if(dev.d_minor >= NKL11+NDL11) {
		u.u_error = ENXIO;
		return;
	}
	tp = &kl11[dev.d_minor];
	/*
	 * set up minor 0 to address KLADDR
	 * set up minor 1 thru NKL11-1 to address from KLBASE
	 * set up minor NKL11 on to address from DLBASE
	 */
	addr = KLADDR + 8*dev.d_minor;
	tp->t_dtype = KLTYPE;
	if(dev.d_minor)
		addr =+ KLBASE-KLADDR-8;
	if(dev.d_minor >= NKL11) {
		addr =+ DLBASE-KLBASE-8*NKL11+8;
		tp->t_dtype = DLTYPE;
	}
	tp->t_addr = addr;
	tp->t_dev = dev;
	if (flag) {
		addr->klrcsr =| IENABLE|RDRENB;
		addr->kltcsr =| IENABLE;
		(*linesw[tp->t_discp].l_open)(dev, tp);
	}
}

klclose(dev, flag)
{
	register struct tty *tp;

	tp = &kl11[dev.d_minor];
	(*linesw[tp->t_discp].l_close)(dev, tp);
}

klread(dev)
{
	register struct tty *tp;

	tp = &kl11[dev.d_minor];
	(*linesw[tp->t_discp].l_read)(tp);
}

klwrite(dev)
{
	register struct tty *tp;

	tp = &kl11[dev.d_minor];
	(*linesw[tp->t_discp].l_write)(tp);
}

klsgtty(dev, flag)
{
	register struct tty *tp;

	tp = &kl11[dev.d_minor];
	ttioctl(dev, tp, flag);
}

klxint(dev)
{
	register struct tty *tp;

	tp = &kl11[dev.d_minor];
	ttstart(tp);
}

klrint(dev)
{
	register int c, *addr;
	register struct tty *tp;
	int rcsr, status, action;

	tp = &kl11[dev.d_minor];
	addr = tp->t_addr;
	while ((rcsr = addr->klrcsr)<0 || rcsr&DONE) {
		if(rcsr < 0) {
			/*
			 * DL modem transition 
			 */
			status = (rcsr>>10)&(CARRIER|SR)|(rcsr>>12)&CTS;
			action = (*linesw[tp->t_discp].l_mt)(tp, status);
			if(action > 0)
				dlmctl(dev, action);
			continue;
		}
		c = addr->klrbuf & (OVERRUN|FRERROR|PERROR|0377);
		addr->klrcsr =| RDRENB;
		(*linesw[tp->t_discp].l_rcvd)(c, tp);
	}
}

klmctl(dev, action)
{

	if(action == FSTATUS)
		return(CARRIER|CTS);
}

dlmctl(dev, action)
{
	struct tty *tp;
	register int sps, *addr;
	int rcsr, status;

	tp = &kl11[dev.d_minor];
	addr = tp->t_addr;
	status = 0;
	sps = PS->integ;
	spl5();
	switch(action&03) {

	case FSTATUS:
		rcsr = addr->klrcsr;
		status = (rcsr>>10)&(CARRIER|SR)|(rcsr>>12)&CTS;
		break;

	case DISABLE:
		addr->klrcsr =& ~(ST|RQS|DTR|MENABLE);
		break;

	case HUP:
		addr->klrcsr =& ~DTR;
		break;

	case TURNON:
	/*
	 * Enable modem and set data terminal ready. Set primary and
	 * secondary carrier according to bits 2 and 3 of "action"
	 */
		action =& (RQS|ST);
		addr->klrcsr = addr->klrcsr & ~(RQS|ST) | (action|DTR|MENABLE);
	}

	PS->integ = sps;
	return(status);
}
