xref: /csrg-svn/sys/hp300/dev/dcm.c (revision 50220)
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  *
1249301Shibler  * from: $Hdr: dcm.c 1.26 91/01/21$
1341480Smckusick  *
14*50220Skarels  *	@(#)dcm.c	7.14 (Berkeley) 06/27/91
1541480Smckusick  */
1641480Smckusick 
1741480Smckusick /*
1842354Smckusick  * TODO:
1942354Smckusick  *	Timeouts
2049130Skarels  *	Test console support.
2141480Smckusick  */
2241480Smckusick 
2341480Smckusick #include "dcm.h"
2441480Smckusick #if NDCM > 0
2541480Smckusick /*
2641480Smckusick  *  98642/MUX
2741480Smckusick  */
2845788Sbostic #include "sys/param.h"
2945788Sbostic #include "sys/systm.h"
3045788Sbostic #include "sys/ioctl.h"
3145788Sbostic #include "sys/tty.h"
3249130Skarels #include "sys/proc.h"
3345788Sbostic #include "sys/conf.h"
3445788Sbostic #include "sys/file.h"
3545788Sbostic #include "sys/uio.h"
3645788Sbostic #include "sys/kernel.h"
3745788Sbostic #include "sys/syslog.h"
3845788Sbostic #include "sys/time.h"
3941480Smckusick 
4041480Smckusick #include "device.h"
4141480Smckusick #include "dcmreg.h"
4249130Skarels #include "machine/cpu.h"
4345788Sbostic #include "../hp300/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];
5945750Smckusick struct	modemreg *dcm_modem[NDCMLINE];
6044317Shibler char	mcndlast[NDCMLINE];	/* XXX last modem status for line */
6142354Smckusick int	ndcm = NDCMLINE;
6242354Smckusick 
6342354Smckusick int	dcm_active;
6441480Smckusick int	dcmsoftCAR[NDCM];
6541480Smckusick struct	dcmdevice *dcm_addr[NDCM];
6641480Smckusick struct	isr dcmisr[NDCM];
6741480Smckusick 
6841480Smckusick struct speedtab dcmspeedtab[] = {
6941480Smckusick 	0,	BR_0,
7041480Smckusick 	50,	BR_50,
7141480Smckusick 	75,	BR_75,
7241480Smckusick 	110,	BR_110,
7341480Smckusick 	134,	BR_134,
7441480Smckusick 	150,	BR_150,
7541480Smckusick 	300,	BR_300,
7641480Smckusick 	600,	BR_600,
7741480Smckusick 	1200,	BR_1200,
7841480Smckusick 	1800,	BR_1800,
7941480Smckusick 	2400,	BR_2400,
8041480Smckusick 	4800,	BR_4800,
8141480Smckusick 	9600,	BR_9600,
8241480Smckusick 	19200,	BR_19200,
8341480Smckusick 	38400,	BR_38400,
8441480Smckusick 	-1,	-1
8541480Smckusick };
8641480Smckusick 
8742354Smckusick /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */
8842354Smckusick #define	DCM_USPERCH(s)	(10000000 / (s))
8942354Smckusick 
9042354Smckusick /*
9142354Smckusick  * Per board interrupt scheme.  16.7ms is the polling interrupt rate
92*50220Skarels  * (16.7ms is about 550 baud, 38.4k is 72 chars in 16.7ms).
9342354Smckusick  */
9442354Smckusick #define DIS_TIMER	0
9542354Smckusick #define DIS_PERCHAR	1
9642354Smckusick #define DIS_RESET	2
9742354Smckusick 
9842354Smckusick int	dcmistype = -1;		/* -1 == dynamic, 0 == timer, 1 == perchar */
9942354Smckusick int     dcminterval = 5;	/* interval (secs) between checks */
10042354Smckusick struct	dcmischeme {
10142354Smckusick 	int	dis_perchar;	/* non-zero if interrupting per char */
10242354Smckusick 	long	dis_time;	/* last time examined */
10342354Smckusick 	int	dis_intr;	/* recv interrupts during last interval */
10442354Smckusick 	int	dis_char;	/* characters read during last interval */
10542354Smckusick } dcmischeme[NDCM];
10642354Smckusick 
10742354Smckusick /*
10842354Smckusick  * Console support
10942354Smckusick  */
11049130Skarels #ifdef DCMCONSOLE
11149130Skarels int	dcmconsole = DCMCONSOLE;
11249130Skarels #else
11342354Smckusick int	dcmconsole = -1;
11449130Skarels #endif
11549130Skarels int	dcmconsinit;
11642354Smckusick int	dcmdefaultrate = DEFAULT_BAUD_RATE;
11742354Smckusick int	dcmconbrdbusy = 0;
11849130Skarels int	dcmmajor;
11942354Smckusick extern	struct tty *constty;
12042354Smckusick 
12142354Smckusick #ifdef KGDB
12242354Smckusick /*
12342354Smckusick  * Kernel GDB support
12442354Smckusick  */
12549130Skarels #include "machine/remote-sl.h"
12649130Skarels 
127*50220Skarels extern dev_t kgdb_dev;
12842354Smckusick extern int kgdb_rate;
12942354Smckusick extern int kgdb_debug_init;
13042354Smckusick #endif
13142354Smckusick 
13242354Smckusick /* #define IOSTATS */
13342354Smckusick 
13441480Smckusick #ifdef DEBUG
13545750Smckusick int	dcmdebug = 0x0;
13641480Smckusick #define DDB_SIOERR	0x01
13741480Smckusick #define DDB_PARAM	0x02
13841480Smckusick #define DDB_INPUT	0x04
13941480Smckusick #define DDB_OUTPUT	0x08
14041480Smckusick #define DDB_INTR	0x10
14142354Smckusick #define DDB_IOCTL	0x20
14242354Smckusick #define DDB_INTSCHM	0x40
14342354Smckusick #define DDB_MODEM	0x80
14441480Smckusick #define DDB_OPENCLOSE	0x100
14542354Smckusick #endif
14641480Smckusick 
14742354Smckusick #ifdef IOSTATS
14842354Smckusick #define	DCMRBSIZE	94
14942354Smckusick #define DCMXBSIZE	24
15042354Smckusick 
15142354Smckusick struct	dcmstats {
15242354Smckusick 	long	xints;		    /* # of xmit ints */
15342354Smckusick 	long	xchars;		    /* # of xmit chars */
15442354Smckusick 	long	xempty;		    /* times outq is empty in dcmstart */
15542354Smckusick 	long	xrestarts;	    /* times completed while xmitting */
15642354Smckusick 	long	rints;		    /* # of recv ints */
15742354Smckusick 	long	rchars;		    /* # of recv chars */
15842354Smckusick 	long	xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */
15942354Smckusick 	long	rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */
16042354Smckusick } dcmstats[NDCM];
16141480Smckusick #endif
16241480Smckusick 
16341480Smckusick #define UNIT(x)		minor(x)
16442354Smckusick #define	BOARD(x)	(((x) >> 2) & 0x3f)
16541480Smckusick #define PORT(x)		((x) & 3)
16641480Smckusick #define MKUNIT(b,p)	(((b) << 2) | (p))
16741480Smckusick 
168*50220Skarels /*
169*50220Skarels  * Conversion from "HP DCE" to almost-normal DCE: on the 638 8-port mux,
170*50220Skarels  * the distribution panel uses "HP DCE" conventions.  If requested via
171*50220Skarels  * the device flags, we swap the inputs to something closer to normal DCE,
172*50220Skarels  * allowing a straight-through cable to a DTE or a reversed cable
173*50220Skarels  * to a DCE (reversing 2-3, 4-5, 8-20 and leaving 6 unconnected;
174*50220Skarels  * this gets "DCD" on pin 20 and "CTS" on 4, but doesn't connect
175*50220Skarels  * DSR or make RTS work, though).  The following gives the full
176*50220Skarels  * details of a cable from this mux panel to a modem:
177*50220Skarels  *
178*50220Skarels  *		     HP		    modem
179*50220Skarels  *		name	pin	pin	name
180*50220Skarels  * HP inputs:
181*50220Skarels  *		"Rx"	 2	 3	Tx
182*50220Skarels  *		CTS	 4	 5	CTS	(only needed for CCTS_OFLOW)
183*50220Skarels  *		DCD	20	 8	DCD
184*50220Skarels  *		"DSR"	 9	 6	DSR	(unneeded)
185*50220Skarels  *		RI	22	22	RI	(unneeded)
186*50220Skarels  *
187*50220Skarels  * HP outputs:
188*50220Skarels  *		"Tx"	 3	 2	Rx
189*50220Skarels  *		"DTR"	 6	not connected
190*50220Skarels  *		"RTS"	 8	20	DTR
191*50220Skarels  *		"SR"	23	 4	RTS	(often not needed)
192*50220Skarels  */
193*50220Skarels #define	FLAG_STDDCE	0x10	/* map inputs if this bit is set in flags */
194*50220Skarels #define hp2dce_in(ibits)	(iconv[(ibits) & 0xf])
195*50220Skarels static char iconv[16] = {
196*50220Skarels 	0,		MI_DM,		MI_CTS,		MI_CTS|MI_DM,
197*50220Skarels 	MI_CD,		MI_CD|MI_DM,	MI_CD|MI_CTS,	MI_CD|MI_CTS|MI_DM,
198*50220Skarels 	MI_RI,		MI_RI|MI_DM,	MI_RI|MI_CTS,	MI_RI|MI_CTS|MI_DM,
199*50220Skarels 	MI_RI|MI_CD,	MI_RI|MI_CD|MI_DM, MI_RI|MI_CD|MI_CTS,
200*50220Skarels 	MI_RI|MI_CD|MI_CTS|MI_DM
201*50220Skarels };
202*50220Skarels 
20341480Smckusick dcmprobe(hd)
20441480Smckusick 	register struct hp_device *hd;
20541480Smckusick {
20641480Smckusick 	register struct dcmdevice *dcm;
20741480Smckusick 	register int i;
20841480Smckusick 	register int timo = 0;
209*50220Skarels 	int s, brd, isconsole, mbits;
21041480Smckusick 
21141480Smckusick 	dcm = (struct dcmdevice *)hd->hp_addr;
21241480Smckusick 	if ((dcm->dcm_rsid & 0x1f) != DCMID)
21341480Smckusick 		return (0);
21441480Smckusick 	brd = hd->hp_unit;
21542354Smckusick 	isconsole = (brd == BOARD(dcmconsole));
21642354Smckusick 	/*
21742354Smckusick 	 * XXX selected console device (CONSUNIT) as determined by
21842354Smckusick 	 * dcmcnprobe does not agree with logical numbering imposed
21942354Smckusick 	 * by the config file (i.e. lowest address DCM is not unit
22042354Smckusick 	 * CONSUNIT).  Don't recognize this card.
22142354Smckusick 	 */
22242354Smckusick 	if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)])
223*50220Skarels 		return (0);
22442354Smckusick 
22542354Smckusick 	/*
22642354Smckusick 	 * Empirically derived self-test magic
22742354Smckusick 	 */
22841480Smckusick 	s = spltty();
22941480Smckusick 	dcm->dcm_rsid = DCMRS;
23041480Smckusick 	DELAY(50000);	/* 5000 is not long enough */
23141480Smckusick 	dcm->dcm_rsid = 0;
23241480Smckusick 	dcm->dcm_ic = IC_IE;
23341480Smckusick 	dcm->dcm_cr = CR_SELFT;
23442354Smckusick 	while ((dcm->dcm_ic & IC_IR) == 0)
23542354Smckusick 		if (++timo == 20000)
236*50220Skarels 			return (0);
23741480Smckusick 	DELAY(50000)	/* XXX why is this needed ???? */
23842354Smckusick 	while ((dcm->dcm_iir & IIR_SELFT) == 0)
23942354Smckusick 		if (++timo == 400000)
240*50220Skarels 			return (0);
24141480Smckusick 	DELAY(50000)	/* XXX why is this needed ???? */
24241480Smckusick 	if (dcm->dcm_stcon != ST_OK) {
24342354Smckusick 		if (!isconsole)
24442354Smckusick 			printf("dcm%d: self test failed: %x\n",
24542354Smckusick 			       brd, dcm->dcm_stcon);
246*50220Skarels 		return (0);
24741480Smckusick 	}
24841480Smckusick 	dcm->dcm_ic = IC_ID;
24941480Smckusick 	splx(s);
25041480Smckusick 
25141480Smckusick 	hd->hp_ipl = DCMIPL(dcm->dcm_ic);
25242354Smckusick 	dcm_addr[brd] = dcm;
25342354Smckusick 	dcm_active |= 1 << brd;
25442354Smckusick 	dcmsoftCAR[brd] = hd->hp_flags;
25541480Smckusick 	dcmisr[brd].isr_ipl = hd->hp_ipl;
25641480Smckusick 	dcmisr[brd].isr_arg = brd;
25741480Smckusick 	dcmisr[brd].isr_intr = dcmintr;
25841480Smckusick 	isrlink(&dcmisr[brd]);
25942354Smckusick #ifdef KGDB
26049130Skarels 	if (major(kgdb_dev) == dcmmajor && BOARD(kgdb_dev) == brd) {
26142354Smckusick 		if (dcmconsole == UNIT(kgdb_dev))
262*50220Skarels 			kgdb_dev = NODEV; /* can't debug over console port */
26349301Shibler #ifndef KGDB_CHEAT
26449130Skarels 		/*
26549130Skarels 		 * The following could potentially be replaced
26649130Skarels 		 * by the corresponding code in dcmcnprobe.
26749130Skarels 		 */
26842354Smckusick 		else {
26942354Smckusick 			(void) dcminit(kgdb_dev, kgdb_rate);
27042354Smckusick 			if (kgdb_debug_init) {
27149130Skarels 				printf("dcm%d: ", UNIT(kgdb_dev));
27249130Skarels 				kgdb_connect(1);
27342354Smckusick 			} else
27449130Skarels 				printf("dcm%d: kgdb enabled\n", UNIT(kgdb_dev));
27542354Smckusick 		}
27649130Skarels 		/* end could be replaced */
27749301Shibler #endif
27842354Smckusick 	}
27942354Smckusick #endif
28042354Smckusick 	if (dcmistype == DIS_TIMER)
28142354Smckusick 		dcmsetischeme(brd, DIS_RESET|DIS_TIMER);
28242354Smckusick 	else
28342354Smckusick 		dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR);
28445750Smckusick 
28545750Smckusick 	/* load pointers to modem control */
28645750Smckusick 	dcm_modem[MKUNIT(brd, 0)] = &dcm->dcm_modem0;
28745750Smckusick 	dcm_modem[MKUNIT(brd, 1)] = &dcm->dcm_modem1;
28845750Smckusick 	dcm_modem[MKUNIT(brd, 2)] = &dcm->dcm_modem2;
28945750Smckusick 	dcm_modem[MKUNIT(brd, 3)] = &dcm->dcm_modem3;
29045750Smckusick 	/* set DCD (modem) and CTS (flow control) on all ports */
291*50220Skarels 	if (dcmsoftCAR[brd] & FLAG_STDDCE)
292*50220Skarels 		mbits = hp2dce_in(MI_CD|MI_CTS);
293*50220Skarels 	else
294*50220Skarels 		mbits = MI_CD|MI_CTS;
29545750Smckusick 	for (i = 0; i < 4; i++)
296*50220Skarels 		dcm_modem[MKUNIT(brd, i)]->mdmmsk = mbits;
29745750Smckusick 
29842354Smckusick 	dcm->dcm_ic = IC_IE;		/* turn all interrupts on */
29941480Smckusick 	/*
30041480Smckusick 	 * Need to reset baud rate, etc. of next print so reset dcmconsole.
30141480Smckusick 	 * Also make sure console is always "hardwired"
30241480Smckusick 	 */
30342354Smckusick 	if (isconsole) {
30449130Skarels 		dcmconsinit = 0;
30541480Smckusick 		dcmsoftCAR[brd] |= (1 << PORT(dcmconsole));
30641480Smckusick 	}
30741480Smckusick 	return (1);
30841480Smckusick }
30941480Smckusick 
31049130Skarels /* ARGSUSED */
31149130Skarels #ifdef __STDC__
31249130Skarels dcmopen(dev_t dev, int flag, int mode, struct proc *p)
31349130Skarels #else
31449130Skarels dcmopen(dev, flag, mode, p)
31541480Smckusick 	dev_t dev;
31649130Skarels 	int flag, mode;
31749130Skarels 	struct proc *p;
31849130Skarels #endif
31941480Smckusick {
32041480Smckusick 	register struct tty *tp;
32141480Smckusick 	register int unit, brd;
322*50220Skarels 	int error = 0, mbits;
32341480Smckusick 
32441480Smckusick 	unit = UNIT(dev);
32541480Smckusick 	brd = BOARD(unit);
32642354Smckusick 	if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0)
32741480Smckusick 		return (ENXIO);
32841480Smckusick 	tp = &dcm_tty[unit];
32941480Smckusick 	tp->t_oproc = dcmstart;
33042354Smckusick 	tp->t_param = dcmparam;
33141480Smckusick 	tp->t_dev = dev;
33241480Smckusick 	if ((tp->t_state & TS_ISOPEN) == 0) {
33342950Smarc 		tp->t_state |= TS_WOPEN;
33441480Smckusick 		ttychars(tp);
33549130Skarels 		if (tp->t_ispeed == 0) {
33649130Skarels 			tp->t_iflag = TTYDEF_IFLAG;
33749130Skarels 			tp->t_oflag = TTYDEF_OFLAG;
33849130Skarels 			tp->t_cflag = TTYDEF_CFLAG;
33949130Skarels 			tp->t_lflag = TTYDEF_LFLAG;
34049130Skarels 			tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
34149130Skarels 		}
34242354Smckusick 		(void) dcmparam(tp, &tp->t_termios);
34341480Smckusick 		ttsetwater(tp);
34449130Skarels 	} else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
34541480Smckusick 		return (EBUSY);
346*50220Skarels 	mbits = MO_ON;
347*50220Skarels 	if (dcmsoftCAR[brd] & FLAG_STDDCE)
348*50220Skarels 		mbits |= MO_SR;		/* pin 23, could be used as RTS */
349*50220Skarels 	(void) dcmmctl(dev, mbits, DMSET);	/* enable port */
35049130Skarels 	if ((dcmsoftCAR[brd] & (1 << PORT(unit))) ||
35149130Skarels 	    (dcmmctl(dev, MO_OFF, DMGET) & MI_CD))
35241480Smckusick 		tp->t_state |= TS_CARR_ON;
35345750Smckusick #ifdef DEBUG
35445750Smckusick 	if (dcmdebug & DDB_MODEM)
35545750Smckusick 		printf("dcm%d: dcmopen port %d softcarr %c\n",
35645750Smckusick 		       brd, unit, (tp->t_state & TS_CARR_ON) ? '1' : '0');
35745750Smckusick #endif
35841480Smckusick 	(void) spltty();
35944295Shibler 	while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
360*50220Skarels 	    (tp->t_state & TS_CARR_ON) == 0) {
36141480Smckusick 		tp->t_state |= TS_WOPEN;
36244295Shibler 		if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
36344295Shibler 		    ttopen, 0))
36444295Shibler 			break;
36541480Smckusick 	}
36641480Smckusick 	(void) spl0();
36745750Smckusick 
36841480Smckusick #ifdef DEBUG
36941480Smckusick 	if (dcmdebug & DDB_OPENCLOSE)
37041480Smckusick 		printf("dcmopen: u %x st %x fl %x\n",
37141480Smckusick 			unit, tp->t_state, tp->t_flags);
37241480Smckusick #endif
37344295Shibler 	if (error == 0)
37444295Shibler 		error = (*linesw[tp->t_line].l_open)(dev, tp);
37544295Shibler 	return (error);
37641480Smckusick }
37741480Smckusick 
37841480Smckusick /*ARGSUSED*/
37949750Smarc dcmclose(dev, flag, mode, p)
38041480Smckusick 	dev_t dev;
38149750Smarc 	int flag, mode;
38249750Smarc 	struct proc *p;
38341480Smckusick {
38441480Smckusick 	register struct tty *tp;
38541480Smckusick 	int unit;
38641480Smckusick 
38741480Smckusick 	unit = UNIT(dev);
38841480Smckusick 	tp = &dcm_tty[unit];
38949750Smarc 	(*linesw[tp->t_line].l_close)(tp, flag);
390*50220Skarels 	if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
391*50220Skarels 	    (tp->t_state&TS_ISOPEN) == 0)
392*50220Skarels 		(void) dcmmctl(dev, MO_OFF, DMSET);
39341480Smckusick #ifdef DEBUG
39441480Smckusick 	if (dcmdebug & DDB_OPENCLOSE)
39541480Smckusick 		printf("dcmclose: u %x st %x fl %x\n",
39641480Smckusick 			unit, tp->t_state, tp->t_flags);
39741480Smckusick #endif
39841480Smckusick 	ttyclose(tp);
399*50220Skarels 	return (0);
40041480Smckusick }
40141480Smckusick 
40241480Smckusick dcmread(dev, uio, flag)
40341480Smckusick 	dev_t dev;
40441480Smckusick 	struct uio *uio;
40541480Smckusick {
40641480Smckusick 	register struct tty *tp;
40741480Smckusick 
40841480Smckusick 	tp = &dcm_tty[UNIT(dev)];
40941480Smckusick 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
41041480Smckusick }
41141480Smckusick 
41241480Smckusick dcmwrite(dev, uio, flag)
41341480Smckusick 	dev_t dev;
41441480Smckusick 	struct uio *uio;
41541480Smckusick {
41641480Smckusick 	int unit = UNIT(dev);
41741480Smckusick 	register struct tty *tp;
41841480Smckusick 
41941480Smckusick 	tp = &dcm_tty[unit];
42042354Smckusick 	/*
42142354Smckusick 	 * XXX we disallow virtual consoles if the physical console is
42242354Smckusick 	 * a serial port.  This is in case there is a display attached that
42342354Smckusick 	 * is not the console.  In that situation we don't need/want the X
42442354Smckusick 	 * server taking over the console.
42542354Smckusick 	 */
42642354Smckusick 	if (constty && unit == dcmconsole)
42742354Smckusick 		constty = NULL;
42841480Smckusick 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
42941480Smckusick }
43041480Smckusick 
43141480Smckusick dcmintr(brd)
43241480Smckusick 	register int brd;
43341480Smckusick {
43442354Smckusick 	register struct dcmdevice *dcm = dcm_addr[brd];
43542354Smckusick 	register struct dcmischeme *dis;
43645750Smckusick 	register int unit = MKUNIT(brd, 0);
43745750Smckusick 	register int code, i;
43845750Smckusick 	int pcnd[4], mcode, mcnd[4];
43941480Smckusick 
44042354Smckusick 	/*
44142354Smckusick 	 * Do all guarded register accesses right off to minimize
44242354Smckusick 	 * block out of hardware.
44342354Smckusick 	 */
44441480Smckusick 	SEM_LOCK(dcm);
44541480Smckusick 	if ((dcm->dcm_ic & IC_IR) == 0) {
44641480Smckusick 		SEM_UNLOCK(dcm);
447*50220Skarels 		return (0);
44841480Smckusick 	}
44941480Smckusick 	for (i = 0; i < 4; i++) {
45041480Smckusick 		pcnd[i] = dcm->dcm_icrtab[i].dcm_data;
45141480Smckusick 		dcm->dcm_icrtab[i].dcm_data = 0;
452*50220Skarels 		code = dcm_modem[unit+i]->mdmin;
453*50220Skarels 		if (dcmsoftCAR[brd] & FLAG_STDDCE)
454*50220Skarels 			code = hp2dce_in(code);
455*50220Skarels 		mcnd[i] = code;
45641480Smckusick 	}
45741480Smckusick 	code = dcm->dcm_iir & IIR_MASK;
45842354Smckusick 	dcm->dcm_iir = 0;	/* XXX doc claims read clears interrupt?! */
45945750Smckusick 	mcode = dcm->dcm_modemintr;
46045750Smckusick 	dcm->dcm_modemintr = 0;
46141480Smckusick 	SEM_UNLOCK(dcm);
46241480Smckusick 
46341480Smckusick #ifdef DEBUG
46445750Smckusick 	if (dcmdebug & DDB_INTR) {
46545750Smckusick 		printf("dcmintr(%d): iir %x pc %x/%x/%x/%x ",
46645750Smckusick 		       brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3]);
46745750Smckusick 		printf("miir %x mc %x/%x/%x/%x\n",
46845750Smckusick 		       mcode, mcnd[0], mcnd[1], mcnd[2], mcnd[3]);
46945750Smckusick 	}
47041480Smckusick #endif
47142354Smckusick 	if (code & IIR_TIMEO)
47242354Smckusick 		dcmrint(brd, dcm);
47341480Smckusick 	if (code & IIR_PORT0)
47445750Smckusick 		dcmpint(unit+0, pcnd[0], dcm);
47541480Smckusick 	if (code & IIR_PORT1)
47645750Smckusick 		dcmpint(unit+1, pcnd[1], dcm);
47741480Smckusick 	if (code & IIR_PORT2)
47845750Smckusick 		dcmpint(unit+2, pcnd[2], dcm);
47941480Smckusick 	if (code & IIR_PORT3)
48045750Smckusick 		dcmpint(unit+3, pcnd[3], dcm);
48145750Smckusick 	if (code & IIR_MODM) {
48245750Smckusick 		if (mcode == 0 || mcode & 0x1)	/* mcode==0 -> 98642 board */
48345750Smckusick 			dcmmint(unit+0, mcnd[0], dcm);
48445750Smckusick 		if (mcode & 0x2)
48545750Smckusick 			dcmmint(unit+1, mcnd[1], dcm);
48645750Smckusick 		if (mcode & 0x4)
48745750Smckusick 			dcmmint(unit+2, mcnd[2], dcm);
48845750Smckusick 		if (mcode & 0x8)
48945750Smckusick 			dcmmint(unit+3, mcnd[3], dcm);
49045750Smckusick 	}
49141480Smckusick 
49242354Smckusick 	dis = &dcmischeme[brd];
49341480Smckusick 	/*
49442354Smckusick 	 * Chalk up a receiver interrupt if the timer running or one of
49542354Smckusick 	 * the ports reports a special character interrupt.
49641480Smckusick 	 */
49742354Smckusick 	if ((code & IIR_TIMEO) ||
49842354Smckusick 	    ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC))
49942354Smckusick 		dis->dis_intr++;
50042354Smckusick 	/*
50142354Smckusick 	 * See if it is time to check/change the interrupt rate.
50242354Smckusick 	 */
50342354Smckusick 	if (dcmistype < 0 &&
50445750Smckusick 	    (i = time.tv_sec - dis->dis_time) >= dcminterval) {
50541480Smckusick 		/*
50642354Smckusick 		 * If currently per-character and averaged over 70 interrupts
50742354Smckusick 		 * per-second (66 is threshold of 600 baud) in last interval,
50842354Smckusick 		 * switch to timer mode.
50942354Smckusick 		 *
51042354Smckusick 		 * XXX decay counts ala load average to avoid spikes?
51141480Smckusick 		 */
51245750Smckusick 		if (dis->dis_perchar && dis->dis_intr > 70 * i)
51342354Smckusick 			dcmsetischeme(brd, DIS_TIMER);
51442354Smckusick 		/*
51542354Smckusick 		 * If currently using timer and had more interrupts than
51642354Smckusick 		 * received characters in the last interval, switch back
51742354Smckusick 		 * to per-character.  Note that after changing to per-char
51842354Smckusick 		 * we must process any characters already in the queue
51942354Smckusick 		 * since they may have arrived before the bitmap was setup.
52042354Smckusick 		 *
52142354Smckusick 		 * XXX decay counts?
52242354Smckusick 		 */
52342354Smckusick 		else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) {
52442354Smckusick 			dcmsetischeme(brd, DIS_PERCHAR);
52541480Smckusick 			dcmrint(brd, dcm);
52641480Smckusick 		}
52742354Smckusick 		dis->dis_intr = dis->dis_char = 0;
52842354Smckusick 		dis->dis_time = time.tv_sec;
52942354Smckusick 	}
530*50220Skarels 	return (1);
53141480Smckusick }
53241480Smckusick 
53341480Smckusick /*
53441480Smckusick  *  Port interrupt.  Can be two things:
53541480Smckusick  *	First, it might be a special character (exception interrupt);
53641480Smckusick  *	Second, it may be a buffer empty (transmit interrupt);
53741480Smckusick  */
53841480Smckusick dcmpint(unit, code, dcm)
53941480Smckusick 	int unit, code;
54042354Smckusick 	struct dcmdevice *dcm;
54141480Smckusick {
54242354Smckusick 	struct tty *tp = &dcm_tty[unit];
54341480Smckusick 
54442354Smckusick 	if (code & IT_SPEC)
54542354Smckusick 		dcmreadbuf(unit, dcm, tp);
54641480Smckusick 	if (code & IT_TX)
54742354Smckusick 		dcmxint(unit, dcm, tp);
54841480Smckusick }
54941480Smckusick 
55041480Smckusick dcmrint(brd, dcm)
55141480Smckusick 	int brd;
55241480Smckusick 	register struct dcmdevice *dcm;
55341480Smckusick {
55442354Smckusick 	register int i, unit;
55541480Smckusick 	register struct tty *tp;
55641480Smckusick 
55741480Smckusick 	unit = MKUNIT(brd, 0);
55841480Smckusick 	tp = &dcm_tty[unit];
55942354Smckusick 	for (i = 0; i < 4; i++, tp++, unit++)
56042354Smckusick 		dcmreadbuf(unit, dcm, tp);
56141480Smckusick }
56241480Smckusick 
56342354Smckusick dcmreadbuf(unit, dcm, tp)
56441480Smckusick 	int unit;
56541480Smckusick 	register struct dcmdevice *dcm;
56641480Smckusick 	register struct tty *tp;
56741480Smckusick {
56842354Smckusick 	int port = PORT(unit);
56942354Smckusick 	register struct dcmpreg *pp = dcm_preg(dcm, port);
57042354Smckusick 	register struct dcmrfifo *fifo;
57141480Smckusick 	register int c, stat;
57241480Smckusick 	register unsigned head;
57342354Smckusick 	int nch = 0;
57442354Smckusick #ifdef IOSTATS
57542354Smckusick 	struct dcmstats *dsp = &dcmstats[BOARD(unit)];
57641480Smckusick 
57742354Smckusick 	dsp->rints++;
57842354Smckusick #endif
57943407Shibler 	if ((tp->t_state & TS_ISOPEN) == 0) {
58042354Smckusick #ifdef KGDB
58149130Skarels 		if ((makedev(dcmmajor, unit) == kgdb_dev) &&
58242354Smckusick 		    (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) &&
58349130Skarels 		    dcm->dcm_rfifos[3-port][head>>1].data_char == FRAME_END) {
58442354Smckusick 			pp->r_head = (head + 2) & RX_MASK;
58549130Skarels 			kgdb_connect(0);	/* trap into kgdb */
58642354Smckusick 			return;
58742354Smckusick 		}
58849130Skarels #endif /* KGDB */
58942354Smckusick 		pp->r_head = pp->r_tail & RX_MASK;
59042354Smckusick 		return;
59142354Smckusick 	}
59241480Smckusick 
59342354Smckusick 	head = pp->r_head & RX_MASK;
59442354Smckusick 	fifo = &dcm->dcm_rfifos[3-port][head>>1];
59542354Smckusick 	/*
59642354Smckusick 	 * XXX upper bound on how many chars we will take in one swallow?
59742354Smckusick 	 */
59842354Smckusick 	while (head != (pp->r_tail & RX_MASK)) {
59942354Smckusick 		/*
60042354Smckusick 		 * Get character/status and update head pointer as fast
60142354Smckusick 		 * as possible to make room for more characters.
60242354Smckusick 		 */
60342354Smckusick 		c = fifo->data_char;
60442354Smckusick 		stat = fifo->data_stat;
60541480Smckusick 		head = (head + 2) & RX_MASK;
60642354Smckusick 		pp->r_head = head;
60742354Smckusick 		fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0];
60842354Smckusick 		nch++;
60941480Smckusick 
61041480Smckusick #ifdef DEBUG
61141480Smckusick 		if (dcmdebug & DDB_INPUT)
61242354Smckusick 			printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n",
61342354Smckusick 			       unit, c&0xFF, c, stat&0xFF,
61442354Smckusick 			       tp->t_flags, head, pp->r_tail);
61541480Smckusick #endif
61642354Smckusick 		/*
61742354Smckusick 		 * Check for and handle errors
61842354Smckusick 		 */
61942354Smckusick 		if (stat & RD_MASK) {
62041480Smckusick #ifdef DEBUG
62142354Smckusick 			if (dcmdebug & (DDB_INPUT|DDB_SIOERR))
62242354Smckusick 				printf("dcmreadbuf(%d): err: c%x('%c') s%x\n",
62342354Smckusick 				       unit, stat, c&0xFF, c);
62441480Smckusick #endif
62541480Smckusick 			if (stat & (RD_BD | RD_FE))
62641480Smckusick 				c |= TTY_FE;
62741480Smckusick 			else if (stat & RD_PE)
62841480Smckusick 				c |= TTY_PE;
62941480Smckusick 			else if (stat & RD_OVF)
63041480Smckusick 				log(LOG_WARNING,
63142354Smckusick 				    "dcm%d: silo overflow\n", unit);
63241480Smckusick 			else if (stat & RD_OE)
63341480Smckusick 				log(LOG_WARNING,
63442354Smckusick 				    "dcm%d: uart overflow\n", unit);
63541480Smckusick 		}
63641480Smckusick 		(*linesw[tp->t_line].l_rint)(c, tp);
63741480Smckusick 	}
63842354Smckusick 	dcmischeme[BOARD(unit)].dis_char += nch;
63942354Smckusick #ifdef IOSTATS
64042354Smckusick 	dsp->rchars += nch;
64142354Smckusick 	if (nch <= DCMRBSIZE)
64242354Smckusick 		dsp->rsilo[nch]++;
64341480Smckusick 	else
64442354Smckusick 		dsp->rsilo[DCMRBSIZE+1]++;
64541480Smckusick #endif
64641480Smckusick }
64741480Smckusick 
64842354Smckusick dcmxint(unit, dcm, tp)
64941480Smckusick 	int unit;
65041480Smckusick 	struct dcmdevice *dcm;
65142354Smckusick 	register struct tty *tp;
65241480Smckusick {
65341480Smckusick 	tp->t_state &= ~TS_BUSY;
65441480Smckusick 	if (tp->t_state & TS_FLUSH)
65541480Smckusick 		tp->t_state &= ~TS_FLUSH;
65649130Skarels 	(*linesw[tp->t_line].l_start)(tp);
65741480Smckusick }
65841480Smckusick 
65941480Smckusick dcmmint(unit, mcnd, dcm)
66041480Smckusick 	register int unit;
66141480Smckusick 	register struct dcmdevice *dcm;
66241480Smckusick         int mcnd;
66341480Smckusick {
66441480Smckusick 	register struct tty *tp;
66544317Shibler 	int delta;
66641480Smckusick 
66741480Smckusick #ifdef DEBUG
66842354Smckusick 	if (dcmdebug & DDB_MODEM)
66945750Smckusick 		printf("dcmmint: port %d mcnd %x mcndlast %x\n",
67044317Shibler 		       unit, mcnd, mcndlast[unit]);
67142354Smckusick #endif
67241480Smckusick 	tp = &dcm_tty[unit];
67344317Shibler 	delta = mcnd ^ mcndlast[unit];
67444317Shibler 	mcndlast[unit] = mcnd;
67549130Skarels 	if ((delta & MI_CTS) && (tp->t_state & TS_ISOPEN) &&
676*50220Skarels 	    (tp->t_flags & CCTS_OFLOW)) {
67749130Skarels 		if (mcnd & MI_CTS) {
67849130Skarels 			tp->t_state &= ~TS_TTSTOP;
67949130Skarels 			ttstart(tp);
68049130Skarels 		} else
68149130Skarels 			tp->t_state |= TS_TTSTOP;	/* inline dcmstop */
68249130Skarels 	}
683*50220Skarels 	if (delta & MI_CD) {
68441480Smckusick 		if (mcnd & MI_CD)
68544317Shibler 			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
686*50220Skarels 		else if ((dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0 &&
687*50220Skarels 		    (*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
688*50220Skarels 			dcm_modem[unit]->mdmout = MO_OFF;
68941480Smckusick 			SEM_LOCK(dcm);
69045750Smckusick 			dcm->dcm_modemchng |= 1<<(unit & 3);
69141480Smckusick 			dcm->dcm_cr |= CR_MODM;
69241480Smckusick 			SEM_UNLOCK(dcm);
69342354Smckusick 			DELAY(10); /* time to change lines */
69441480Smckusick 		}
69541480Smckusick 	}
69641480Smckusick }
69741480Smckusick 
69841480Smckusick dcmioctl(dev, cmd, data, flag)
69941480Smckusick 	dev_t dev;
70041480Smckusick 	caddr_t data;
70141480Smckusick {
70241480Smckusick 	register struct tty *tp;
70341480Smckusick 	register int unit = UNIT(dev);
70441480Smckusick 	register struct dcmdevice *dcm;
70541480Smckusick 	register int port;
70642354Smckusick 	int error, s;
70741480Smckusick 
70841480Smckusick #ifdef DEBUG
70941480Smckusick 	if (dcmdebug & DDB_IOCTL)
71041480Smckusick 		printf("dcmioctl: unit %d cmd %x data %x flag %x\n",
71141480Smckusick 		       unit, cmd, *data, flag);
71241480Smckusick #endif
71341480Smckusick 	tp = &dcm_tty[unit];
71441480Smckusick 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
71541480Smckusick 	if (error >= 0)
71641480Smckusick 		return (error);
71741480Smckusick 	error = ttioctl(tp, cmd, data, flag);
71841480Smckusick 	if (error >= 0)
71941480Smckusick 		return (error);
72041480Smckusick 
72141480Smckusick 	port = PORT(unit);
72241480Smckusick 	dcm = dcm_addr[BOARD(unit)];
72341480Smckusick 	switch (cmd) {
72441480Smckusick 	case TIOCSBRK:
72542354Smckusick 		/*
72642354Smckusick 		 * Wait for transmitter buffer to empty
72742354Smckusick 		 */
72842354Smckusick 		s = spltty();
72942354Smckusick 		while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
73042354Smckusick 			DELAY(DCM_USPERCH(tp->t_ospeed));
73141480Smckusick 		SEM_LOCK(dcm);
73242354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_BRK;
73342354Smckusick 		dcm->dcm_cr |= (1 << port);	/* start break */
73441480Smckusick 		SEM_UNLOCK(dcm);
73542354Smckusick 		splx(s);
73641480Smckusick 		break;
73741480Smckusick 
73841480Smckusick 	case TIOCCBRK:
73941480Smckusick 		SEM_LOCK(dcm);
74042354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_BRK;
74142354Smckusick 		dcm->dcm_cr |= (1 << port);	/* end break */
74241480Smckusick 		SEM_UNLOCK(dcm);
74341480Smckusick 		break;
74441480Smckusick 
74541480Smckusick 	case TIOCSDTR:
74641480Smckusick 		(void) dcmmctl(dev, MO_ON, DMBIS);
74741480Smckusick 		break;
74841480Smckusick 
74941480Smckusick 	case TIOCCDTR:
75041480Smckusick 		(void) dcmmctl(dev, MO_ON, DMBIC);
75141480Smckusick 		break;
75241480Smckusick 
75341480Smckusick 	case TIOCMSET:
75441480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMSET);
75541480Smckusick 		break;
75641480Smckusick 
75741480Smckusick 	case TIOCMBIS:
75841480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMBIS);
75941480Smckusick 		break;
76041480Smckusick 
76141480Smckusick 	case TIOCMBIC:
76241480Smckusick 		(void) dcmmctl(dev, *(int *)data, DMBIC);
76341480Smckusick 		break;
76441480Smckusick 
76541480Smckusick 	case TIOCMGET:
76641480Smckusick 		*(int *)data = dcmmctl(dev, 0, DMGET);
76741480Smckusick 		break;
76841480Smckusick 
76941480Smckusick 	default:
77041480Smckusick 		return (ENOTTY);
77141480Smckusick 	}
77241480Smckusick 	return (0);
77341480Smckusick }
77441480Smckusick 
77541480Smckusick dcmparam(tp, t)
77641480Smckusick 	register struct tty *tp;
77741480Smckusick 	register struct termios *t;
77841480Smckusick {
77941480Smckusick 	register struct dcmdevice *dcm;
78042354Smckusick 	register int port, mode, cflag = t->c_cflag;
78141480Smckusick 	int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab);
78242354Smckusick 
78341480Smckusick 	/* check requested parameters */
78441480Smckusick         if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
785*50220Skarels                 return (EINVAL);
78641480Smckusick         /* and copy to tty */
78741480Smckusick         tp->t_ispeed = t->c_ispeed;
78841480Smckusick         tp->t_ospeed = t->c_ospeed;
78941480Smckusick         tp->t_cflag = cflag;
79041480Smckusick 	if (ospeed == 0) {
79142354Smckusick 		(void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET);
792*50220Skarels 		return (0);
79341480Smckusick 	}
79442354Smckusick 
79542354Smckusick 	mode = 0;
79641480Smckusick 	switch (cflag&CSIZE) {
79741480Smckusick 	case CS5:
79841480Smckusick 		mode = LC_5BITS; break;
79941480Smckusick 	case CS6:
80041480Smckusick 		mode = LC_6BITS; break;
80141480Smckusick 	case CS7:
80241480Smckusick 		mode = LC_7BITS; break;
80341480Smckusick 	case CS8:
80441480Smckusick 		mode = LC_8BITS; break;
80541480Smckusick 	}
80641480Smckusick 	if (cflag&PARENB) {
80741480Smckusick 		if (cflag&PARODD)
80841480Smckusick 			mode |= LC_PODD;
80941480Smckusick 		else
81041480Smckusick 			mode |= LC_PEVEN;
81141480Smckusick 	}
81241480Smckusick 	if (cflag&CSTOPB)
81341480Smckusick 		mode |= LC_2STOP;
81441480Smckusick 	else
81541480Smckusick 		mode |= LC_1STOP;
81641480Smckusick #ifdef DEBUG
81741480Smckusick 	if (dcmdebug & DDB_PARAM)
81842354Smckusick 		printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n",
81942354Smckusick 		       UNIT(tp->t_dev), cflag, mode, tp->t_ospeed,
82042354Smckusick 		       DCM_USPERCH(tp->t_ospeed));
82141480Smckusick #endif
82242354Smckusick 
82342354Smckusick 	port = PORT(tp->t_dev);
82442354Smckusick 	dcm = dcm_addr[BOARD(tp->t_dev)];
82542354Smckusick 	/*
82642354Smckusick 	 * Wait for transmitter buffer to empty.
82742354Smckusick 	 */
82841480Smckusick 	while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
82942354Smckusick 		DELAY(DCM_USPERCH(tp->t_ospeed));
83042354Smckusick 	/*
83142354Smckusick 	 * Make changes known to hardware.
83242354Smckusick 	 */
83342354Smckusick 	dcm->dcm_data[port].dcm_baud = ospeed;
83441480Smckusick 	dcm->dcm_data[port].dcm_conf = mode;
83541480Smckusick 	SEM_LOCK(dcm);
83642354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_CON;
83742354Smckusick 	dcm->dcm_cr |= (1 << port);
83841480Smckusick 	SEM_UNLOCK(dcm);
83942354Smckusick 	/*
84049130Skarels 	 * Delay for config change to take place. Weighted by baud.
84142354Smckusick 	 * XXX why do we do this?
84242354Smckusick 	 */
84342354Smckusick 	DELAY(16 * DCM_USPERCH(tp->t_ospeed));
844*50220Skarels 	return (0);
84541480Smckusick }
84641480Smckusick 
84741480Smckusick dcmstart(tp)
84841480Smckusick 	register struct tty *tp;
84941480Smckusick {
85041480Smckusick 	register struct dcmdevice *dcm;
85142354Smckusick 	register struct dcmpreg *pp;
85242354Smckusick 	register struct dcmtfifo *fifo;
85342354Smckusick 	register char *bp;
85442354Smckusick 	register unsigned tail, next;
85542354Smckusick 	register int port, nch;
85642354Smckusick 	unsigned head;
85742354Smckusick 	char buf[16];
85842354Smckusick 	int s;
85942354Smckusick #ifdef IOSTATS
86042354Smckusick 	struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)];
86142354Smckusick 	int tch = 0;
86242354Smckusick #endif
86342354Smckusick 
86441480Smckusick 	s = spltty();
86542354Smckusick #ifdef IOSTATS
86642354Smckusick 	dsp->xints++;
86742354Smckusick #endif
86841480Smckusick #ifdef DEBUG
86941480Smckusick 	if (dcmdebug & DDB_OUTPUT)
87042354Smckusick 		printf("dcmstart(%d): state %x flags %x outcc %d\n",
87142354Smckusick 		       UNIT(tp->t_dev), tp->t_state, tp->t_flags,
87242354Smckusick 		       tp->t_outq.c_cc);
87341480Smckusick #endif
87441480Smckusick 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
87541480Smckusick 		goto out;
87641480Smckusick 	if (tp->t_outq.c_cc <= tp->t_lowat) {
87741480Smckusick 		if (tp->t_state&TS_ASLEEP) {
87841480Smckusick 			tp->t_state &= ~TS_ASLEEP;
87941480Smckusick 			wakeup((caddr_t)&tp->t_outq);
88041480Smckusick 		}
88141480Smckusick 		if (tp->t_wsel) {
88241480Smckusick 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
88341480Smckusick 			tp->t_wsel = 0;
88441480Smckusick 			tp->t_state &= ~TS_WCOLL;
88541480Smckusick 		}
88641480Smckusick 	}
88742354Smckusick 	if (tp->t_outq.c_cc == 0) {
88842354Smckusick #ifdef IOSTATS
88942354Smckusick 		dsp->xempty++;
89042354Smckusick #endif
89142354Smckusick 		goto out;
89242354Smckusick 	}
89342354Smckusick 
89442354Smckusick 	dcm = dcm_addr[BOARD(tp->t_dev)];
89542354Smckusick 	port = PORT(tp->t_dev);
89642354Smckusick 	pp = dcm_preg(dcm, port);
89742354Smckusick 	tail = pp->t_tail & TX_MASK;
89842354Smckusick 	next = (tail + 1) & TX_MASK;
89942354Smckusick 	head = pp->t_head & TX_MASK;
90042354Smckusick 	if (head == next)
90142354Smckusick 		goto out;
90242354Smckusick 	fifo = &dcm->dcm_tfifos[3-port][tail];
90342354Smckusick again:
90442354Smckusick 	nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK);
90542354Smckusick #ifdef IOSTATS
90642354Smckusick 	tch += nch;
90742354Smckusick #endif
90841480Smckusick #ifdef DEBUG
90942354Smckusick 	if (dcmdebug & DDB_OUTPUT)
91042354Smckusick 		printf("\thead %x tail %x nch %d\n", head, tail, nch);
91141480Smckusick #endif
91242354Smckusick 	/*
91342354Smckusick 	 * Loop transmitting all the characters we can.
91442354Smckusick 	 */
91542354Smckusick 	for (bp = buf; --nch >= 0; bp++) {
91642354Smckusick 		fifo->data_char = *bp;
91742354Smckusick 		pp->t_tail = next;
91842354Smckusick 		/*
91942354Smckusick 		 * If this is the first character,
92042354Smckusick 		 * get the hardware moving right now.
92142354Smckusick 		 */
92242354Smckusick 		if (bp == buf) {
92342354Smckusick 			tp->t_state |= TS_BUSY;
92442354Smckusick 			SEM_LOCK(dcm);
92542354Smckusick 			dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
92642354Smckusick 			dcm->dcm_cr |= (1 << port);
92742354Smckusick 			SEM_UNLOCK(dcm);
92842354Smckusick 		}
92941480Smckusick 		tail = next;
93042354Smckusick 		fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0];
93142354Smckusick 		next = (next + 1) & TX_MASK;
93241480Smckusick 	}
93342354Smckusick 	/*
93442354Smckusick 	 * Head changed while we were loading the buffer,
93542354Smckusick 	 * go back and load some more if we can.
93642354Smckusick 	 */
93742354Smckusick 	if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) {
93842354Smckusick #ifdef IOSTATS
93942354Smckusick 		dsp->xrestarts++;
94042354Smckusick #endif
94142354Smckusick 		head = pp->t_head & TX_MASK;
94242354Smckusick 		goto again;
94342354Smckusick 	}
94449130Skarels 
94542354Smckusick 	/*
94642354Smckusick 	 * Kick it one last time in case it finished while we were
94744317Shibler 	 * loading the last bunch.
94842354Smckusick 	 */
94942354Smckusick 	if (bp > &buf[1]) {
95041480Smckusick 		tp->t_state |= TS_BUSY;
95141480Smckusick 		SEM_LOCK(dcm);
95242354Smckusick 		dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
95342354Smckusick 		dcm->dcm_cr |= (1 << port);
95442354Smckusick 		SEM_UNLOCK(dcm);
95542354Smckusick 	}
95641480Smckusick #ifdef DEBUG
95741480Smckusick 	if (dcmdebug & DDB_INTR)
95843407Shibler 		printf("dcmstart(%d): head %x tail %x outqcc %d\n",
95943407Shibler 		       UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc);
96041480Smckusick #endif
96141480Smckusick out:
96242354Smckusick #ifdef IOSTATS
96342354Smckusick 	dsp->xchars += tch;
96442354Smckusick 	if (tch <= DCMXBSIZE)
96542354Smckusick 		dsp->xsilo[tch]++;
96642354Smckusick 	else
96742354Smckusick 		dsp->xsilo[DCMXBSIZE+1]++;
96842354Smckusick #endif
96941480Smckusick 	splx(s);
97041480Smckusick }
97141480Smckusick 
97241480Smckusick /*
97341480Smckusick  * Stop output on a line.
97441480Smckusick  */
97541480Smckusick dcmstop(tp, flag)
97641480Smckusick 	register struct tty *tp;
97741480Smckusick {
97841480Smckusick 	int s;
97941480Smckusick 
98041480Smckusick 	s = spltty();
98141480Smckusick 	if (tp->t_state & TS_BUSY) {
98242354Smckusick 		/* XXX is there some way to safely stop transmission? */
98344317Shibler 		if ((tp->t_state&TS_TTSTOP) == 0)
98441480Smckusick 			tp->t_state |= TS_FLUSH;
98541480Smckusick 	}
98641480Smckusick 	splx(s);
98741480Smckusick }
98841480Smckusick 
98945750Smckusick /*
99045750Smckusick  * Modem control
99145750Smckusick  */
99241480Smckusick dcmmctl(dev, bits, how)
99341480Smckusick 	dev_t dev;
99441480Smckusick 	int bits, how;
99541480Smckusick {
99641480Smckusick 	register struct dcmdevice *dcm;
997*50220Skarels 	int s, unit, brd, hit = 0;
99841480Smckusick 
99945750Smckusick 	unit = UNIT(dev);
100045750Smckusick #ifdef DEBUG
100145750Smckusick 	if (dcmdebug & DDB_MODEM)
100245750Smckusick 		printf("dcmmctl(%d) unit %d  bits 0x%x how %x\n",
100345750Smckusick 		       BOARD(unit), unit, bits, how);
100445750Smckusick #endif
100541480Smckusick 
1006*50220Skarels 	brd = BOARD(unit);
1007*50220Skarels 	dcm = dcm_addr[brd];
100841480Smckusick 	s = spltty();
100941480Smckusick 	switch (how) {
101041480Smckusick 
101141480Smckusick 	case DMSET:
101245750Smckusick 		dcm_modem[unit]->mdmout = bits;
101341480Smckusick 		hit++;
101441480Smckusick 		break;
101541480Smckusick 
101641480Smckusick 	case DMBIS:
101745750Smckusick 		dcm_modem[unit]->mdmout |= bits;
101841480Smckusick 		hit++;
101941480Smckusick 		break;
102041480Smckusick 
102141480Smckusick 	case DMBIC:
102245750Smckusick 		dcm_modem[unit]->mdmout &= ~bits;
102341480Smckusick 		hit++;
102441480Smckusick 		break;
102541480Smckusick 
102641480Smckusick 	case DMGET:
102745750Smckusick 		bits = dcm_modem[unit]->mdmin;
1028*50220Skarels 		if (dcmsoftCAR[brd] & FLAG_STDDCE)
1029*50220Skarels 			bits = hp2dce_in(bits);
103041480Smckusick 		break;
103141480Smckusick 	}
103241480Smckusick 	if (hit) {
103341480Smckusick 		SEM_LOCK(dcm);
103445750Smckusick 		dcm->dcm_modemchng |= 1<<(unit & 3);
103541480Smckusick 		dcm->dcm_cr |= CR_MODM;
103641480Smckusick 		SEM_UNLOCK(dcm);
103742354Smckusick 		DELAY(10); /* delay until done */
103841480Smckusick 		(void) splx(s);
103941480Smckusick 	}
1040*50220Skarels 	return (bits);
104141480Smckusick }
104241480Smckusick 
104342354Smckusick /*
104442354Smckusick  * Set board to either interrupt per-character or at a fixed interval.
104542354Smckusick  */
104642354Smckusick dcmsetischeme(brd, flags)
104742354Smckusick 	int brd, flags;
104841480Smckusick {
104942354Smckusick 	register struct dcmdevice *dcm = dcm_addr[brd];
105042354Smckusick 	register struct dcmischeme *dis = &dcmischeme[brd];
105141480Smckusick 	register int i;
105242354Smckusick 	u_char mask;
105342354Smckusick 	int perchar = flags & DIS_PERCHAR;
105441480Smckusick 
105541480Smckusick #ifdef DEBUG
105642354Smckusick 	if (dcmdebug & DDB_INTSCHM)
105742354Smckusick 		printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n",
105842354Smckusick 		       brd, perchar, dis->dis_perchar,
105942354Smckusick 		       dis->dis_intr, dis->dis_char);
106042354Smckusick 	if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) {
106142354Smckusick 		printf("dcmsetischeme(%d):  redundent request %d\n",
106242354Smckusick 		       brd, perchar);
106342354Smckusick 		return;
106442354Smckusick 	}
106542354Smckusick #endif
106642354Smckusick 	/*
106742354Smckusick 	 * If perchar is non-zero, we enable interrupts on all characters
106842354Smckusick 	 * otherwise we disable perchar interrupts and use periodic
106942354Smckusick 	 * polling interrupts.
107042354Smckusick 	 */
107142354Smckusick 	dis->dis_perchar = perchar;
107242354Smckusick 	mask = perchar ? 0xf : 0x0;
107342354Smckusick 	for (i = 0; i < 256; i++)
107442354Smckusick 		dcm->dcm_bmap[i].data_data = mask;
107542354Smckusick 	/*
107642354Smckusick 	 * Don't slow down tandem mode, interrupt on flow control
107742354Smckusick 	 * chars for any port on the board.
107842354Smckusick 	 */
107942354Smckusick 	if (!perchar) {
108042354Smckusick 		register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)];
108142354Smckusick 		int c;
108242354Smckusick 
108342354Smckusick 		for (i = 0; i < 4; i++, tp++) {
108442354Smckusick 			if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE)
108542354Smckusick 				dcm->dcm_bmap[c].data_data |= (1 << i);
108642354Smckusick 			if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE)
108742354Smckusick 				dcm->dcm_bmap[c].data_data |= (1 << i);
108841480Smckusick 		}
108941480Smckusick 	}
109042354Smckusick 	/*
109142354Smckusick 	 * Board starts with timer disabled so if first call is to
109242354Smckusick 	 * set perchar mode then we don't want to toggle the timer.
109342354Smckusick 	 */
109442354Smckusick 	if (flags == (DIS_RESET|DIS_PERCHAR))
109541480Smckusick 		return;
109642354Smckusick 	/*
109742354Smckusick 	 * Toggle card 16.7ms interrupts (we first make sure that card
109842354Smckusick 	 * has cleared the bit so it will see the toggle).
109942354Smckusick 	 */
110042354Smckusick 	while (dcm->dcm_cr & CR_TIMER)
110142354Smckusick 		;
110241480Smckusick 	SEM_LOCK(dcm);
110342354Smckusick 	dcm->dcm_cr |= CR_TIMER;
110441480Smckusick 	SEM_UNLOCK(dcm);
110541480Smckusick }
110641480Smckusick 
110741480Smckusick /*
110841480Smckusick  * Following are all routines needed for DCM to act as console
110941480Smckusick  */
111045788Sbostic #include "../hp300/cons.h"
111141480Smckusick 
111242354Smckusick dcmcnprobe(cp)
111342354Smckusick 	struct consdev *cp;
111441480Smckusick {
111542354Smckusick 	register struct hp_hw *hw;
111649130Skarels 	int unit;
111741480Smckusick 
111849130Skarels 	/* locate the major number */
111949130Skarels 	for (dcmmajor = 0; dcmmajor < nchrdev; dcmmajor++)
112049130Skarels 		if (cdevsw[dcmmajor].d_open == dcmopen)
112149130Skarels 			break;
112249130Skarels 
112342354Smckusick 	/*
112442354Smckusick 	 * Implicitly assigns the lowest select code DCM card found to be
112542354Smckusick 	 * logical unit 0 (actually CONUNIT).  If your config file does
112642354Smckusick 	 * anything different, you're screwed.
112742354Smckusick 	 */
112842354Smckusick 	for (hw = sc_table; hw->hw_type; hw++)
112949301Shibler 		if (HW_ISDEV(hw, D_COMMDCM) && !badaddr((short *)hw->hw_kva))
113042354Smckusick 			break;
113149301Shibler 	if (!HW_ISDEV(hw, D_COMMDCM)) {
113242354Smckusick 		cp->cn_pri = CN_DEAD;
113342354Smckusick 		return;
113442354Smckusick 	}
113542354Smckusick 	unit = CONUNIT;
113649301Shibler 	dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_kva;
113741480Smckusick 
113842354Smckusick 	/* initialize required fields */
113949130Skarels 	cp->cn_dev = makedev(dcmmajor, unit);
114042354Smckusick 	cp->cn_tp = &dcm_tty[unit];
114142354Smckusick 	switch (dcm_addr[BOARD(unit)]->dcm_rsid) {
114242354Smckusick 	case DCMID:
114342354Smckusick 		cp->cn_pri = CN_NORMAL;
114442354Smckusick 		break;
114542354Smckusick 	case DCMID|DCMCON:
114642354Smckusick 		cp->cn_pri = CN_REMOTE;
114742354Smckusick 		break;
114842354Smckusick 	default:
114942354Smckusick 		cp->cn_pri = CN_DEAD;
115049130Skarels 		return;
115142354Smckusick 	}
115249130Skarels 	/*
115349130Skarels 	 * If dcmconsole is initialized, raise our priority.
115449130Skarels 	 */
115549130Skarels 	if (dcmconsole == UNIT(unit))
115649130Skarels 		cp->cn_pri = CN_REMOTE;
115749301Shibler #ifdef KGDB_CHEAT
115849130Skarels 	/*
115949130Skarels 	 * This doesn't currently work, at least not with ite consoles;
116049130Skarels 	 * the console hasn't been initialized yet.
116149130Skarels 	 */
116249130Skarels 	if (major(kgdb_dev) == dcmmajor && BOARD(kgdb_dev) == BOARD(unit)) {
116349130Skarels 		(void) dcminit(kgdb_dev, kgdb_rate);
116449130Skarels 		if (kgdb_debug_init) {
116549130Skarels 			/*
116649130Skarels 			 * We assume that console is ready for us...
116749130Skarels 			 * this assumes that a dca or ite console
116849130Skarels 			 * has been selected already and will init
116949130Skarels 			 * on the first putc.
117049130Skarels 			 */
117149130Skarels 			printf("dcm%d: ", UNIT(kgdb_dev));
117249130Skarels 			kgdb_connect(1);
117349130Skarels 		}
117449130Skarels 	}
117549130Skarels #endif
117642354Smckusick }
117742354Smckusick 
117842354Smckusick dcmcninit(cp)
117942354Smckusick 	struct consdev *cp;
118042354Smckusick {
118142354Smckusick 	dcminit(cp->cn_dev, dcmdefaultrate);
118249130Skarels 	dcmconsinit = 1;
118342354Smckusick 	dcmconsole = UNIT(cp->cn_dev);
118442354Smckusick }
118542354Smckusick 
118642354Smckusick dcminit(dev, rate)
118742354Smckusick 	dev_t dev;
118842354Smckusick 	int rate;
118942354Smckusick {
119042354Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
119142354Smckusick 	int s, mode, port;
119242354Smckusick 
119342354Smckusick 	port = PORT(dev);
119442354Smckusick 	mode = LC_8BITS | LC_1STOP;
119541480Smckusick 	s = splhigh();
119642354Smckusick 	/*
119742354Smckusick 	 * Wait for transmitter buffer to empty.
119842354Smckusick 	 */
119942354Smckusick 	while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr)
120042354Smckusick 		DELAY(DCM_USPERCH(rate));
120142354Smckusick 	/*
120242354Smckusick 	 * Make changes known to hardware.
120342354Smckusick 	 */
120442354Smckusick 	dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab);
120542354Smckusick 	dcm->dcm_data[port].dcm_conf = mode;
120642354Smckusick 	SEM_LOCK(dcm);
120742354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_CON;
120842354Smckusick 	dcm->dcm_cr |= (1 << port);
120942354Smckusick 	SEM_UNLOCK(dcm);
121042354Smckusick 	/*
121149130Skarels 	 * Delay for config change to take place. Weighted by baud.
121242354Smckusick 	 * XXX why do we do this?
121342354Smckusick 	 */
121442354Smckusick 	DELAY(16 * DCM_USPERCH(rate));
121541480Smckusick 	splx(s);
121641480Smckusick }
121741480Smckusick 
121841480Smckusick dcmcngetc(dev)
121942354Smckusick 	dev_t dev;
122041480Smckusick {
122142354Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
122242354Smckusick 	register struct dcmrfifo *fifo;
122342354Smckusick 	register struct dcmpreg *pp;
122442354Smckusick 	register unsigned head;
122542354Smckusick 	int s, c, stat, port;
122642354Smckusick 
122742354Smckusick 	port = PORT(dev);
122842354Smckusick 	pp = dcm_preg(dcm, port);
122942354Smckusick 	s = splhigh();
123042354Smckusick 	head = pp->r_head & RX_MASK;
123142354Smckusick 	fifo = &dcm->dcm_rfifos[3-port][head>>1];
123242354Smckusick 	while (head == (pp->r_tail & RX_MASK))
123342354Smckusick 		;
123442354Smckusick 	/*
123542354Smckusick 	 * If board interrupts are enabled, just let our received char
123642354Smckusick 	 * interrupt through in case some other port on the board was
123742354Smckusick 	 * busy.  Otherwise we must clear the interrupt.
123842354Smckusick 	 */
123942354Smckusick 	SEM_LOCK(dcm);
124042354Smckusick 	if ((dcm->dcm_ic & IC_IE) == 0)
124142354Smckusick 		stat = dcm->dcm_iir;
124242354Smckusick 	SEM_UNLOCK(dcm);
124342354Smckusick 	c = fifo->data_char;
124442354Smckusick 	stat = fifo->data_stat;
124542354Smckusick 	pp->r_head = (head + 2) & RX_MASK;
124642354Smckusick 	splx(s);
1247*50220Skarels 	return (c);
124841480Smckusick }
124941480Smckusick 
125041480Smckusick /*
125141480Smckusick  * Console kernel output character routine.
125241480Smckusick  */
125341480Smckusick dcmcnputc(dev, c)
125441480Smckusick 	dev_t dev;
125542354Smckusick 	int c;
125641480Smckusick {
125741480Smckusick 	register struct dcmdevice *dcm = dcm_addr[BOARD(dev)];
125842354Smckusick 	register struct dcmpreg *pp;
125942354Smckusick 	unsigned tail;
126042354Smckusick 	int s, port, stat;
126141480Smckusick 
126242354Smckusick 	port = PORT(dev);
126342354Smckusick 	pp = dcm_preg(dcm, port);
126442354Smckusick 	s = splhigh();
126542354Smckusick #ifdef KGDB
126642354Smckusick 	if (dev != kgdb_dev)
126742354Smckusick #endif
126849130Skarels 	if (dcmconsinit == 0) {
126942354Smckusick 		(void) dcminit(dev, dcmdefaultrate);
127049130Skarels 		dcmconsinit = 1;
127142354Smckusick 	}
127242354Smckusick 	tail = pp->t_tail & TX_MASK;
127342354Smckusick 	while (tail != (pp->t_head & TX_MASK))
127442354Smckusick 		;
127542354Smckusick 	dcm->dcm_tfifos[3-port][tail].data_char = c;
127642354Smckusick 	pp->t_tail = tail = (tail + 1) & TX_MASK;
127741480Smckusick 	SEM_LOCK(dcm);
127842354Smckusick 	dcm->dcm_cmdtab[port].dcm_data |= CT_TX;
127942354Smckusick 	dcm->dcm_cr |= (1 << port);
128041480Smckusick 	SEM_UNLOCK(dcm);
128142354Smckusick 	while (tail != (pp->t_head & TX_MASK))
128242354Smckusick 		;
128342354Smckusick 	/*
128442354Smckusick 	 * If board interrupts are enabled, just let our completion
128542354Smckusick 	 * interrupt through in case some other port on the board
128642354Smckusick 	 * was busy.  Otherwise we must clear the interrupt.
128742354Smckusick 	 */
128842354Smckusick 	if ((dcm->dcm_ic & IC_IE) == 0) {
128942354Smckusick 		SEM_LOCK(dcm);
129042354Smckusick 		stat = dcm->dcm_iir;
129142354Smckusick 		SEM_UNLOCK(dcm);
129242354Smckusick 	}
129341480Smckusick 	splx(s);
129441480Smckusick }
129541480Smckusick #endif
1296