xref: /openbsd/sys/arch/luna88k/dev/siotty.c (revision 09467b48)
1 /* $OpenBSD: siotty.c,v 1.23 2019/02/25 11:29:30 jca Exp $ */
2 /* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */
3 
4 /*-
5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Tohru Nishimura.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/conf.h>
37 #include <sys/ioctl.h>
38 #include <sys/malloc.h>
39 #include <sys/proc.h>
40 #include <sys/tty.h>
41 #include <sys/uio.h>
42 #include <sys/fcntl.h>
43 #include <dev/cons.h>
44 
45 #include <machine/board.h>
46 #include <machine/cpu.h>
47 
48 #include <luna88k/dev/sioreg.h>
49 #include <luna88k/dev/siovar.h>
50 
51 #define	TIOCM_BREAK 01000 /* non standard use */
52 
53 static const u_int8_t ch0_regs[6] = {
54 	WR0_RSTINT,				/* reset E/S interrupt */
55 	WR1_RXALLS | WR1_TXENBL,		/* Rx per char, Tx */
56 	0,					/* */
57 	WR3_RX8BIT | WR3_RXENBL,		/* Rx */
58 	WR4_BAUD96 | WR4_STOP1,			/* Tx/Rx */
59 	WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */
60 };
61 
62 static const struct speedtab siospeedtab[] = {
63 	{ 2400,	WR4_BAUD24, },
64 	{ 4800,	WR4_BAUD48, },
65 	{ 9600,	WR4_BAUD96, },
66 	{ -1,	0, },
67 };
68 
69 struct siotty_softc {
70 	struct device	sc_dev;
71 	struct tty	*sc_tty;
72 	struct sioreg	*sc_ctl;
73 	u_int 		sc_flags;
74 	u_int8_t	sc_wr[6];
75 	void		*sc_si;		/* software interrupt handler */
76 	u_int		sc_hwflags;
77 #define	SIOTTY_HW_CONSOLE	0x0001
78 
79 	u_int8_t	*sc_rbuf;
80 	u_int8_t	*sc_rbufend;
81 	u_int8_t * volatile sc_rbget;
82 	u_int8_t * volatile sc_rbput;
83 	volatile u_int	sc_rbavail;
84 
85 	u_int8_t	*sc_tba;
86 	u_int		sc_tbc;
87 
88 	bool		sc_rx_ready;
89 	bool		sc_tx_busy;
90 	bool		sc_tx_done;
91 };
92 
93 #define	SIOTTY_RING_SIZE	2048
94 u_int siotty_rbuf_size = SIOTTY_RING_SIZE;
95 
96 cdev_decl(sio);
97 void siostart(struct tty *);
98 int  sioparam(struct tty *, struct termios *);
99 void siottyintr(void *);
100 void siottysoft(void *);
101 void siotty_rxsoft(struct siotty_softc *, struct tty *);
102 void siotty_txsoft(struct siotty_softc *, struct tty *);
103 int  siomctl(struct siotty_softc *, int, int);
104 
105 int  siotty_match(struct device *, void *, void *);
106 void siotty_attach(struct device *, struct device *, void *);
107 
108 const struct cfattach siotty_ca = {
109 	sizeof(struct siotty_softc), siotty_match, siotty_attach
110 };
111 
112 struct cfdriver siotty_cd = {
113 	NULL, "siotty", DV_TTY
114 };
115 
116 int
117 siotty_match(struct device *parent, void *cf, void *aux)
118 {
119 	struct sio_attach_args *args = aux;
120 
121 	if (args->channel != 0) /* XXX allow tty on Ch.B XXX */
122 		return 0;
123 	return 1;
124 }
125 
126 void
127 siotty_attach(struct device *parent, struct device *self, void *aux)
128 {
129 	struct sio_softc *siosc = (void *)parent;
130 	struct siotty_softc *sc = (void *)self;
131 	struct sio_attach_args *args = aux;
132 	int channel;
133 	struct tty *tp;
134 
135 	channel = args->channel;
136 	sc->sc_ctl = &siosc->sc_ctl[channel];
137 	memcpy(sc->sc_wr, ch0_regs, sizeof(ch0_regs));
138 	siosc->sc_intrhand[channel].ih_func = siottyintr;
139 	siosc->sc_intrhand[channel].ih_arg = sc;
140 	if (args->hwflags == 1)
141 		sc->sc_hwflags |= SIOTTY_HW_CONSOLE;
142 
143 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE) {
144 		printf(" (console)");
145 		sc->sc_flags = TIOCFLAG_SOFTCAR;
146 	} else {
147 		setsioreg(sc->sc_ctl, WR0, WR0_CHANRST);
148 		setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1);
149 		setsioreg(sc->sc_ctl, WR2B, 0);
150 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
151 		setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
152 		setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
153 		setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
154 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
155 	}
156 	setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */
157 
158 	printf("\n");
159 
160 	sc->sc_rbuf = malloc(siotty_rbuf_size * 2, M_DEVBUF, M_NOWAIT);
161 	if (sc->sc_rbuf == NULL) {
162 		printf("%s: unable to allocate ring buffer\n",
163 		    (sc->sc_dev).dv_xname);
164 		return;
165 	}
166 	sc->sc_rbufend = sc->sc_rbuf + (siotty_rbuf_size * 2);
167 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
168 	sc->sc_rbavail = siotty_rbuf_size;
169 
170 	tp = ttymalloc(0);
171 	tp->t_oproc = siostart;
172 	tp->t_param = sioparam;
173 	tp->t_hwiflow = NULL /* XXX siohwiflow XXX */;
174 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE)
175 		tp->t_dev = cn_tab->cn_dev;
176 	sc->sc_tty = tp;
177 
178 	sc->sc_si = softintr_establish(IPL_SOFTTTY, siottysoft, sc);
179 }
180 
181 /*--------------------  low level routine --------------------*/
182 
183 void
184 siottyintr(void *arg)
185 {
186 	struct siotty_softc *sc;
187 	struct sioreg *sio;
188 	u_int8_t *put, *end;
189 	u_int8_t c;
190 	u_int16_t rr;
191 	int cc;
192 
193 	sc = arg;
194 	end = sc->sc_rbufend;
195 	put = sc->sc_rbput;
196 	cc = sc->sc_rbavail;
197 
198 	sio = sc->sc_ctl;
199 	rr = getsiocsr(sio);
200 	if (rr & RR_RXRDY) {
201 		do {
202 			if (cc > 0) {
203 				c = sio->sio_data;
204 				put[0] = c;
205 				put[1] = rr & 0xff;
206 				put += 2;
207 				if (put >= end)
208 					put = sc->sc_rbuf;
209 				cc--;
210 			}
211 			if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY))
212 				sio->sio_cmd = WR0_ERRRST;
213 
214 			sc->sc_rbput = put;
215 			sc->sc_rbavail = cc;
216 			sc->sc_rx_ready = true;
217 		} while ((rr = getsiocsr(sio)) & RR_RXRDY);
218 	}
219 	if (rr & RR_TXRDY) {
220 		sio->sio_cmd = WR0_RSTPEND;
221 		if (sc->sc_tbc > 0) {
222 			sio->sio_data = *sc->sc_tba;
223 			sc->sc_tba++;
224 			sc->sc_tbc--;
225 		} else {
226 			if (sc->sc_tx_busy) {
227 				sc->sc_tx_busy = false;
228 				sc->sc_tx_done = true;
229 			}
230 		}
231 	}
232 	softintr_schedule(sc->sc_si);
233 }
234 
235 void
236 siottysoft(void *arg)
237 {
238 	struct siotty_softc *sc;
239 	struct tty *tp;
240 
241 	sc = arg;
242 	tp = sc->sc_tty;
243 
244 	if (sc->sc_rx_ready) {
245 		sc->sc_rx_ready = false;
246 		siotty_rxsoft(sc, tp);
247 	}
248 	if (sc->sc_tx_done) {
249 		sc->sc_tx_done = false;
250 		siotty_txsoft(sc, tp);
251 	}
252 }
253 
254 void
255 siotty_rxsoft(struct siotty_softc *sc, struct tty *tp)
256 {
257 	uint8_t *get, *end;
258 	u_int cc, scc;
259 	unsigned int code;
260 	uint8_t stat;
261 	int s;
262 
263 	end = sc->sc_rbufend;
264 	get = sc->sc_rbget;
265 	scc = cc = siotty_rbuf_size - sc->sc_rbavail;
266 
267 	if (cc == siotty_rbuf_size) {
268 		printf("%s: rx buffer overflow\n", (sc->sc_dev).dv_xname);
269 	}
270 
271 	while (cc > 0) {
272 		code = get[0];
273 		stat = get[1];
274 		if ((stat & RR_FRAMING) != 0)
275 			code |= TTY_FE;
276 		else if ((stat & RR_PARITY) != 0)
277 			code |= TTY_PE;
278 
279 		(*linesw[tp->t_line].l_rint)(code, tp);
280 		get += 2;
281 		if (get >= end)
282 			get = sc->sc_rbuf;
283 		cc--;
284 	}
285 
286 	if (cc != scc) {
287 		s = spltty();
288 		sc->sc_rbget = get;
289 		sc->sc_rbavail += scc - cc;
290 		splx(s);
291 	}
292 }
293 
294 void
295 siotty_txsoft(struct siotty_softc *sc, struct tty *tp)
296 {
297 
298 	tp->t_state &= ~TS_BUSY;
299 	if ((tp->t_state & TS_FLUSH) != 0)
300 		tp->t_state &= ~TS_FLUSH;
301 	else
302 		ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
303 	(*linesw[tp->t_line].l_start)(tp);
304 }
305 
306 void
307 siostart(struct tty *tp)
308 {
309 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
310 	int s;
311 	u_int8_t *tba;
312 	int tbc;
313 
314 	s = spltty();
315 	if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP))
316 		goto out;
317 	ttwakeupwr(tp);
318 	if (tp->t_outq.c_cc == 0)
319 		goto out;
320 
321 	tp->t_state |= TS_BUSY;
322 
323 	tba = tp->t_outq.c_cf;
324 	tbc = ndqb(&tp->t_outq, 0);
325 
326 	sc->sc_tba = tba;
327 	sc->sc_tbc = tbc;
328 	sc->sc_tx_busy = true;
329 
330 	sc->sc_ctl->sio_data = *sc->sc_tba;
331 	sc->sc_tba++;
332 	sc->sc_tbc--;
333 out:
334 	splx(s);
335 }
336 
337 int
338 siostop(struct tty *tp, int flag)
339 {
340 	int s;
341 
342 	s = spltty();
343 	if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) {
344 		/*
345 		 * Device is transmitting; must stop it.
346 		 */
347 		tp->t_state |= TS_FLUSH;
348 	}
349 	splx(s);
350 	return (0);
351 }
352 
353 int
354 sioparam(struct tty *tp, struct termios *t)
355 {
356 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
357 	int wr4, s;
358 
359 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
360 		return EINVAL;
361 	wr4 = ttspeedtab(t->c_ospeed, siospeedtab);
362 	if (wr4 < 0)
363 		return EINVAL;
364 
365 	if (sc->sc_flags & TIOCFLAG_SOFTCAR) {
366 		t->c_cflag |= CLOCAL;
367 		t->c_cflag &= ~HUPCL;
368 	}
369 	if (sc->sc_flags & TIOCFLAG_CLOCAL)
370 		t->c_cflag |= CLOCAL;
371 
372 	/*
373 	 * If there were no changes, don't do anything.  This avoids dropping
374 	 * input and improves performance when all we did was frob things like
375 	 * VMIN and VTIME.
376 	 */
377 	if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag)
378 		return 0;
379 
380 	tp->t_ispeed = t->c_ispeed;
381 	tp->t_ospeed = t->c_ospeed;
382 	tp->t_cflag = t->c_cflag;
383 
384 	sc->sc_wr[WR3] &= 0x3f;
385 	sc->sc_wr[WR5] &= 0x9f;
386 	switch (tp->t_cflag & CSIZE) {
387 	case CS7:
388 		sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT;
389 		break;
390 	case CS8:
391 		sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT;
392 		break;
393 	}
394 	if (tp->t_cflag & PARENB) {
395 		wr4 |= WR4_PARENAB;
396 		if ((tp->t_cflag & PARODD) == 0)
397 			wr4 |= WR4_EPARITY;
398 	}
399 	wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1;
400 	sc->sc_wr[WR4] = wr4;
401 
402 	s = spltty();
403 	setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
404 	setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
405 	setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
406 	splx(s);
407 
408 	return 0;
409 }
410 
411 int
412 siomctl(struct siotty_softc *sc, int control, int op)
413 {
414 	int val, s, wr5, rr;
415 
416 	val = 0;
417 	if (control & TIOCM_BREAK)
418 		val |= WR5_BREAK;
419 	if (control & TIOCM_DTR)
420 		val |= WR5_DTR;
421 	if (control & TIOCM_RTS)
422 		val |= WR5_RTS;
423 	s = spltty();
424 	wr5 = sc->sc_wr[WR5];
425 	switch (op) {
426 	case DMSET:
427 		wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS);
428 		/* FALLTHROUGH */
429 	case DMBIS:
430 		wr5 |= val;
431 		break;
432 	case DMBIC:
433 		wr5 &= ~val;
434 		break;
435 	case DMGET:
436 		val = 0;
437 		rr = getsiocsr(sc->sc_ctl);
438 		if (wr5 & WR5_DTR)
439 			val |= TIOCM_DTR;
440 		if (wr5 & WR5_RTS)
441 			val |= TIOCM_RTS;
442 		if (rr & RR_CTS)
443 			val |= TIOCM_CTS;
444 		if (rr & RR_DCD)
445 			val |= TIOCM_CD;
446 		goto done;
447 	}
448 	sc->sc_wr[WR5] = wr5;
449 	setsioreg(sc->sc_ctl, WR5, wr5);
450 	val = 0;
451 done:
452 	splx(s);
453 	return val;
454 }
455 
456 /*--------------------  cdevsw[] interface --------------------*/
457 
458 int
459 sioopen(dev_t dev, int flag, int mode, struct proc *p)
460 {
461 	struct siotty_softc *sc;
462 	struct tty *tp;
463 	int error;
464 	int s;
465 
466 	if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL)
467 		return ENXIO;
468 
469 	tp = sc->sc_tty;
470 
471 	if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE)
472 	    && suser(p) != 0)
473 		return EBUSY;
474 
475 	if ((tp->t_state & TS_ISOPEN) == 0) {
476 		struct termios t;
477 
478 		tp->t_dev = dev;
479 		t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
480 		t.c_cflag = TTYDEF_CFLAG;
481 		tp->t_ospeed = 0; /* force register update */
482 		(void)sioparam(tp, &t);
483 		tp->t_iflag = TTYDEF_IFLAG;
484 		tp->t_oflag = TTYDEF_OFLAG;
485 		tp->t_lflag = TTYDEF_LFLAG;
486 		ttychars(tp);
487 		ttsetwater(tp);
488 		/* raise RTS and DTR here; but, DTR lead is not wired */
489 		/* then check DCD condition; but, DCD lead is not wired */
490 #if 0
491 		if ((sc->sc_flags & TIOCFLAG_SOFTCAR)
492 		    || (tp->t_cflag & MDMBUF)
493 		    || (getsiocsr(sc->sc_ctl) & RR_DCD))
494 			tp->t_state |= TS_CARR_ON;
495 		else
496 			tp->t_state &= ~TS_CARR_ON;
497 #else
498 		tp->t_state |= TS_CARR_ON; /* assume detected all the time */
499 #endif
500 		s = spltty();
501 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
502 		sc->sc_rbavail = siotty_rbuf_size;
503 		splx(s);
504 	}
505 
506 	error = ttyopen(dev, tp, p);
507 	if (error > 0)
508 		return error;
509 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
510 }
511 
512 int
513 sioclose(dev_t dev, int flag, int mode, struct proc *p)
514 {
515 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
516 	struct tty *tp = sc->sc_tty;
517 	int s;
518 
519 	(*linesw[tp->t_line].l_close)(tp, flag, p);
520 
521 	s = spltty();
522 	siomctl(sc, TIOCM_BREAK, DMBIC);
523 #if 0 /* because unable to feed DTR signal */
524 	if ((tp->t_cflag & HUPCL)
525 	    || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) {
526 		siomctl(sc, TIOCM_DTR, DMBIC);
527 		/* Yield CPU time to others for 1 second, then ... */
528 		siomctl(sc, TIOCM_DTR, DMBIS);
529 	}
530 #endif
531 	splx(s);
532 	return ttyclose(tp);
533 }
534 
535 int
536 sioread(dev_t dev, struct uio *uio, int flag)
537 {
538 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
539 	struct tty *tp = sc->sc_tty;
540 
541 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
542 }
543 
544 int
545 siowrite(dev_t dev, struct uio *uio, int flag)
546 {
547 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
548 	struct tty *tp = sc->sc_tty;
549 
550 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
551 }
552 
553 int
554 sioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
555 {
556 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
557 	struct tty *tp = sc->sc_tty;
558 	int error;
559 
560 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
561 	if (error >= 0)
562 		return error;
563 
564 	error = ttioctl(tp, cmd, data, flag, p);
565 	if (error >= 0)
566 		return error;
567 
568 	/* the last resort for TIOC ioctl traversing */
569 	switch (cmd) {
570 	case TIOCSBRK: /* Set the hardware into BREAK condition */
571 		siomctl(sc, TIOCM_BREAK, DMBIS);
572 		break;
573 	case TIOCCBRK: /* Clear the hardware BREAK condition */
574 		siomctl(sc, TIOCM_BREAK, DMBIC);
575 		break;
576 	case TIOCSDTR: /* Assert DTR signal */
577 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS);
578 		break;
579 	case TIOCCDTR: /* Clear DTR signal */
580 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC);
581 		break;
582 	case TIOCMSET: /* Set modem state replacing current one */
583 		siomctl(sc, *(int *)data, DMSET);
584 		break;
585 	case TIOCMGET: /* Return current modem state */
586 		*(int *)data = siomctl(sc, 0, DMGET);
587 		break;
588 	case TIOCMBIS: /* Set individual bits of modem state */
589 		siomctl(sc, *(int *)data, DMBIS);
590 		break;
591 	case TIOCMBIC: /* Clear individual bits of modem state */
592 		siomctl(sc, *(int *)data, DMBIC);
593 		break;
594 	case TIOCSFLAGS: /* Instruct how serial port behaves */
595 		error = suser(p);
596 		if (error != 0)
597 			return EPERM;
598 		sc->sc_flags = *(int *)data;
599 		break;
600 	case TIOCGFLAGS: /* Return current serial port state */
601 		*(int *)data = sc->sc_flags;
602 		break;
603 	default:
604 		return ENOTTY;
605 	}
606 	return 0;
607 }
608 
609 /* ARSGUSED */
610 struct tty *
611 siotty(dev_t dev)
612 {
613 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
614 
615 	return sc->sc_tty;
616 }
617 
618 /*--------------------  miscellaneous routines --------------------*/
619 
620 /* EXPORT */ void
621 setsioreg(struct sioreg *sio, int regno, int val)
622 {
623 	if (regno != 0)
624 		sio->sio_cmd = regno;	/* DELAY(); */
625 	sio->sio_cmd = val;		/* DELAY(); */
626 }
627 
628 /* EXPORT */ int
629 getsiocsr(struct sioreg *sio)
630 {
631 	int val;
632 
633 	val = sio->sio_stat << 8;	/* DELAY(); */
634 	sio->sio_cmd = 1;		/* DELAY(); */
635 	val |= sio->sio_stat;		/* DELAY(); */
636 	return val;
637 }
638 
639 /*---------------------  console interface ----------------------*/
640 
641 void syscnattach(int);
642 int  syscngetc(dev_t);
643 void syscnputc(dev_t, int);
644 
645 struct consdev syscons = {
646 	NULL,
647 	NULL,
648 	syscngetc,
649 	syscnputc,
650 	nullcnpollc,
651 	NULL,
652 	NODEV,
653 	CN_HIGHPRI,
654 };
655 
656 /* EXPORT */ void
657 syscnattach(int channel)
658 {
659 /*
660  * Channel A is immediately initialized with 9600N1 right after cold
661  * boot/reset/poweron.  ROM monitor emits one line message on CH.A.
662  */
663 	struct sioreg *sio;
664 	sio = (struct sioreg *)OBIO_SIO + channel;
665 
666 	syscons.cn_dev = makedev(12, channel);
667 	cn_tab = &syscons;
668 
669 	setsioreg(sio, WR0, WR0_CHANRST);
670 	setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1);
671 	setsioreg(sio, WR2B, 0);
672 	setsioreg(sio, WR0, ch0_regs[WR0]);
673 	setsioreg(sio, WR4, ch0_regs[WR4]);
674 	setsioreg(sio, WR3, ch0_regs[WR3]);
675 	setsioreg(sio, WR5, ch0_regs[WR5]);
676 	setsioreg(sio, WR0, ch0_regs[WR0]);
677 }
678 
679 /* EXPORT */ int
680 syscngetc(dev_t dev)
681 {
682 	struct sioreg *sio;
683 	int s, c;
684 
685 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
686 	s = splhigh();
687 	while ((getsiocsr(sio) & RR_RXRDY) == 0)
688 		;
689 	c = sio->sio_data;
690 	splx(s);
691 
692 	return c;
693 }
694 
695 /* EXPORT */ void
696 syscnputc(dev_t dev, int c)
697 {
698 	struct sioreg *sio;
699 	int s;
700 
701 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
702 	s = splhigh();
703 	while ((getsiocsr(sio) & RR_TXRDY) == 0)
704 		;
705 	sio->sio_cmd = WR0_RSTPEND;
706 	sio->sio_data = c;
707 	splx(s);
708 }
709