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