xref: /original-bsd/sys/pmax/dev/dc.c (revision 95ecee29)
1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ralph Campbell and Rick Macklem.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)dc.c	8.2 (Berkeley) 11/30/93
11  */
12 
13 /*
14  * devDC7085.c --
15  *
16  *     	This file contains machine-dependent routines that handle the
17  *	output queue for the serial lines.
18  *
19  *	Copyright (C) 1989 Digital Equipment Corporation.
20  *	Permission to use, copy, modify, and distribute this software and
21  *	its documentation for any purpose and without fee is hereby granted,
22  *	provided that the above copyright notice appears in all copies.
23  *	Digital Equipment Corporation makes no representations about the
24  *	suitability of this software for any purpose.  It is provided "as is"
25  *	without express or implied warranty.
26  *
27  * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devDC7085.c,
28  *	v 1.4 89/08/29 11:55:30 nelson Exp $ SPRITE (DECWRL)";
29  */
30 
31 #include <dc.h>
32 #if NDC > 0
33 /*
34  * DC7085 (DZ-11 look alike) Driver
35  */
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/ioctl.h>
39 #include <sys/tty.h>
40 #include <sys/proc.h>
41 #include <sys/map.h>
42 #include <sys/buf.h>
43 #include <sys/conf.h>
44 #include <sys/file.h>
45 #include <sys/uio.h>
46 #include <sys/kernel.h>
47 #include <sys/syslog.h>
48 
49 #include <machine/dc7085cons.h>
50 #include <machine/pmioctl.h>
51 
52 #include <pmax/pmax/pmaxtype.h>
53 #include <pmax/pmax/cons.h>
54 
55 #include <pmax/dev/device.h>
56 #include <pmax/dev/pdma.h>
57 #include <pmax/dev/fbreg.h>
58 
59 extern int pmax_boardtype;
60 extern struct consdev cn_tab;
61 
62 /*
63  * Driver information for auto-configuration stuff.
64  */
65 int	dcprobe();
66 void	dcintr();
67 struct	driver dcdriver = {
68 	"dc", dcprobe, 0, 0, dcintr,
69 };
70 
71 #define	NDCLINE 	(NDC*4)
72 
73 void dcstart	__P((struct tty *));
74 void dcxint	__P((struct tty *));
75 void dcPutc	__P((dev_t, int));
76 void dcscan	__P((void *));
77 extern void ttrstrt __P((void *));
78 int dcGetc	__P((dev_t));
79 int dcparam	__P((struct tty *, struct termios *));
80 
81 struct	tty dc_tty[NDCLINE];
82 int	dc_cnt = NDCLINE;
83 void	(*dcDivertXInput)();	/* X windows keyboard input routine */
84 void	(*dcMouseEvent)();	/* X windows mouse motion event routine */
85 void	(*dcMouseButtons)();	/* X windows mouse buttons event routine */
86 #ifdef DEBUG
87 int	debugChar;
88 #endif
89 
90 /*
91  * Software copy of brk register since it isn't readable
92  */
93 int	dc_brk[NDC];
94 char	dcsoftCAR[NDC];		/* mask of dc's with carrier on (DSR) */
95 
96 /*
97  * The DC7085 doesn't interrupt on carrier transitions, so
98  * we have to use a timer to watch it.
99  */
100 int	dc_timer;		/* true if timer started */
101 
102 /*
103  * Pdma structures for fast output code
104  */
105 struct	pdma dcpdma[NDCLINE];
106 
107 struct speedtab dcspeedtab[] = {
108 	0,	0,
109 	50,	LPR_B50,
110 	75,	LPR_B75,
111 	110,	LPR_B110,
112 	134,	LPR_B134,
113 	150,	LPR_B150,
114 	300,	LPR_B300,
115 	600,	LPR_B600,
116 	1200,	LPR_B1200,
117 	1800,	LPR_B1800,
118 	2400,	LPR_B2400,
119 	4800,	LPR_B4800,
120 	9600,	LPR_B9600,
121 	19200,	LPR_B19200,
122 	-1,	-1
123 };
124 
125 #ifndef	PORTSELECTOR
126 #define	ISPEED	TTYDEF_SPEED
127 #define	LFLAG	TTYDEF_LFLAG
128 #else
129 #define	ISPEED	B4800
130 #define	LFLAG	(TTYDEF_LFLAG & ~ECHO)
131 #endif
132 
133 /*
134  * Test to see if device is present.
135  * Return true if found and initialized ok.
136  */
137 dcprobe(cp)
138 	register struct pmax_ctlr *cp;
139 {
140 	register dcregs *dcaddr;
141 	register struct pdma *pdp;
142 	register struct tty *tp;
143 	register int cntr;
144 	int s;
145 
146 	if (cp->pmax_unit >= NDC)
147 		return (0);
148 	if (badaddr(cp->pmax_addr, 2))
149 		return (0);
150 
151 	/*
152 	 * For a remote console, wait a while for previous output to
153 	 * complete.
154 	 */
155 	if (major(cn_tab.cn_dev) == DCDEV && cp->pmax_unit == 0 &&
156 		cn_tab.cn_screen == 0)
157 		DELAY(10000);
158 
159 	/* reset chip */
160 	dcaddr = (dcregs *)cp->pmax_addr;
161 	dcaddr->dc_csr = CSR_CLR;
162 	MachEmptyWriteBuffer();
163 	while (dcaddr->dc_csr & CSR_CLR)
164 		;
165 	dcaddr->dc_csr = CSR_MSE | CSR_TIE | CSR_RIE;
166 
167 	/* init pseudo DMA structures */
168 	pdp = &dcpdma[cp->pmax_unit * 4];
169 	tp = &dc_tty[cp->pmax_unit * 4];
170 	for (cntr = 0; cntr < 4; cntr++) {
171 		pdp->p_addr = (void *)dcaddr;
172 		pdp->p_arg = (int)tp;
173 		pdp->p_fcn = dcxint;
174 		pdp++, tp++;
175 	}
176 	dcsoftCAR[cp->pmax_unit] = cp->pmax_flags | 0xB;
177 
178 	if (dc_timer == 0) {
179 		dc_timer = 1;
180 		timeout(dcscan, (void *)0, hz);
181 	}
182 
183 	/*
184 	 * Special handling for consoles.
185 	 */
186 	if (cp->pmax_unit == 0) {
187 		if (cn_tab.cn_screen) {
188 			s = spltty();
189 			dcaddr->dc_lpr = LPR_RXENAB | LPR_8_BIT_CHAR |
190 				LPR_B4800 | DCKBD_PORT;
191 			MachEmptyWriteBuffer();
192 			dcaddr->dc_lpr = LPR_RXENAB | LPR_B4800 | LPR_OPAR |
193 				LPR_PARENB | LPR_8_BIT_CHAR | DCMOUSE_PORT;
194 			MachEmptyWriteBuffer();
195 			DELAY(1000);
196 			KBDReset(makedev(DCDEV, DCKBD_PORT), dcPutc);
197 			MouseInit(makedev(DCDEV, DCMOUSE_PORT), dcPutc, dcGetc);
198 			splx(s);
199 		} else if (major(cn_tab.cn_dev) == DCDEV) {
200 			s = spltty();
201 			dcaddr->dc_lpr = LPR_RXENAB | LPR_8_BIT_CHAR |
202 				LPR_B9600 | minor(cn_tab.cn_dev);
203 			MachEmptyWriteBuffer();
204 			DELAY(1000);
205 			cn_tab.cn_disabled = 0;
206 			splx(s);
207 		}
208 	}
209 	printf("dc%d at nexus0 csr 0x%x priority %d\n",
210 		cp->pmax_unit, cp->pmax_addr, cp->pmax_pri);
211 	return (1);
212 }
213 
214 dcopen(dev, flag, mode, p)
215 	dev_t dev;
216 	int flag, mode;
217 	struct proc *p;
218 {
219 	register struct tty *tp;
220 	register int unit;
221 	int s, error = 0;
222 
223 	unit = minor(dev);
224 	if (unit >= dc_cnt || dcpdma[unit].p_addr == (void *)0)
225 		return (ENXIO);
226 	tp = &dc_tty[unit];
227 	tp->t_oproc = dcstart;
228 	tp->t_param = dcparam;
229 	tp->t_dev = dev;
230 	if ((tp->t_state & TS_ISOPEN) == 0) {
231 		tp->t_state |= TS_WOPEN;
232 		ttychars(tp);
233 #ifndef PORTSELECTOR
234 		if (tp->t_ispeed == 0) {
235 #endif
236 			tp->t_iflag = TTYDEF_IFLAG;
237 			tp->t_oflag = TTYDEF_OFLAG;
238 			tp->t_cflag = TTYDEF_CFLAG;
239 			tp->t_lflag = LFLAG;
240 			tp->t_ispeed = tp->t_ospeed = ISPEED;
241 #ifdef PORTSELECTOR
242 			tp->t_cflag |= HUPCL;
243 #else
244 		}
245 #endif
246 		(void) dcparam(tp, &tp->t_termios);
247 		ttsetwater(tp);
248 	} else if ((tp->t_state & TS_XCLUDE) && curproc->p_ucred->cr_uid != 0)
249 		return (EBUSY);
250 	(void) dcmctl(dev, DML_DTR, DMSET);
251 	s = spltty();
252 	while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL) &&
253 	       !(tp->t_state & TS_CARR_ON)) {
254 		tp->t_state |= TS_WOPEN;
255 		if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
256 		    ttopen, 0))
257 			break;
258 	}
259 	splx(s);
260 	if (error)
261 		return (error);
262 	return ((*linesw[tp->t_line].l_open)(dev, tp));
263 }
264 
265 /*ARGSUSED*/
266 dcclose(dev, flag, mode, p)
267 	dev_t dev;
268 	int flag, mode;
269 	struct proc *p;
270 {
271 	register struct tty *tp;
272 	register int unit, bit;
273 
274 	unit = minor(dev);
275 	tp = &dc_tty[unit];
276 	bit = 1 << ((unit & 03) + 8);
277 	if (dc_brk[unit >> 2] & bit) {
278 		dc_brk[unit >> 2] &= ~bit;
279 		ttyoutput(0, tp);
280 	}
281 	(*linesw[tp->t_line].l_close)(tp, flag);
282 	if ((tp->t_cflag & HUPCL) || (tp->t_state & TS_WOPEN) ||
283 	    !(tp->t_state & TS_ISOPEN))
284 		(void) dcmctl(dev, 0, DMSET);
285 	return (ttyclose(tp));
286 }
287 
288 dcread(dev, uio, flag)
289 	dev_t dev;
290 	struct uio *uio;
291 {
292 	register struct tty *tp;
293 
294 	tp = &dc_tty[minor(dev)];
295 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
296 }
297 
298 dcwrite(dev, uio, flag)
299 	dev_t dev;
300 	struct uio *uio;
301 {
302 	register struct tty *tp;
303 
304 	tp = &dc_tty[minor(dev)];
305 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
306 }
307 
308 /*ARGSUSED*/
309 dcioctl(dev, cmd, data, flag, p)
310 	dev_t dev;
311 	int cmd;
312 	caddr_t data;
313 	int flag;
314 	struct proc *p;
315 {
316 	register struct tty *tp;
317 	register int unit = minor(dev);
318 	register int dc = unit >> 2;
319 	int error;
320 
321 	tp = &dc_tty[unit];
322 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
323 	if (error >= 0)
324 		return (error);
325 	error = ttioctl(tp, cmd, data, flag);
326 	if (error >= 0)
327 		return (error);
328 
329 	switch (cmd) {
330 
331 	case TIOCSBRK:
332 		dc_brk[dc] |= 1 << ((unit & 03) + 8);
333 		ttyoutput(0, tp);
334 		break;
335 
336 	case TIOCCBRK:
337 		dc_brk[dc] &= ~(1 << ((unit & 03) + 8));
338 		ttyoutput(0, tp);
339 		break;
340 
341 	case TIOCSDTR:
342 		(void) dcmctl(dev, DML_DTR|DML_RTS, DMBIS);
343 		break;
344 
345 	case TIOCCDTR:
346 		(void) dcmctl(dev, DML_DTR|DML_RTS, DMBIC);
347 		break;
348 
349 	case TIOCMSET:
350 		(void) dcmctl(dev, *(int *)data, DMSET);
351 		break;
352 
353 	case TIOCMBIS:
354 		(void) dcmctl(dev, *(int *)data, DMBIS);
355 		break;
356 
357 	case TIOCMBIC:
358 		(void) dcmctl(dev, *(int *)data, DMBIC);
359 		break;
360 
361 	case TIOCMGET:
362 		*(int *)data = dcmctl(dev, 0, DMGET);
363 		break;
364 
365 	default:
366 		return (ENOTTY);
367 	}
368 	return (0);
369 }
370 
371 dcparam(tp, t)
372 	register struct tty *tp;
373 	register struct termios *t;
374 {
375 	register dcregs *dcaddr;
376 	register int lpr;
377 	register int cflag = t->c_cflag;
378 	int unit = minor(tp->t_dev);
379 	int ospeed = ttspeedtab(t->c_ospeed, dcspeedtab);
380 
381 	/* check requested parameters */
382         if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed) ||
383             (cflag & CSIZE) == CS5 || (cflag & CSIZE) == CS6 ||
384 	    (pmax_boardtype == DS_PMAX && t->c_ospeed == 19200))
385                 return (EINVAL);
386         /* and copy to tty */
387         tp->t_ispeed = t->c_ispeed;
388         tp->t_ospeed = t->c_ospeed;
389         tp->t_cflag = cflag;
390 
391 	dcaddr = (dcregs *)dcpdma[unit].p_addr;
392 
393 	/*
394 	 * Handle console cases specially.
395 	 */
396 	if (cn_tab.cn_screen) {
397 		if (unit == DCKBD_PORT) {
398 			dcaddr->dc_lpr = LPR_RXENAB | LPR_8_BIT_CHAR |
399 				LPR_B4800 | DCKBD_PORT;
400 			MachEmptyWriteBuffer();
401 			return (0);
402 		} else if (unit == DCMOUSE_PORT) {
403 			dcaddr->dc_lpr = LPR_RXENAB | LPR_B4800 | LPR_OPAR |
404 				LPR_PARENB | LPR_8_BIT_CHAR | DCMOUSE_PORT;
405 			MachEmptyWriteBuffer();
406 			return (0);
407 		}
408 	} else if (tp->t_dev == cn_tab.cn_dev) {
409 		dcaddr->dc_lpr = LPR_RXENAB | LPR_8_BIT_CHAR |
410 			LPR_B9600 | unit;
411 		MachEmptyWriteBuffer();
412 		return (0);
413 	}
414 	if (ospeed == 0) {
415 		(void) dcmctl(unit, 0, DMSET);	/* hang up line */
416 		return (0);
417 	}
418 	lpr = LPR_RXENAB | ospeed | (unit & 03);
419 	if ((cflag & CSIZE) == CS7)
420 		lpr |= LPR_7_BIT_CHAR;
421 	else
422 		lpr |= LPR_8_BIT_CHAR;
423 	if (cflag & PARENB)
424 		lpr |= LPR_PARENB;
425 	if (cflag & PARODD)
426 		lpr |= LPR_OPAR;
427 	if (cflag & CSTOPB)
428 		lpr |= LPR_2_STOP;
429 	dcaddr->dc_lpr = lpr;
430 	MachEmptyWriteBuffer();
431 	DELAY(10);
432 	return (0);
433 }
434 
435 /*
436  * Check for interrupts from all devices.
437  */
438 void
439 dcintr(unit)
440 	register int unit;
441 {
442 	register dcregs *dcaddr;
443 	register unsigned csr;
444 
445 	unit <<= 2;
446 	dcaddr = (dcregs *)dcpdma[unit].p_addr;
447 	while ((csr = dcaddr->dc_csr) & (CSR_RDONE | CSR_TRDY)) {
448 		if (csr & CSR_RDONE)
449 			dcrint(unit);
450 		if (csr & CSR_TRDY)
451 			dcxint(&dc_tty[unit + ((csr >> 8) & 03)]);
452 	}
453 }
454 
455 dcrint(unit)
456 	register int unit;
457 {
458 	register dcregs *dcaddr;
459 	register struct tty *tp;
460 	register int c, cc;
461 	register struct tty *tp0;
462 	int overrun = 0;
463 
464 	dcaddr = (dcregs *)dcpdma[unit].p_addr;
465 	tp0 = &dc_tty[unit];
466 	while ((c = dcaddr->dc_rbuf) < 0) {	/* char present */
467 		cc = c & 0xff;
468 		tp = tp0 + ((c >> 8) & 03);
469 		if ((c & RBUF_OERR) && overrun == 0) {
470 			log(LOG_WARNING, "dc%d,%d: silo overflow\n", unit >> 2,
471 				(c >> 8) & 03);
472 			overrun = 1;
473 		}
474 		/* the keyboard requires special translation */
475 		if (tp == &dc_tty[DCKBD_PORT] && cn_tab.cn_screen) {
476 #ifdef KADB
477 			if (cc == LK_DO) {
478 				spl0();
479 				kdbpanic();
480 				return;
481 			}
482 #endif
483 #ifdef DEBUG
484 			debugChar = cc;
485 #endif
486 			if (dcDivertXInput) {
487 				(*dcDivertXInput)(cc);
488 				return;
489 			}
490 			if ((cc = kbdMapChar(cc)) < 0)
491 				return;
492 		} else if (tp == &dc_tty[DCMOUSE_PORT] && dcMouseButtons) {
493 			register MouseReport *mrp;
494 			static MouseReport currentRep;
495 
496 			mrp = &currentRep;
497 			mrp->byteCount++;
498 			if (cc & MOUSE_START_FRAME) {
499 				/*
500 				 * The first mouse report byte (button state).
501 				 */
502 				mrp->state = cc;
503 				if (mrp->byteCount > 1)
504 					mrp->byteCount = 1;
505 			} else if (mrp->byteCount == 2) {
506 				/*
507 				 * The second mouse report byte (delta x).
508 				 */
509 				mrp->dx = cc;
510 			} else if (mrp->byteCount == 3) {
511 				/*
512 				 * The final mouse report byte (delta y).
513 				 */
514 				mrp->dy = cc;
515 				mrp->byteCount = 0;
516 				if (mrp->dx != 0 || mrp->dy != 0) {
517 					/*
518 					 * If the mouse moved,
519 					 * post a motion event.
520 					 */
521 					(*dcMouseEvent)(mrp);
522 				}
523 				(*dcMouseButtons)(mrp);
524 			}
525 			return;
526 		}
527 		if (!(tp->t_state & TS_ISOPEN)) {
528 			wakeup((caddr_t)&tp->t_rawq);
529 #ifdef PORTSELECTOR
530 			if (!(tp->t_state & TS_WOPEN))
531 #endif
532 				return;
533 		}
534 		if (c & RBUF_FERR)
535 			cc |= TTY_FE;
536 		if (c & RBUF_PERR)
537 			cc |= TTY_PE;
538 		(*linesw[tp->t_line].l_rint)(cc, tp);
539 	}
540 	DELAY(10);
541 }
542 
543 void
544 dcxint(tp)
545 	register struct tty *tp;
546 {
547 	register struct pdma *dp;
548 	register dcregs *dcaddr;
549 
550 	dp = &dcpdma[minor(tp->t_dev)];
551 	if (dp->p_mem < dp->p_end) {
552 		dcaddr = (dcregs *)dp->p_addr;
553 		dcaddr->dc_tdr = dc_brk[(tp - dc_tty) >> 2] | *dp->p_mem++;
554 		MachEmptyWriteBuffer();
555 		DELAY(10);
556 		return;
557 	}
558 	tp->t_state &= ~TS_BUSY;
559 	if (tp->t_state & TS_FLUSH)
560 		tp->t_state &= ~TS_FLUSH;
561 	else {
562 		ndflush(&tp->t_outq, dp->p_mem-tp->t_outq.c_cf);
563 		dp->p_end = dp->p_mem = tp->t_outq.c_cf;
564 	}
565 	if (tp->t_line)
566 		(*linesw[tp->t_line].l_start)(tp);
567 	else
568 		dcstart(tp);
569 	if (tp->t_outq.c_cc == 0 || !(tp->t_state & TS_BUSY)) {
570 		dcaddr = (dcregs *)dp->p_addr;
571 		dcaddr->dc_tcr &= ~(1 << (minor(tp->t_dev) & 03));
572 		MachEmptyWriteBuffer();
573 		DELAY(10);
574 	}
575 }
576 
577 void
578 dcstart(tp)
579 	register struct tty *tp;
580 {
581 	register struct pdma *dp;
582 	register dcregs *dcaddr;
583 	register int cc;
584 	int s;
585 
586 	dp = &dcpdma[minor(tp->t_dev)];
587 	dcaddr = (dcregs *)dp->p_addr;
588 	s = spltty();
589 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
590 		goto out;
591 	if (tp->t_outq.c_cc <= tp->t_lowat) {
592 		if (tp->t_state & TS_ASLEEP) {
593 			tp->t_state &= ~TS_ASLEEP;
594 			wakeup((caddr_t)&tp->t_outq);
595 		}
596 		selwakeup(&tp->t_wsel);
597 	}
598 	if (tp->t_outq.c_cc == 0)
599 		goto out;
600 	/* handle console specially */
601 	if (tp == &dc_tty[DCKBD_PORT] && cn_tab.cn_screen) {
602 		while (tp->t_outq.c_cc > 0) {
603 			cc = getc(&tp->t_outq) & 0x7f;
604 			cnputc(cc);
605 		}
606 		/*
607 		 * After we flush the output queue we may need to wake
608 		 * up the process that made the output.
609 		 */
610 		if (tp->t_outq.c_cc <= tp->t_lowat) {
611 			if (tp->t_state & TS_ASLEEP) {
612 				tp->t_state &= ~TS_ASLEEP;
613 				wakeup((caddr_t)&tp->t_outq);
614 			}
615 			selwakeup(&tp->t_wsel);
616 		}
617 		goto out;
618 	}
619 	if (tp->t_flags & (RAW|LITOUT))
620 		cc = ndqb(&tp->t_outq, 0);
621 	else {
622 		cc = ndqb(&tp->t_outq, 0200);
623 		if (cc == 0) {
624 			cc = getc(&tp->t_outq);
625 			timeout(ttrstrt, (void *)tp, (cc & 0x7f) + 6);
626 			tp->t_state |= TS_TIMEOUT;
627 			goto out;
628 		}
629 	}
630 	tp->t_state |= TS_BUSY;
631 	dp->p_end = dp->p_mem = tp->t_outq.c_cf;
632 	dp->p_end += cc;
633 	dcaddr->dc_tcr |= 1 << (minor(tp->t_dev) & 03);
634 	MachEmptyWriteBuffer();
635 out:
636 	splx(s);
637 }
638 
639 /*
640  * Stop output on a line.
641  */
642 /*ARGSUSED*/
643 dcstop(tp, flag)
644 	register struct tty *tp;
645 {
646 	register struct pdma *dp;
647 	register int s;
648 
649 	dp = &dcpdma[minor(tp->t_dev)];
650 	s = spltty();
651 	if (tp->t_state & TS_BUSY) {
652 		dp->p_end = dp->p_mem;
653 		if (!(tp->t_state & TS_TTSTOP))
654 			tp->t_state |= TS_FLUSH;
655 	}
656 	splx(s);
657 }
658 
659 dcmctl(dev, bits, how)
660 	dev_t dev;
661 	int bits, how;
662 {
663 	register dcregs *dcaddr;
664 	register int unit, mbits;
665 	int b, s;
666 	register int msr;
667 
668 	unit = minor(dev);
669 	b = 1 << (unit & 03);
670 	dcaddr = (dcregs *)dcpdma[unit].p_addr;
671 	s = spltty();
672 	/* only channel 2 has modem control (what about line 3?) */
673 	mbits = DML_DTR | DML_DSR | DML_CAR;
674 	switch (unit & 03) {
675 	case 2:
676 		mbits = 0;
677 		if (dcaddr->dc_tcr & TCR_DTR2)
678 			mbits |= DML_DTR;
679 		msr = dcaddr->dc_msr;
680 		if (msr & MSR_CD2)
681 			mbits |= DML_CAR;
682 		if (msr & MSR_DSR2) {
683 			if (pmax_boardtype == DS_PMAX)
684 				mbits |= DML_CAR | DML_DSR;
685 			else
686 				mbits |= DML_DSR;
687 		}
688 		break;
689 
690 	case 3:
691 		if (pmax_boardtype != DS_PMAX) {
692 			mbits = 0;
693 			if (dcaddr->dc_tcr & TCR_DTR3)
694 				mbits |= DML_DTR;
695 			msr = dcaddr->dc_msr;
696 			if (msr & MSR_CD3)
697 				mbits |= DML_CAR;
698 			if (msr & MSR_DSR3)
699 				mbits |= DML_DSR;
700 		}
701 	}
702 	switch (how) {
703 	case DMSET:
704 		mbits = bits;
705 		break;
706 
707 	case DMBIS:
708 		mbits |= bits;
709 		break;
710 
711 	case DMBIC:
712 		mbits &= ~bits;
713 		break;
714 
715 	case DMGET:
716 		(void) splx(s);
717 		return (mbits);
718 	}
719 	switch (unit & 03) {
720 	case 2:
721 		if (mbits & DML_DTR)
722 			dcaddr->dc_tcr |= TCR_DTR2;
723 		else
724 			dcaddr->dc_tcr &= ~TCR_DTR2;
725 		break;
726 
727 	case 3:
728 		if (pmax_boardtype != DS_PMAX) {
729 			if (mbits & DML_DTR)
730 				dcaddr->dc_tcr |= TCR_DTR3;
731 			else
732 				dcaddr->dc_tcr &= ~TCR_DTR3;
733 		}
734 	}
735 	if ((mbits & DML_DTR) && (dcsoftCAR[unit >> 2] & b))
736 		dc_tty[unit].t_state |= TS_CARR_ON;
737 	(void) splx(s);
738 	return (mbits);
739 }
740 
741 /*
742  * This is called by timeout() periodically.
743  * Check to see if modem status bits have changed.
744  */
745 void
746 dcscan(arg)
747 	void *arg;
748 {
749 	register dcregs *dcaddr;
750 	register struct tty *tp;
751 	register int i, bit, car;
752 	int s;
753 
754 	s = spltty();
755 	/* only channel 2 has modem control (what about line 3?) */
756 	dcaddr = (dcregs *)dcpdma[i = 2].p_addr;
757 	tp = &dc_tty[i];
758 	bit = TCR_DTR2;
759 	if (dcsoftCAR[i >> 2] & bit)
760 		car = 1;
761 	else
762 		car = dcaddr->dc_msr & MSR_DSR2;
763 	if (car) {
764 		/* carrier present */
765 		if (!(tp->t_state & TS_CARR_ON))
766 			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
767 	} else if ((tp->t_state & TS_CARR_ON) &&
768 	    (*linesw[tp->t_line].l_modem)(tp, 0) == 0)
769 		dcaddr->dc_tcr &= ~bit;
770 	splx(s);
771 	timeout(dcscan, (void *)0, hz);
772 }
773 
774 /*
775  * ----------------------------------------------------------------------------
776  *
777  * dcGetc --
778  *
779  *	Read a character from a serial line.
780  *
781  * Results:
782  *	A character read from the serial port.
783  *
784  * Side effects:
785  *	None.
786  *
787  * ----------------------------------------------------------------------------
788  */
789 int
790 dcGetc(dev)
791 	dev_t dev;
792 {
793 	register dcregs *dcaddr;
794 	register int c;
795 	int s;
796 
797 	dcaddr = (dcregs *)dcpdma[minor(dev)].p_addr;
798 	if (!dcaddr)
799 		return (0);
800 	s = spltty();
801 	for (;;) {
802 		if (!(dcaddr->dc_csr & CSR_RDONE))
803 			continue;
804 		c = dcaddr->dc_rbuf;
805 		DELAY(10);
806 		if (((c >> 8) & 03) == (minor(dev) & 03))
807 			break;
808 	}
809 	splx(s);
810 	return (c & 0xff);
811 }
812 
813 /*
814  * Send a char on a port, non interrupt driven.
815  */
816 void
817 dcPutc(dev, c)
818 	dev_t dev;
819 	int c;
820 {
821 	register dcregs *dcaddr;
822 	register u_short tcr;
823 	register int timeout;
824 	int s, line;
825 
826 	s = spltty();
827 
828 	dcaddr = (dcregs *)dcpdma[minor(dev)].p_addr;
829 	tcr = dcaddr->dc_tcr;
830 	dcaddr->dc_tcr = tcr | (1 << minor(dev));
831 	MachEmptyWriteBuffer();
832 	DELAY(10);
833 	while (1) {
834 		/*
835 		 * Wait for transmitter to be not busy.
836 		 */
837 		timeout = 1000000;
838 		while (!(dcaddr->dc_csr & CSR_TRDY) && timeout > 0)
839 			timeout--;
840 		if (timeout == 0) {
841 			printf("dcPutc: timeout waiting for CSR_TRDY\n");
842 			break;
843 		}
844 		line = (dcaddr->dc_csr >> 8) & 3;
845 		/*
846 		 * Check to be sure its the right port.
847 		 */
848 		if (line != minor(dev)) {
849 			tcr |= 1 << line;
850 			dcaddr->dc_tcr &= ~(1 << line);
851 			MachEmptyWriteBuffer();
852 			DELAY(10);
853 			continue;
854 		}
855 		/*
856 		 * Start sending the character.
857 		 */
858 		dcaddr->dc_tdr = dc_brk[0] | (c & 0xff);
859 		MachEmptyWriteBuffer();
860 		DELAY(10);
861 		/*
862 		 * Wait for character to be sent.
863 		 */
864 		while (1) {
865 			/*
866 			 * cc -O bug: this code produces and infinite loop!
867 			 * while (!(dcaddr->dc_csr & CSR_TRDY))
868 			 *	;
869 			 */
870 			timeout = 1000000;
871 			while (!(dcaddr->dc_csr & CSR_TRDY) && timeout > 0)
872 				timeout--;
873 			line = (dcaddr->dc_csr >> 8) & 3;
874 			if (line != minor(dev)) {
875 				tcr |= 1 << line;
876 				dcaddr->dc_tcr &= ~(1 << line);
877 				MachEmptyWriteBuffer();
878 				DELAY(10);
879 				continue;
880 			}
881 			dcaddr->dc_tcr &= ~(1 << minor(dev));
882 			MachEmptyWriteBuffer();
883 			DELAY(10);
884 			break;
885 		}
886 		break;
887 	}
888 	/*
889 	 * Enable interrupts for other lines which became ready.
890 	 */
891 	if (tcr & 0xF) {
892 		dcaddr->dc_tcr = tcr;
893 		MachEmptyWriteBuffer();
894 		DELAY(10);
895 	}
896 
897 	splx(s);
898 }
899 #endif /* NDC */
900