xref: /csrg-svn/sys/hp300/dev/dcm.c (revision 42946)
141480Smckusick /*
241480Smckusick  * Copyright (c) 1988 University of Utah.
341480Smckusick  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
441480Smckusick  * All rights reserved.
541480Smckusick  *
641480Smckusick  * This code is derived from software contributed to Berkeley by
741480Smckusick  * the Systems Programming Group of the University of Utah Computer
841480Smckusick  * Science Department.
941480Smckusick  *
1041480Smckusick  * %sccs.include.redist.c%
1141480Smckusick  *
1241480Smckusick  * from: $Hdr: dcm.c 1.17 89/10/01$
1341480Smckusick  *
14*42946Smarc  *	@(#)dcm.c	7.3 (Berkeley) 06/06/90
1541480Smckusick  */
1641480Smckusick 
1741480Smckusick /*
1842354Smckusick  * TODO:
1942354Smckusick  *	Timeouts
2042354Smckusick  *	Test console/kgdb support.
2141480Smckusick  */
2241480Smckusick 
2341480Smckusick #include "dcm.h"
2441480Smckusick #if NDCM > 0
2541480Smckusick /*
2641480Smckusick  *  98642/MUX
2741480Smckusick  */
2841480Smckusick #include "param.h"
2941480Smckusick #include "systm.h"
3041480Smckusick #include "ioctl.h"
3141480Smckusick #include "tty.h"
3241480Smckusick #include "user.h"
3341480Smckusick #include "conf.h"
3441480Smckusick #include "file.h"
3541480Smckusick #include "uio.h"
3641480Smckusick #include "kernel.h"
3741480Smckusick #include "syslog.h"
3841480Smckusick #include "time.h"
3941480Smckusick 
4041480Smckusick #include "device.h"
4141480Smckusick #include "dcmreg.h"
4241480Smckusick #include "machine/cpu.h"
4341480Smckusick #include "machine/isr.h"
4441480Smckusick 
4542354Smckusick #ifndef DEFAULT_BAUD_RATE
4642354Smckusick #define DEFAULT_BAUD_RATE 9600
4742354Smckusick #endif
4842354Smckusick 
4942354Smckusick int	ttrstrt();
5042354Smckusick int	dcmprobe(), dcmstart(), dcmintr(), dcmparam();
5142354Smckusick 
5241480Smckusick struct	driver dcmdriver = {
5341480Smckusick 	dcmprobe, "dcm",
5441480Smckusick };
5541480Smckusick 
5641480Smckusick #define NDCMLINE (NDCM*4)
5741480Smckusick 
5842354Smckusick struct	tty dcm_tty[NDCMLINE];
5942354Smckusick int	ndcm = NDCMLINE;
6042354Smckusick 
6142354Smckusick int	dcm_active;
6241480Smckusick int	dcmsoftCAR[NDCM];
6341480Smckusick struct	dcmdevice *dcm_addr[NDCM];
6441480Smckusick struct	isr dcmisr[NDCM];
6541480Smckusick 
6641480Smckusick struct speedtab dcmspeedtab[] = {
6741480Smckusick 	0,	BR_0,
6841480Smckusick 	50,	BR_50,
6941480Smckusick 	75,	BR_75,
7041480Smckusick 	110,	BR_110,
7141480Smckusick 	134,	BR_134,
7241480Smckusick 	150,	BR_150,
7341480Smckusick 	300,	BR_300,
7441480Smckusick 	600,	BR_600,
7541480Smckusick 	1200,	BR_1200,
7641480Smckusick 	1800,	BR_1800,
7741480Smckusick 	2400,	BR_2400,
7841480Smckusick 	4800,	BR_4800,
7941480Smckusick 	9600,	BR_9600,
8041480Smckusick 	19200,	BR_19200,
8141480Smckusick 	38400,	BR_38400,
8241480Smckusick 	-1,	-1
8341480Smckusick };
8441480Smckusick 
8542354Smckusick /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */
8642354Smckusick #define	DCM_USPERCH(s)	(10000000 / (s))
8742354Smckusick 
8842354Smckusick /*
8942354Smckusick  * Per board interrupt scheme.  16.7ms is the polling interrupt rate
9042354Smckusick  * (16.7ms is about 550 buad, 38.4k is 72 chars in 16.7ms).
9142354Smckusick  */
9242354Smckusick #define DIS_TIMER	0
9342354Smckusick #define DIS_PERCHAR	1
9442354Smckusick #define DIS_RESET	2
9542354Smckusick 
9642354Smckusick int	dcmistype = -1;		/* -1 == dynamic, 0 == timer, 1 == perchar */
9742354Smckusick int     dcminterval = 5;	/* interval (secs) between checks */
9842354Smckusick struct	dcmischeme {
9942354Smckusick 	int	dis_perchar;	/* non-zero if interrupting per char */
10042354Smckusick 	long	dis_time;	/* last time examined */
10142354Smckusick 	int	dis_intr;	/* recv interrupts during last interval */
10242354Smckusick 	int	dis_char;	/* characters read during last interval */
10342354Smckusick } dcmischeme[NDCM];
10442354Smckusick 
10542354Smckusick /*
10642354Smckusick  * Console support
10742354Smckusick  */
10842354Smckusick int	dcmconsole = -1;
10942354Smckusick int	dcmdefaultrate = DEFAULT_BAUD_RATE;
11042354Smckusick int	dcmconbrdbusy = 0;
11142354Smckusick extern	struct tty *constty;
11242354Smckusick 
11342354Smckusick #ifdef KGDB
11442354Smckusick /*
11542354Smckusick  * Kernel GDB support
11642354Smckusick  */
11742354Smckusick extern int kgdb_dev;
11842354Smckusick extern int kgdb_rate;
11942354Smckusick extern int kgdb_debug_init;
12042354Smckusick #endif
12142354Smckusick 
12242354Smckusick /* #define IOSTATS */
12342354Smckusick 
12441480Smckusick #ifdef DEBUG
12541480Smckusick int	dcmdebug = 0x00;
12641480Smckusick #define DDB_SIOERR	0x01
12741480Smckusick #define DDB_PARAM	0x02
12841480Smckusick #define DDB_INPUT	0x04
12941480Smckusick #define DDB_OUTPUT	0x08
13041480Smckusick #define DDB_INTR	0x10
13142354Smckusick #define DDB_IOCTL	0x20
13242354Smckusick #define DDB_INTSCHM	0x40
13342354Smckusick #define DDB_MODEM	0x80
13441480Smckusick #define DDB_OPENCLOSE	0x100
13542354Smckusick #endif
13641480Smckusick 
13742354Smckusick #ifdef IOSTATS
13842354Smckusick #define	DCMRBSIZE	94
13942354Smckusick #define DCMXBSIZE	24
14042354Smckusick 
14142354Smckusick struct	dcmstats {
14242354Smckusick 	long	xints;		    /* # of xmit ints */
14342354Smckusick 	long	xchars;		    /* # of xmit chars */
14442354Smckusick 	long	xempty;		    /* times outq is empty in dcmstart */
14542354Smckusick 	long	xrestarts;	    /* times completed while xmitting */
14642354Smckusick 	long	rints;		    /* # of recv ints */
14742354Smckusick 	long	rchars;		    /* # of recv chars */
14842354Smckusick 	long	xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */
14942354Smckusick 	long	rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */
15042354Smckusick } dcmstats[NDCM];
15141480Smckusick #endif
15241480Smckusick 
15341480Smckusick #define UNIT(x)		minor(x)
15442354Smckusick #define	BOARD(x)	(((x) >> 2) & 0x3f)
15541480Smckusick #define PORT(x)		((x) & 3)
15641480Smckusick #define MKUNIT(b,p)	(((b) << 2) | (p))
15741480Smckusick 
15841480Smckusick dcmprobe(hd)
15941480Smckusick 	register struct hp_device *hd;
16041480Smckusick {
16141480Smckusick 	register struct dcmdevice *dcm;
16241480Smckusick 	register int i;
16341480Smckusick 	register int timo = 0;
16442354Smckusick 	int s, brd, isconsole;
16541480Smckusick 
16641480Smckusick 	dcm = (struct dcmdevice *)hd->hp_addr;
16741480Smckusick 	if ((dcm->dcm_rsid & 0x1f) != DCMID)
16841480Smckusick 		return (0);
16941480Smckusick 	brd = hd->hp_unit;
17042354Smckusick 	isconsole = (brd == BOARD(dcmconsole));
17142354Smckusick 	/*
17242354Smckusick 	 * XXX selected console device (CONSUNIT) as determined by
17342354Smckusick 	 * dcmcnprobe does not agree with logical numbering imposed
17442354Smckusick 	 * by the config file (i.e. lowest address DCM is not unit
17542354Smckusick 	 * CONSUNIT).  Don't recognize this card.
17642354Smckusick 	 */
17742354Smckusick 	if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)])
17842354Smckusick 		return(0);
17942354Smckusick 
18042354Smckusick 	/*
18142354Smckusick 	 * Empirically derived self-test magic
18242354Smckusick 	 */
18341480Smckusick 	s = spltty();
18441480Smckusick 	dcm->dcm_rsid = DCMRS;
18541480Smckusick 	DELAY(50000);	/* 5000 is not long enough */
18641480Smckusick 	dcm->dcm_rsid = 0;
18741480Smckusick 	dcm->dcm_ic = IC_IE;
18841480Smckusick 	dcm->dcm_cr = CR_SELFT;
18942354Smckusick 	while ((dcm->dcm_ic & IC_IR) == 0)
19042354Smckusick 		if (++timo == 20000)
19141480Smckusick 			return(0);
19241480Smckusick 	DELAY(50000)	/* XXX why is this needed ???? */
19342354Smckusick 	while ((dcm->dcm_iir & IIR_SELFT) == 0)
19442354Smckusick 		if (++timo == 400000)
19541480Smckusick 			return(0);
19641480Smckusick 	DELAY(50000)	/* XXX why is this needed ???? */
19741480Smckusick 	if (dcm->dcm_stcon != ST_OK) {
19842354Smckusick 		if (!isconsole)
19942354Smckusick 			printf("dcm%d: self test failed: %x\n",
20042354Smckusick 			       brd, dcm->dcm_stcon);
20141480Smckusick 		return(0);
20241480Smckusick 	}
20341480Smckusick 	dcm->dcm_ic = IC_ID;
20441480Smckusick 	splx(s);
20541480Smckusick 
20641480Smckusick 	hd->hp_ipl = DCMIPL(dcm->dcm_ic);
20742354Smckusick 	dcm_addr[brd] = dcm;
20842354Smckusick 	dcm_active |= 1 << brd;
20942354Smckusick 	dcmsoftCAR[brd] = hd->hp_flags;
21041480Smckusick 	dcmisr[brd].isr_ipl = hd->hp_ipl;
21141480Smckusick 	dcmisr[brd].isr_arg = brd;
21241480Smckusick 	dcmisr[brd].isr_intr = dcmintr;
21341480Smckusick 	isrlink(&dcmisr[brd]);
21442354Smckusick #ifdef KGDB
21542354Smckusick 	if (major(kgdb_dev) == 2 && BOARD(kgdb_dev) == brd) {
21642354Smckusick 		if (dcmconsole == UNIT(kgdb_dev))
21742354Smckusick 			kgdb_dev = -1;	/* can't debug over console port */
21842354Smckusick 		else {
21942354Smckusick 			(void) dcminit(kgdb_dev, kgdb_rate);
22042354Smckusick 			if (kgdb_debug_init) {
22142354Smckusick 				printf("dcm%d: kgdb waiting...",
22242354Smckusick 				       UNIT(kgdb_dev));
22342354Smckusick 				/* trap into kgdb */
22442354Smckusick 				asm("trap #15;");
22542354Smckusick 				printf("connected.\n");
22642354Smckusick 			} else
22742354Smckusick 				printf("dcm%d: kgdb enabled\n",
22842354Smckusick 				       UNIT(kgdb_dev));
22942354Smckusick 		}
23042354Smckusick 	}
23142354Smckusick #endif
23242354Smckusick 	if (dcmistype == DIS_TIMER)
23342354Smckusick 		dcmsetischeme(brd, DIS_RESET|DIS_TIMER);
23442354Smckusick 	else
23542354Smckusick 		dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR);
23642354Smckusick 	dcm->dcm_mdmmsk = MI_CD;	/* enable modem carrier detect intr */
23742354Smckusick 	dcm->dcm_ic = IC_IE;		/* turn all interrupts on */
23841480Smckusick 	/*
23941480Smckusick 	 * Need to reset baud rate, etc. of next print so reset dcmconsole.
24041480Smckusick 	 * Also make sure console is always "hardwired"
24141480Smckusick 	 */
24242354Smckusick 	if (isconsole) {
24342354Smckusick 		dcmconsole = -1;
24441480Smckusick 		dcmsoftCAR[brd] |= (1 << PORT(dcmconsole));
24541480Smckusick 	}
24641480Smckusick 	return (1);
24741480Smckusick }
24841480Smckusick 
24941480Smckusick dcmopen(dev, flag)
25041480Smckusick 	dev_t dev;
25141480Smckusick {
25241480Smckusick 	register struct tty *tp;
25341480Smckusick 	register int unit, brd;
25442354Smckusick 	int error;
25541480Smckusick 
25641480Smckusick 	unit = UNIT(dev);
25741480Smckusick 	brd = BOARD(unit);
25842354Smckusick 	if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0)
25941480Smckusick 		return (ENXIO);
26042354Smckusick #ifdef KGDB
26142354Smckusick 	if (unit == UNIT(kgdb_dev))
26242354Smckusick 		return (EBUSY);
26342354Smckusick #endif
26441480Smckusick 	tp = &dcm_tty[unit];
26541480Smckusick 	tp->t_oproc = dcmstart;
26642354Smckusick 	tp->t_param = dcmparam;
26741480Smckusick 	tp->t_dev = dev;
26841480Smckusick 	if ((tp->t_state & TS_ISOPEN) == 0) {
26941480Smckusick 		ttychars(tp);
27041480Smckusick 		tp->t_iflag = TTYDEF_IFLAG;
27141480Smckusick 		tp->t_oflag = TTYDEF_OFLAG;
27241480Smckusick 		tp->t_cflag = TTYDEF_CFLAG;
27341480Smckusick 		tp->t_lflag = TTYDEF_LFLAG;
27441480Smckusick 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
27542354Smckusick 		(void) dcmparam(tp, &tp->t_termios);
27641480Smckusick 		ttsetwater(tp);
27741480Smckusick 	} else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
27841480Smckusick 		return (EBUSY);
27941480Smckusick 	if (PORT(unit) == 0)	/* enable port 0 */
28041480Smckusick 		(void) dcmmctl(dev, MO_ON, DMSET);
28141480Smckusick 	if (dcmsoftCAR[brd] & (1 << PORT(unit)))
28241480Smckusick 		tp->t_state |= TS_CARR_ON;
28341480Smckusick 	else if (PORT(unit))		/* Only port 0 has modem control */
28441480Smckusick 		tp->t_state |= TS_CARR_ON;
28541480Smckusick 	else if (dcmmctl(dev, MO_OFF, DMGET) & MI_CD)
28641480Smckusick 		tp->t_state |= TS_CARR_ON;
28741480Smckusick 	(void) spltty();
28841480Smckusick 	while (!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) &&
28941480Smckusick 	       (tp->t_state & TS_CARR_ON) == 0) {
29041480Smckusick 		tp->t_state |= TS_WOPEN;
291*42946Smarc 		if ((error = tsleep((caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
292*42946Smarc 				   ttopen, 0)) ||
293*42946Smarc 		    (error = ttclosed(tp))) {
29442354Smckusick 			tp->t_state &= ~TS_WOPEN;
29542354Smckusick 			(void) spl0();
29642354Smckusick 			return (error);
29742354Smckusick 		}
29841480Smckusick 	}
29941480Smckusick 	(void) spl0();
30041480Smckusick 
30141480Smckusick #ifdef DEBUG
30241480Smckusick 	if (dcmdebug & DDB_OPENCLOSE)
30341480Smckusick 		printf("dcmopen: u %x st %x fl %x\n",
30441480Smckusick 			unit, tp->t_state, tp->t_flags);
30541480Smckusick #endif
30641480Smckusick 	return ((*linesw[tp->t_line].l_open)(dev, tp));
30741480Smckusick }
30841480Smckusick 
30941480Smckusick /*ARGSUSED*/
31041480Smckusick dcmclose(dev, flag)
31141480Smckusick 	dev_t dev;
31241480Smckusick {
31341480Smckusick 	register struct tty *tp;
31441480Smckusick 	int unit;
31541480Smckusick 
31641480Smckusick 	unit = UNIT(dev);
31741480Smckusick 	tp = &dcm_tty[unit];
31841480Smckusick 	(*linesw[tp->t_line].l_close)(tp);
31941480Smckusick 	if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
32041480Smckusick 	    (tp->t_state&TS_ISOPEN) == 0)
32141480Smckusick 		(void) dcmmctl(dev, MO_OFF, DMSET);
32241480Smckusick #ifdef DEBUG
32341480Smckusick 	if (dcmdebug & DDB_OPENCLOSE)
32441480Smckusick 		printf("dcmclose: u %x st %x fl %x\n",
32541480Smckusick 			unit, tp->t_state, tp->t_flags);
32641480Smckusick #endif
32741480Smckusick 	ttyclose(tp);
32841480Smckusick 	return(0);
32941480Smckusick }
33041480Smckusick 
33141480Smckusick dcmread(dev, uio, flag)
33241480Smckusick 	dev_t dev;
33341480Smckusick 	struct uio *uio;
33441480Smckusick {
33541480Smckusick 	register struct tty *tp;
33641480Smckusick 
33741480Smckusick 	tp = &dcm_tty[UNIT(dev)];
33841480Smckusick 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
33941480Smckusick }
34041480Smckusick 
34141480Smckusick dcmwrite(dev, uio, flag)
34241480Smckusick 	dev_t dev;
34341480Smckusick 	struct uio *uio;
34441480Smckusick {
34541480Smckusick 	int unit = UNIT(dev);
34641480Smckusick 	register struct tty *tp;
34741480Smckusick 
34841480Smckusick 	tp = &dcm_tty[unit];
34942354Smckusick 	/*
35042354Smckusick 	 * XXX we disallow virtual consoles if the physical console is
35142354Smckusick 	 * a serial port.  This is in case there is a display attached that
35242354Smckusick 	 * is not the console.  In that situation we don't need/want the X
35342354Smckusick 	 * server taking over the console.
35442354Smckusick 	 */
35542354Smckusick 	if (constty && unit == dcmconsole)
35642354Smckusick 		constty = NULL;
35741480Smckusick 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
35841480Smckusick }
35941480Smckusick 
36041480Smckusick dcmintr(brd)
36141480Smckusick 	register int brd;
36241480Smckusick {
36342354Smckusick 	register struct dcmdevice *dcm = dcm_addr[brd];
36442354Smckusick 	register struct dcmischeme *dis;
36541480Smckusick 	int i, code, pcnd[4], mcnd, delta;
36641480Smckusick 
36742354Smckusick 	/*
36842354Smckusick 	 * Do all guarded register accesses right off to minimize
36942354Smckusick 	 * block out of hardware.
37042354Smckusick 	 */
37141480Smckusick 	SEM_LOCK(dcm);
37241480Smckusick 	if ((dcm->dcm_ic & IC_IR) == 0) {
37341480Smckusick 		SEM_UNLOCK(dcm);
37441480Smckusick 		return(0);
37541480Smckusick 	}
37641480Smckusick 	for (i = 0; i < 4; i++) {
37741480Smckusick 		pcnd[i] = dcm->dcm_icrtab[i].dcm_data;
37841480Smckusick 		dcm->dcm_icrtab[i].dcm_data = 0;
37941480Smckusick 	}
38041480Smckusick 	mcnd = dcm->dcm_mdmin;
38141480Smckusick 	code = dcm->dcm_iir & IIR_MASK;
38242354Smckusick 	dcm->dcm_iir = 0;	/* XXX doc claims read clears interrupt?! */
38341480Smckusick 	SEM_UNLOCK(dcm);
38441480Smckusick 
38541480Smckusick #ifdef DEBUG
38641480Smckusick 	if (dcmdebug & DDB_INTR)
38742354Smckusick 		printf("dcmintr(%d): iir %x p0 %x p1 %x p2 %x p3 %x m %x\n",
38842354Smckusick 		       brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3], mcnd);
38941480Smckusick #endif
39042354Smckusick 	if (code & IIR_TIMEO)
39142354Smckusick 		dcmrint(brd, dcm);
39241480Smckusick 	if (code & IIR_PORT0)
39341480Smckusick 		dcmpint(MKUNIT(brd, 0), pcnd[0], dcm);
39441480Smckusick 	if (code & IIR_PORT1)
39541480Smckusick 		dcmpint(MKUNIT(brd, 1), pcnd[1],  dcm);
39641480Smckusick 	if (code & IIR_PORT2)
39741480Smckusick 		dcmpint(MKUNIT(brd, 2), pcnd[2], dcm);
39841480Smckusick 	if (code & IIR_PORT3)
39941480Smckusick 		dcmpint(MKUNIT(brd, 3), pcnd[3], dcm);
40041480Smckusick 	if (code & IIR_MODM)
40141480Smckusick 		dcmmint(MKUNIT(brd, 0), mcnd, dcm);	/* always port 0 */
40241480Smckusick 
40342354Smckusick 	dis = &dcmischeme[brd];
40441480Smckusick 	/*
40542354Smckusick 	 * Chalk up a receiver interrupt if the timer running or one of
40642354Smckusick 	 * the ports reports a special character interrupt.
40741480Smckusick 	 */
40842354Smckusick 	if ((code & IIR_TIMEO) ||
40942354Smckusick 	    ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC))
41042354Smckusick 		dis->dis_intr++;
41142354Smckusick 	/*
41242354Smckusick 	 * See if it is time to check/change the interrupt rate.
41342354Smckusick 	 */
41442354Smckusick 	if (dcmistype < 0 &&
41542354Smckusick 	    (delta = time.tv_sec - dis->dis_time) >= dcminterval) {
41641480Smckusick 		/*
41742354Smckusick 		 * If currently per-character and averaged over 70 interrupts
41842354Smckusick 		 * per-second (66 is threshold of 600 baud) in last interval,
41942354Smckusick 		 * switch to timer mode.
42042354Smckusick 		 *
42142354Smckusick 		 * XXX decay counts ala load average to avoid spikes?
42241480Smckusick 		 */
42342354Smckusick 		if (dis->dis_perchar && dis->dis_intr > 70 * delta)
42442354Smckusick 			dcmsetischeme(brd, DIS_TIMER);
42542354Smckusick 		/*
42642354Smckusick 		 * If currently using timer and had more interrupts than
42742354Smckusick 		 * received characters in the last interval, switch back
42842354Smckusick 		 * to per-character.  Note that after changing to per-char
42942354Smckusick 		 * we must process any characters already in the queue
43042354Smckusick 		 * since they may have arrived before the bitmap was setup.
43142354Smckusick 		 *
43242354Smckusick 		 * XXX decay counts?
43342354Smckusick 		 */
43442354Smckusick 		else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) {
43542354Smckusick 			dcmsetischeme(brd, DIS_PERCHAR);
43641480Smckusick 			dcmrint(brd, dcm);
43741480Smckusick 		}
43842354Smckusick 		dis->dis_intr = dis->dis_char = 0;
43942354Smckusick 		dis->dis_time = time.tv_sec;
44042354Smckusick 	}
44141480Smckusick 	return(1);
44241480Smckusick }
44341480Smckusick 
44441480Smckusick /*
44541480Smckusick  *  Port interrupt.  Can be two things:
44641480Smckusick  *	First, it might be a special character (exception interrupt);
44741480Smckusick  *	Second, it may be a buffer empty (transmit interrupt);
44841480Smckusick  */
44941480Smckusick dcmpint(unit, code, dcm)
45041480Smckusick 	int unit, code;
45142354Smckusick 	struct dcmdevice *dcm;
45241480Smckusick {
45342354Smckusick 	struct tty *tp = &dcm_tty[unit];
45441480Smckusick 
45542354Smckusick 	if (code & IT_SPEC)
45642354Smckusick 		dcmreadbuf(unit, dcm, tp);
45741480Smckusick 	if (code & IT_TX)
45842354Smckusick 		dcmxint(unit, dcm, tp);
45941480Smckusick }
46041480Smckusick 
46141480Smckusick dcmrint(brd, dcm)
46241480Smckusick 	int brd;
46341480Smckusick 	register struct dcmdevice *dcm;
46441480Smckusick {
46542354Smckusick 	register int i, unit;
46641480Smckusick 	register struct tty *tp;
46741480Smckusick 
46841480Smckusick 	unit = MKUNIT(brd, 0);
46941480Smckusick 	tp = &dcm_tty[unit];
47042354Smckusick 	for (i = 0; i < 4; i++, tp++, unit++)
47142354Smckusick 		dcmreadbuf(unit, dcm, tp);
47241480Smckusick }
47341480Smckusick 
47442354Smckusick dcmreadbuf(unit, dcm, tp)
47541480Smckusick 	int unit;
47641480Smckusick 	register struct dcmdevice *dcm;
47741480Smckusick 	register struct tty *tp;
47841480Smckusick {
47942354Smckusick 	int port = PORT(unit);
48042354Smckusick 	register struct dcmpreg *pp = dcm_preg(dcm, port);
48142354Smckusick 	register struct dcmrfifo *fifo;
48241480Smckusick 	register int c, stat;
48341480Smckusick 	register unsigned head;
48442354Smckusick 	int nch = 0;
48542354Smckusick #ifdef IOSTATS
48642354Smckusick 	struct dcmstats *dsp = &dcmstats[BOARD(unit)];
48741480Smckusick 
48842354Smckusick 	dsp->rints++;
48942354Smckusick #endif
49042354Smckusick 	/*
49142354Smckusick 	 * TS_WOPEN catches a race when switching to polling mode from dcmrint
49242354Smckusick 	 */
49342354Smckusick 	if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) {
49442354Smckusick #ifdef KGDB
49542354Smckusick 		if (unit == UNIT(kgdb_dev) &&
49642354Smckusick 		    (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) &&
49742354Smckusick 		    dcm->dcm_rfifos[3-port][head>>1].data_char == '!') {
49842354Smckusick 			pp->r_head = (head + 2) & RX_MASK;
49942354Smckusick 			printf("kgdb trap from dcm%d\n", unit);
50042354Smckusick 			/* trap into kgdb */
50142354Smckusick 			asm("trap #15;");
50242354Smckusick 			return;
50342354Smckusick 		}
50442354Smckusick #endif
50542354Smckusick 		pp->r_head = pp->r_tail & RX_MASK;
50642354Smckusick 		return;
50742354Smckusick 	}
50841480Smckusick 
50942354Smckusick 	head = pp->r_head & RX_MASK;
51042354Smckusick 	fifo = &dcm->dcm_rfifos[3-port][head>>1];
51142354Smckusick 	/*
51242354Smckusick 	 * XXX upper bound on how many chars we will take in one swallow?
51342354Smckusick 	 */
51442354Smckusick 	while (head != (pp->r_tail & RX_MASK)) {
51542354Smckusick 		/*
51642354Smckusick 		 * Get character/status and update head pointer as fast
51742354Smckusick 		 * as possible to make room for more characters.
51842354Smckusick 		 */
51942354Smckusick 		c = fifo->data_char;
52042354Smckusick 		stat = fifo->data_stat;
52141480Smckusick 		head = (head + 2) & RX_MASK;
52242354Smckusick 		pp->r_head = head;
52342354Smckusick 		fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0];
52442354Smckusick 		nch++;
52541480Smckusick 
52641480Smckusick #ifdef DEBUG
52741480Smckusick 		if (dcmdebug & DDB_INPUT)
52842354Smckusick 			printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n",
52942354Smckusick 			       unit, c&0xFF, c, stat&0xFF,
53042354Smckusick 			       tp->t_flags, head, pp->r_tail);
53141480Smckusick #endif
53242354Smckusick 		/*
53342354Smckusick 		 * Check for and handle errors
53442354Smckusick 		 */
53542354Smckusick 		if (stat & RD_MASK) {
53641480Smckusick #ifdef DEBUG
53742354Smckusick 			if (dcmdebug & (DDB_INPUT|DDB_SIOERR))
53842354Smckusick 				printf("dcmreadbuf(%d): err: c%x('%c') s%x\n",
53942354Smckusick 				       unit, stat, c&0xFF, c);
54041480Smckusick #endif
54141480Smckusick 			if (stat & (RD_BD | RD_FE))
54241480Smckusick 				c |= TTY_FE;
54341480Smckusick 			else if (stat & RD_PE)
54441480Smckusick 				c |= TTY_PE;
54541480Smckusick 			else if (stat & RD_OVF)
54641480Smckusick 				log(LOG_WARNING,
54742354Smckusick 				    "dcm%d: silo overflow\n", unit);
54841480Smckusick 			else if (stat & RD_OE)
54941480Smckusick 				log(LOG_WARNING,
55042354Smckusick 				    "dcm%d: uart overflow\n", unit);
55141480Smckusick 		}
55241480Smckusick 		(*linesw[tp->t_line].l_rint)(c, tp);
55341480Smckusick 	}
55442354Smckusick 	dcmischeme[BOARD(unit)].dis_char += nch;
55542354Smckusick #ifdef IOSTATS
55642354Smckusick 	dsp->rchars += nch;
55742354Smckusick 	if (nch <= DCMRBSIZE)
55842354Smckusick 		dsp->rsilo[nch]++;
55941480Smckusick 	else
56042354Smckusick 		dsp->rsilo[DCMRBSIZE+1]++;
56141480Smckusick #endif
56241480Smckusick }
56341480Smckusick 
56442354Smckusick dcmxint(unit, dcm, tp)
56541480Smckusick 	int unit;
56641480Smckusick 	struct dcmdevice *dcm;
56742354Smckusick 	register struct tty *tp;
56841480Smckusick {
56941480Smckusick 	tp->t_state &= ~TS_BUSY;
57041480Smckusick 	if (tp->t_state & TS_FLUSH)
57141480Smckusick 		tp->t_state &= ~TS_FLUSH;
57241480Smckusick 	if (tp->t_line)
57341480Smckusick 		(*linesw[tp->t_line].l_start)(tp);
57441480Smckusick 	else
57541480Smckusick 		dcmstart(tp);
57641480Smckusick }
57741480Smckusick 
57841480Smckusick dcmmint(unit, mcnd, dcm)
57941480Smckusick 	register int unit;
58041480Smckusick 	register struct dcmdevice *dcm;
58141480Smckusick         int mcnd;
58241480Smckusick {
58341480Smckusick 	register struct tty *tp;
58441480Smckusick 
58541480Smckusick #ifdef DEBUG
58642354Smckusick 	if (dcmdebug & DDB_MODEM)
58741480Smckusick 		printf("dcmmint: unit %x mcnd %x\n", unit, mcnd);
58842354Smckusick #endif
58941480Smckusick 	tp = &dcm_tty[unit];
59041480Smckusick 	if ((dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0) {
59141480Smckusick 		if (mcnd & MI_CD)
59241480Smckusick 			(void) (*linesw[tp->t_line].l_modem)(tp, 1);
59341480Smckusick 		else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
59441480Smckusick 			dcm->dcm_mdmout &= ~(MO_DTR | MO_RTS);
59541480Smckusick 			SEM_LOCK(dcm);
59641480Smckusick 			dcm->dcm_cr |= CR_MODM;
59741480Smckusick 			SEM_UNLOCK(dcm);
59842354Smckusick 			DELAY(10); /* time to change lines */
59941480Smckusick 		}
60041480Smckusick 	}
60141480Smckusick }
60241480Smckusick 
60341480Smckusick dcmioctl(dev, cmd, data, flag)
60441480Smckusick 	dev_t dev;
60541480Smckusick 	caddr_t data;
60641480Smckusick {
60741480Smckusick 	register struct tty *tp;
60841480Smckusick 	register int unit = UNIT(dev);
60941480Smckusick 	register struct dcmdevice *dcm;
61041480Smckusick 	register int port;
61142354Smckusick 	int error, s;
61241480Smckusick 
61341480Smckusick #ifdef DEBUG
61441480Smckusick 	if (dcmdebug & DDB_IOCTL)
61541480Smckusick 		printf("dcmioctl: unit %d cmd %x data %x flag %x\n",
61641480Smckusick 		       unit, cmd, *data, flag);
61741480Smckusick #endif
61841480Smckusick 	tp = &dcm_tty[unit];
61941480Smckusick 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
62041480Smckusick 	if (error >= 0)
62141480Smckusick 		return (error);
62241480Smckusick 	error = ttioctl(tp, cmd, data, flag);
62341480Smckusick 	if (error >= 0)
62441480Smckusick 		return (error);
62541480Smckusick 
62641480Smckusick 	port = PORT(unit);
62741480Smckusick 	dcm = dcm_addr[BOARD(unit)];
62841480Smckusick 	switch (cmd) {
62941480Smckusick 	case TIOCSBRK:
63042354Smckusick 		/*
63142354Smckusick 		 * Wait for transmitter buffer to empty
63242354Smckusick 		 */
63342354Smckusick 		s = spltty();
63442354Smckusick 		while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
63542354Smckusick 			DELAY(DCM_USPERCH(tp->t_ospeed));
63641480Smckusick 		SEM_LOCK(dcm);
63742354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_BRK;
63842354Smckusick 		dcm->dcm_cr |= (1 << port);	/* start break */
63941480Smckusick 		SEM_UNLOCK(dcm);
64042354Smckusick 		splx(s);
64141480Smckusick 		break;
64241480Smckusick 
64341480Smckusick 	case TIOCCBRK:
64441480Smckusick 		SEM_LOCK(dcm);
64542354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_BRK;
64642354Smckusick 		dcm->dcm_cr |= (1 << port);	/* end break */
64741480Smckusick 		SEM_UNLOCK(dcm);
64841480Smckusick 		break;
64941480Smckusick 
65041480Smckusick 	case TIOCSDTR:
65141480Smckusick 		(void) dcmmctl(dev, MO_ON, DMBIS);
65241480Smckusick 		break;
65341480Smckusick 
65441480Smckusick 	case TIOCCDTR:
65541480Smckusick 		(void) dcmmctl(dev, MO_ON, DMBIC);
65641480Smckusick 		break;
65741480Smckusick 
65841480Smckusick 	case TIOCMSET:
65941480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMSET);
66041480Smckusick 		break;
66141480Smckusick 
66241480Smckusick 	case TIOCMBIS:
66341480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMBIS);
66441480Smckusick 		break;
66541480Smckusick 
66641480Smckusick 	case TIOCMBIC:
66741480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMBIC);
66841480Smckusick 		break;
66941480Smckusick 
67041480Smckusick 	case TIOCMGET:
67141480Smckusick 		*(int *)data = dcmmctl(dev, 0, DMGET);
67241480Smckusick 		break;
67341480Smckusick 
67441480Smckusick 	default:
67541480Smckusick 		return (ENOTTY);
67641480Smckusick 	}
67741480Smckusick 	return (0);
67841480Smckusick }
67941480Smckusick 
68041480Smckusick dcmparam(tp, t)
68141480Smckusick 	register struct tty *tp;
68241480Smckusick 	register struct termios *t;
68341480Smckusick {
68441480Smckusick 	register struct dcmdevice *dcm;
68542354Smckusick 	register int port, mode, cflag = t->c_cflag;
68641480Smckusick 	int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab);
68742354Smckusick 
68841480Smckusick 	/* check requested parameters */
68941480Smckusick         if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
69041480Smckusick                 return(EINVAL);
69141480Smckusick         /* and copy to tty */
69241480Smckusick         tp->t_ispeed = t->c_ispeed;
69341480Smckusick         tp->t_ospeed = t->c_ospeed;
69441480Smckusick         tp->t_cflag = cflag;
69541480Smckusick 	if (ospeed == 0) {
69642354Smckusick 		(void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET);
69742354Smckusick 		return(0);
69841480Smckusick 	}
69942354Smckusick 
70042354Smckusick 	mode = 0;
70141480Smckusick 	switch (cflag&CSIZE) {
70241480Smckusick 	case CS5:
70341480Smckusick 		mode = LC_5BITS; break;
70441480Smckusick 	case CS6:
70541480Smckusick 		mode = LC_6BITS; break;
70641480Smckusick 	case CS7:
70741480Smckusick 		mode = LC_7BITS; break;
70841480Smckusick 	case CS8:
70941480Smckusick 		mode = LC_8BITS; break;
71041480Smckusick 	}
71141480Smckusick 	if (cflag&PARENB) {
71241480Smckusick 		if (cflag&PARODD)
71341480Smckusick 			mode |= LC_PODD;
71441480Smckusick 		else
71541480Smckusick 			mode |= LC_PEVEN;
71641480Smckusick 	}
71741480Smckusick 	if (cflag&CSTOPB)
71841480Smckusick 		mode |= LC_2STOP;
71941480Smckusick 	else
72041480Smckusick 		mode |= LC_1STOP;
72141480Smckusick #ifdef DEBUG
72241480Smckusick 	if (dcmdebug & DDB_PARAM)
72342354Smckusick 		printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n",
72442354Smckusick 		       UNIT(tp->t_dev), cflag, mode, tp->t_ospeed,
72542354Smckusick 		       DCM_USPERCH(tp->t_ospeed));
72641480Smckusick #endif
72742354Smckusick 
72842354Smckusick 	port = PORT(tp->t_dev);
72942354Smckusick 	dcm = dcm_addr[BOARD(tp->t_dev)];
73042354Smckusick 	/*
73142354Smckusick 	 * Wait for transmitter buffer to empty.
73242354Smckusick 	 */
73341480Smckusick 	while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
73442354Smckusick 		DELAY(DCM_USPERCH(tp->t_ospeed));
73542354Smckusick 	/*
73642354Smckusick 	 * Make changes known to hardware.
73742354Smckusick 	 */
73842354Smckusick 	dcm->dcm_data[port].dcm_baud = ospeed;
73941480Smckusick 	dcm->dcm_data[port].dcm_conf = mode;
74041480Smckusick 	SEM_LOCK(dcm);
74142354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_CON;
74242354Smckusick 	dcm->dcm_cr |= (1 << port);
74341480Smckusick 	SEM_UNLOCK(dcm);
74442354Smckusick 	/*
74542354Smckusick 	 * Delay for config change to take place. Weighted by buad.
74642354Smckusick 	 * XXX why do we do this?
74742354Smckusick 	 */
74842354Smckusick 	DELAY(16 * DCM_USPERCH(tp->t_ospeed));
74942354Smckusick 	return(0);
75041480Smckusick }
75141480Smckusick 
75241480Smckusick dcmstart(tp)
75341480Smckusick 	register struct tty *tp;
75441480Smckusick {
75541480Smckusick 	register struct dcmdevice *dcm;
75642354Smckusick 	register struct dcmpreg *pp;
75742354Smckusick 	register struct dcmtfifo *fifo;
75842354Smckusick 	register char *bp;
75942354Smckusick 	register unsigned tail, next;
76042354Smckusick 	register int port, nch;
76142354Smckusick 	unsigned head;
76242354Smckusick 	char buf[16];
76342354Smckusick 	int s;
76442354Smckusick #ifdef IOSTATS
76542354Smckusick 	struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)];
76642354Smckusick 	int tch = 0;
76742354Smckusick #endif
76842354Smckusick 
76941480Smckusick 	s = spltty();
77042354Smckusick #ifdef IOSTATS
77142354Smckusick 	dsp->xints++;
77242354Smckusick #endif
77341480Smckusick #ifdef DEBUG
77441480Smckusick 	if (dcmdebug & DDB_OUTPUT)
77542354Smckusick 		printf("dcmstart(%d): state %x flags %x outcc %d\n",
77642354Smckusick 		       UNIT(tp->t_dev), tp->t_state, tp->t_flags,
77742354Smckusick 		       tp->t_outq.c_cc);
77841480Smckusick #endif
77941480Smckusick 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
78041480Smckusick 		goto out;
78141480Smckusick 	if (tp->t_outq.c_cc <= tp->t_lowat) {
78241480Smckusick 		if (tp->t_state&TS_ASLEEP) {
78341480Smckusick 			tp->t_state &= ~TS_ASLEEP;
78441480Smckusick 			wakeup((caddr_t)&tp->t_outq);
78541480Smckusick 		}
78641480Smckusick 		if (tp->t_wsel) {
78741480Smckusick 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
78841480Smckusick 			tp->t_wsel = 0;
78941480Smckusick 			tp->t_state &= ~TS_WCOLL;
79041480Smckusick 		}
79141480Smckusick 	}
79242354Smckusick 	if (tp->t_outq.c_cc == 0) {
79342354Smckusick #ifdef IOSTATS
79442354Smckusick 		dsp->xempty++;
79542354Smckusick #endif
79642354Smckusick 		goto out;
79742354Smckusick 	}
79842354Smckusick 
79942354Smckusick 	dcm = dcm_addr[BOARD(tp->t_dev)];
80042354Smckusick 	port = PORT(tp->t_dev);
80142354Smckusick 	pp = dcm_preg(dcm, port);
80242354Smckusick 	tail = pp->t_tail & TX_MASK;
80342354Smckusick 	next = (tail + 1) & TX_MASK;
80442354Smckusick 	head = pp->t_head & TX_MASK;
80542354Smckusick 	if (head == next)
80642354Smckusick 		goto out;
80742354Smckusick 	fifo = &dcm->dcm_tfifos[3-port][tail];
80842354Smckusick again:
80942354Smckusick 	nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK);
81042354Smckusick #ifdef IOSTATS
81142354Smckusick 	tch += nch;
81242354Smckusick #endif
81341480Smckusick #ifdef DEBUG
81442354Smckusick 	if (dcmdebug & DDB_OUTPUT)
81542354Smckusick 		printf("\thead %x tail %x nch %d\n", head, tail, nch);
81641480Smckusick #endif
81742354Smckusick 	/*
81842354Smckusick 	 * Loop transmitting all the characters we can.
81942354Smckusick 	 */
82042354Smckusick 	for (bp = buf; --nch >= 0; bp++) {
82142354Smckusick 		fifo->data_char = *bp;
82242354Smckusick 		pp->t_tail = next;
82342354Smckusick 		/*
82442354Smckusick 		 * If this is the first character,
82542354Smckusick 		 * get the hardware moving right now.
82642354Smckusick 		 */
82742354Smckusick 		if (bp == buf) {
82842354Smckusick 			tp->t_state |= TS_BUSY;
82942354Smckusick 			SEM_LOCK(dcm);
83042354Smckusick 			dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
83142354Smckusick 			dcm->dcm_cr |= (1 << port);
83242354Smckusick 			SEM_UNLOCK(dcm);
83342354Smckusick 		}
83441480Smckusick 		tail = next;
83542354Smckusick 		fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0];
83642354Smckusick 		next = (next + 1) & TX_MASK;
83741480Smckusick 	}
83842354Smckusick 	/*
83942354Smckusick 	 * Head changed while we were loading the buffer,
84042354Smckusick 	 * go back and load some more if we can.
84142354Smckusick 	 */
84242354Smckusick 	if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) {
84342354Smckusick #ifdef IOSTATS
84442354Smckusick 		dsp->xrestarts++;
84542354Smckusick #endif
84642354Smckusick 		head = pp->t_head & TX_MASK;
84742354Smckusick 		goto again;
84842354Smckusick 	}
84942354Smckusick 	/*
85042354Smckusick 	 * Kick it one last time in case it finished while we were
85142354Smckusick 	 * loading the last time.
85242354Smckusick 	 */
85342354Smckusick 	if (bp > &buf[1]) {
85441480Smckusick 		tp->t_state |= TS_BUSY;
85541480Smckusick 		SEM_LOCK(dcm);
85642354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
85742354Smckusick 		dcm->dcm_cr |= (1 << port);
85842354Smckusick 		SEM_UNLOCK(dcm);
85942354Smckusick 	}
86041480Smckusick #ifdef DEBUG
86141480Smckusick 	if (dcmdebug & DDB_INTR)
86242354Smckusick 		printf("dcmstart(%d): head %x tail %x outqcc %d ch %d\n",
86342354Smckusick 		       UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc, tch);
86441480Smckusick #endif
86541480Smckusick out:
86642354Smckusick #ifdef IOSTATS
86742354Smckusick 	dsp->xchars += tch;
86842354Smckusick 	if (tch <= DCMXBSIZE)
86942354Smckusick 		dsp->xsilo[tch]++;
87042354Smckusick 	else
87142354Smckusick 		dsp->xsilo[DCMXBSIZE+1]++;
87242354Smckusick #endif
87341480Smckusick 	splx(s);
87441480Smckusick }
87541480Smckusick 
87641480Smckusick /*
87741480Smckusick  * Stop output on a line.
87841480Smckusick  */
87941480Smckusick dcmstop(tp, flag)
88041480Smckusick 	register struct tty *tp;
88141480Smckusick {
88241480Smckusick 	int s;
88341480Smckusick 
88441480Smckusick 	s = spltty();
88541480Smckusick 	if (tp->t_state & TS_BUSY) {
88642354Smckusick 		/* XXX is there some way to safely stop transmission? */
88741480Smckusick 		if ((tp->t_state&TS_TTSTOP)==0)
88841480Smckusick 			tp->t_state |= TS_FLUSH;
88941480Smckusick 	}
89041480Smckusick 	splx(s);
89141480Smckusick }
89241480Smckusick 
89341480Smckusick /* Modem control */
89441480Smckusick 
89541480Smckusick dcmmctl(dev, bits, how)
89641480Smckusick 	dev_t dev;
89741480Smckusick 	int bits, how;
89841480Smckusick {
89941480Smckusick 	register struct dcmdevice *dcm;
90041480Smckusick 	int s, hit = 0;
90141480Smckusick 
90242354Smckusick 	/*
90342354Smckusick 	 * Only port 0 has modem control lines.
90442354Smckusick 	 * XXX ok for now but needs to changed for the 8 port board.
90542354Smckusick 	 */
90641480Smckusick 	if (PORT(UNIT(dev)) != 0)
90741480Smckusick 		return(bits);
90841480Smckusick 
90941480Smckusick 	dcm = dcm_addr[BOARD(UNIT(dev))];
91041480Smckusick 	s = spltty();
91141480Smckusick 	switch (how) {
91241480Smckusick 
91341480Smckusick 	case DMSET:
91441480Smckusick 		dcm->dcm_mdmout = bits;
91541480Smckusick 		hit++;
91641480Smckusick 		break;
91741480Smckusick 
91841480Smckusick 	case DMBIS:
91941480Smckusick 		dcm->dcm_mdmout |= bits;
92041480Smckusick 		hit++;
92141480Smckusick 		break;
92241480Smckusick 
92341480Smckusick 	case DMBIC:
92441480Smckusick 		dcm->dcm_mdmout &= ~bits;
92541480Smckusick 		hit++;
92641480Smckusick 		break;
92741480Smckusick 
92841480Smckusick 	case DMGET:
92941480Smckusick 		bits = dcm->dcm_mdmin;
93041480Smckusick 		break;
93141480Smckusick 	}
93241480Smckusick 	if (hit) {
93341480Smckusick 		SEM_LOCK(dcm);
93441480Smckusick 		dcm->dcm_cr |= CR_MODM;
93541480Smckusick 		SEM_UNLOCK(dcm);
93642354Smckusick 		DELAY(10); /* delay until done */
93741480Smckusick 		(void) splx(s);
93841480Smckusick 	}
93941480Smckusick 	return(bits);
94041480Smckusick }
94141480Smckusick 
94242354Smckusick /*
94342354Smckusick  * Set board to either interrupt per-character or at a fixed interval.
94442354Smckusick  */
94542354Smckusick dcmsetischeme(brd, flags)
94642354Smckusick 	int brd, flags;
94741480Smckusick {
94842354Smckusick 	register struct dcmdevice *dcm = dcm_addr[brd];
94942354Smckusick 	register struct dcmischeme *dis = &dcmischeme[brd];
95041480Smckusick 	register int i;
95142354Smckusick 	u_char mask;
95242354Smckusick 	int perchar = flags & DIS_PERCHAR;
95341480Smckusick 
95441480Smckusick #ifdef DEBUG
95542354Smckusick 	if (dcmdebug & DDB_INTSCHM)
95642354Smckusick 		printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n",
95742354Smckusick 		       brd, perchar, dis->dis_perchar,
95842354Smckusick 		       dis->dis_intr, dis->dis_char);
95942354Smckusick 	if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) {
96042354Smckusick 		printf("dcmsetischeme(%d):  redundent request %d\n",
96142354Smckusick 		       brd, perchar);
96242354Smckusick 		return;
96342354Smckusick 	}
96442354Smckusick #endif
96542354Smckusick 	/*
96642354Smckusick 	 * If perchar is non-zero, we enable interrupts on all characters
96742354Smckusick 	 * otherwise we disable perchar interrupts and use periodic
96842354Smckusick 	 * polling interrupts.
96942354Smckusick 	 */
97042354Smckusick 	dis->dis_perchar = perchar;
97142354Smckusick 	mask = perchar ? 0xf : 0x0;
97242354Smckusick 	for (i = 0; i < 256; i++)
97342354Smckusick 		dcm->dcm_bmap[i].data_data = mask;
97442354Smckusick 	/*
97542354Smckusick 	 * Don't slow down tandem mode, interrupt on flow control
97642354Smckusick 	 * chars for any port on the board.
97742354Smckusick 	 */
97842354Smckusick 	if (!perchar) {
97942354Smckusick 		register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)];
98042354Smckusick 		int c;
98142354Smckusick 
98242354Smckusick 		for (i = 0; i < 4; i++, tp++) {
98342354Smckusick 			if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE)
98442354Smckusick 				dcm->dcm_bmap[c].data_data |= (1 << i);
98542354Smckusick 			if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE)
98642354Smckusick 				dcm->dcm_bmap[c].data_data |= (1 << i);
98741480Smckusick 		}
98841480Smckusick 	}
98942354Smckusick 	/*
99042354Smckusick 	 * Board starts with timer disabled so if first call is to
99142354Smckusick 	 * set perchar mode then we don't want to toggle the timer.
99242354Smckusick 	 */
99342354Smckusick 	if (flags == (DIS_RESET|DIS_PERCHAR))
99441480Smckusick 		return;
99542354Smckusick 	/*
99642354Smckusick 	 * Toggle card 16.7ms interrupts (we first make sure that card
99742354Smckusick 	 * has cleared the bit so it will see the toggle).
99842354Smckusick 	 */
99942354Smckusick 	while (dcm->dcm_cr & CR_TIMER)
100042354Smckusick 		;
100141480Smckusick 	SEM_LOCK(dcm);
100242354Smckusick 	dcm->dcm_cr |= CR_TIMER;
100341480Smckusick 	SEM_UNLOCK(dcm);
100441480Smckusick }
100541480Smckusick 
100641480Smckusick /*
100741480Smckusick  * Following are all routines needed for DCM to act as console
100841480Smckusick  */
100942354Smckusick #include "machine/cons.h"
101041480Smckusick 
101142354Smckusick dcmcnprobe(cp)
101242354Smckusick 	struct consdev *cp;
101341480Smckusick {
101442354Smckusick 	register struct hp_hw *hw;
101542354Smckusick 	int unit, i;
101642354Smckusick 	extern int dcmopen();
101741480Smckusick 
101842354Smckusick 	/*
101942354Smckusick 	 * Implicitly assigns the lowest select code DCM card found to be
102042354Smckusick 	 * logical unit 0 (actually CONUNIT).  If your config file does
102142354Smckusick 	 * anything different, you're screwed.
102242354Smckusick 	 */
102342354Smckusick 	for (hw = sc_table; hw->hw_type; hw++)
102442354Smckusick 	        if (hw->hw_type == COMMDCM && !badaddr((short *)hw->hw_addr))
102542354Smckusick 			break;
102642354Smckusick 	if (hw->hw_type != COMMDCM) {
102742354Smckusick 		cp->cn_pri = CN_DEAD;
102842354Smckusick 		return;
102942354Smckusick 	}
103042354Smckusick 	unit = CONUNIT;
103142354Smckusick 	dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_addr;
103241480Smckusick 
103342354Smckusick 	/* locate the major number */
103442354Smckusick 	for (i = 0; i < nchrdev; i++)
103542354Smckusick 		if (cdevsw[i].d_open == dcmopen)
103642354Smckusick 			break;
103742354Smckusick 
103842354Smckusick 	/* initialize required fields */
103942354Smckusick 	cp->cn_dev = makedev(i, unit);
104042354Smckusick 	cp->cn_tp = &dcm_tty[unit];
104142354Smckusick 	switch (dcm_addr[BOARD(unit)]->dcm_rsid) {
104242354Smckusick 	case DCMID:
104342354Smckusick 		cp->cn_pri = CN_NORMAL;
104442354Smckusick 		break;
104542354Smckusick 	case DCMID|DCMCON:
104642354Smckusick 		cp->cn_pri = CN_REMOTE;
104742354Smckusick 		break;
104842354Smckusick 	default:
104942354Smckusick 		cp->cn_pri = CN_DEAD;
105042354Smckusick 		break;
105142354Smckusick 	}
105242354Smckusick }
105342354Smckusick 
105442354Smckusick dcmcninit(cp)
105542354Smckusick 	struct consdev *cp;
105642354Smckusick {
105742354Smckusick 	dcminit(cp->cn_dev, dcmdefaultrate);
105842354Smckusick 	dcmconsole = UNIT(cp->cn_dev);
105942354Smckusick }
106042354Smckusick 
106142354Smckusick dcminit(dev, rate)
106242354Smckusick 	dev_t dev;
106342354Smckusick 	int rate;
106442354Smckusick {
106542354Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
106642354Smckusick 	int s, mode, port;
106742354Smckusick 
106842354Smckusick 	port = PORT(dev);
106942354Smckusick 	mode = LC_8BITS | LC_1STOP;
107041480Smckusick 	s = splhigh();
107142354Smckusick 	/*
107242354Smckusick 	 * Wait for transmitter buffer to empty.
107342354Smckusick 	 */
107442354Smckusick 	while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
107542354Smckusick 		DELAY(DCM_USPERCH(rate));
107642354Smckusick 	/*
107742354Smckusick 	 * Make changes known to hardware.
107842354Smckusick 	 */
107942354Smckusick 	dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab);
108042354Smckusick 	dcm->dcm_data[port].dcm_conf = mode;
108142354Smckusick 	SEM_LOCK(dcm);
108242354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_CON;
108342354Smckusick 	dcm->dcm_cr |= (1 << port);
108442354Smckusick 	SEM_UNLOCK(dcm);
108542354Smckusick 	/*
108642354Smckusick 	 * Delay for config change to take place. Weighted by buad.
108742354Smckusick 	 * XXX why do we do this?
108842354Smckusick 	 */
108942354Smckusick 	DELAY(16 * DCM_USPERCH(rate));
109041480Smckusick 	splx(s);
109141480Smckusick }
109241480Smckusick 
109341480Smckusick dcmcngetc(dev)
109442354Smckusick 	dev_t dev;
109541480Smckusick {
109642354Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
109742354Smckusick 	register struct dcmrfifo *fifo;
109842354Smckusick 	register struct dcmpreg *pp;
109942354Smckusick 	register unsigned head;
110042354Smckusick 	int s, c, stat, port;
110142354Smckusick 
110242354Smckusick 	port = PORT(dev);
110342354Smckusick 	pp = dcm_preg(dcm, port);
110442354Smckusick 	s = splhigh();
110542354Smckusick 	head = pp->r_head & RX_MASK;
110642354Smckusick 	fifo = &dcm->dcm_rfifos[3-port][head>>1];
110742354Smckusick 	while (head == (pp->r_tail & RX_MASK))
110842354Smckusick 		;
110942354Smckusick 	/*
111042354Smckusick 	 * If board interrupts are enabled, just let our received char
111142354Smckusick 	 * interrupt through in case some other port on the board was
111242354Smckusick 	 * busy.  Otherwise we must clear the interrupt.
111342354Smckusick 	 */
111442354Smckusick 	SEM_LOCK(dcm);
111542354Smckusick 	if ((dcm->dcm_ic & IC_IE) == 0)
111642354Smckusick 		stat = dcm->dcm_iir;
111742354Smckusick 	SEM_UNLOCK(dcm);
111842354Smckusick 	c = fifo->data_char;
111942354Smckusick 	stat = fifo->data_stat;
112042354Smckusick 	pp->r_head = (head + 2) & RX_MASK;
112142354Smckusick 	splx(s);
112242354Smckusick 	return(c);
112341480Smckusick }
112441480Smckusick 
112541480Smckusick /*
112641480Smckusick  * Console kernel output character routine.
112741480Smckusick  */
112841480Smckusick dcmcnputc(dev, c)
112941480Smckusick 	dev_t dev;
113042354Smckusick 	int c;
113141480Smckusick {
113241480Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
113342354Smckusick 	register struct dcmpreg *pp;
113442354Smckusick 	unsigned tail;
113542354Smckusick 	int s, port, stat;
113641480Smckusick 
113742354Smckusick 	port = PORT(dev);
113842354Smckusick 	pp = dcm_preg(dcm, port);
113942354Smckusick 	s = splhigh();
114042354Smckusick #ifdef KGDB
114142354Smckusick 	if (dev != kgdb_dev)
114242354Smckusick #endif
114342354Smckusick 	if (dcmconsole == -1) {
114442354Smckusick 		(void) dcminit(dev, dcmdefaultrate);
114542354Smckusick 		dcmconsole = UNIT(dev);
114642354Smckusick 	}
114742354Smckusick 	tail = pp->t_tail & TX_MASK;
114842354Smckusick 	while (tail != (pp->t_head & TX_MASK))
114942354Smckusick 		;
115042354Smckusick 	dcm->dcm_tfifos[3-port][tail].data_char = c;
115142354Smckusick 	pp->t_tail = tail = (tail + 1) & TX_MASK;
115241480Smckusick 	SEM_LOCK(dcm);
115342354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
115442354Smckusick 	dcm->dcm_cr |= (1 << port);
115541480Smckusick 	SEM_UNLOCK(dcm);
115642354Smckusick 	while (tail != (pp->t_head & TX_MASK))
115742354Smckusick 		;
115842354Smckusick 	/*
115942354Smckusick 	 * If board interrupts are enabled, just let our completion
116042354Smckusick 	 * interrupt through in case some other port on the board
116142354Smckusick 	 * was busy.  Otherwise we must clear the interrupt.
116242354Smckusick 	 */
116342354Smckusick 	if ((dcm->dcm_ic & IC_IE) == 0) {
116442354Smckusick 		SEM_LOCK(dcm);
116542354Smckusick 		stat = dcm->dcm_iir;
116642354Smckusick 		SEM_UNLOCK(dcm);
116742354Smckusick 	}
116841480Smckusick 	splx(s);
116941480Smckusick }
117041480Smckusick #endif
1171