siotty.c revision 1.41 1 /* $NetBSD: siotty.c,v 1.41 2014/07/18 18:02:08 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.41 2014/07/18 18:02:08 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(void *);
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 .d_open = sioopen,
132 .d_close = sioclose,
133 .d_read = sioread,
134 .d_write = siowrite,
135 .d_ioctl = sioioctl,
136 .d_stop = siostop,
137 .d_tty = siotty,
138 .d_poll = siopoll,
139 .d_mmap = nommap,
140 .d_kqfilter = ttykqfilter,
141 .d_flag = D_TTY
142 };
143
144 static int
145 siotty_match(device_t parent, cfdata_t cf, void *aux)
146 {
147 struct sio_attach_args *args = aux;
148
149 if (args->channel != 0) /* XXX allow tty on Ch.B XXX */
150 return 0;
151 return 1;
152 }
153
154 static void
155 siotty_attach(device_t parent, device_t self, void *aux)
156 {
157 struct sio_softc *siosc = device_private(parent);
158 struct siotty_softc *sc = device_private(self);
159 struct sio_attach_args *args = aux;
160 int channel;
161 struct tty *tp;
162
163 sc->sc_dev = self;
164 channel = args->channel;
165 sc->sc_ctl = &siosc->sc_ctl[channel];
166 memcpy(sc->sc_wr, ch0_regs, sizeof(ch0_regs));
167 siosc->sc_intrhand[channel].ih_func = siottyintr;
168 siosc->sc_intrhand[channel].ih_arg = sc;
169 if (args->hwflags == 1)
170 sc->sc_hwflags |= SIOTTY_HW_CONSOLE;
171
172 if ((sc->sc_hwflags & SIOTTY_HW_CONSOLE) != 0) {
173 aprint_normal(" (console)");
174 sc->sc_flags = TIOCFLAG_SOFTCAR;
175 } else {
176 setsioreg(sc->sc_ctl, WR0, WR0_CHANRST);
177 setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1);
178 setsioreg(sc->sc_ctl, WR2B, 0);
179 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
180 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
181 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
182 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
183 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
184 }
185 setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */
186
187 aprint_normal("\n");
188
189 sc->sc_rbuf = kmem_alloc(siotty_rbuf_size * 2, KM_NOSLEEP);
190 if (sc->sc_rbuf == NULL) {
191 aprint_error_dev(self, "unable to allocate ring buffer\n");
192 return;
193 }
194 sc->sc_rbufend = sc->sc_rbuf + (siotty_rbuf_size * 2);
195 sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
196 sc->sc_rbavail = siotty_rbuf_size;
197
198 tp = tty_alloc();
199 tp->t_oproc = siostart;
200 tp->t_param = sioparam;
201 tp->t_hwiflow = NULL /* XXX siohwiflow XXX */;
202 if ((sc->sc_hwflags & SIOTTY_HW_CONSOLE) != 0)
203 tp->t_dev = cn_tab->cn_dev;
204 sc->sc_tty = tp;
205
206 tty_attach(tp);
207
208 sc->sc_si = softint_establish(SOFTINT_SERIAL, siottysoft, sc);
209 }
210
211 /*-------------------- low level routine --------------------*/
212
213 static void
214 siottyintr(void *arg)
215 {
216 struct siotty_softc *sc;
217 struct sioreg *sio;
218 uint8_t *put, *end;
219 uint8_t c;
220 uint16_t rr;
221 int cc;
222
223 sc = arg;
224 end = sc->sc_rbufend;
225 put = sc->sc_rbput;
226 cc = sc->sc_rbavail;
227
228 sio = sc->sc_ctl;
229 rr = getsiocsr(sio);
230 if ((rr & RR_BREAK) != 0) {
231 sio->sio_cmd = WR0_RSTINT;
232 cn_check_magic(sc->sc_tty->t_dev, CNC_BREAK, siotty_cnm_state);
233 }
234 if ((rr & RR_RXRDY) != 0) {
235 do {
236 if (cc > 0) {
237 c = sio->sio_data;
238 cn_check_magic(sc->sc_tty->t_dev, c,
239 siotty_cnm_state);
240 put[0] = c;
241 put[1] = rr & 0xff;
242 put += 2;
243 if (put >= end)
244 put = sc->sc_rbuf;
245 cc--;
246 }
247 if ((rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) != 0)
248 sio->sio_cmd = WR0_ERRRST;
249
250 sc->sc_rbput = put;
251 sc->sc_rbavail = cc;
252 sc->sc_rx_ready = true;
253 } while (((rr = getsiocsr(sio)) & RR_RXRDY) != 0);
254 }
255 if ((rr & RR_TXRDY) != 0) {
256 sio->sio_cmd = WR0_RSTPEND;
257 if (sc->sc_tbc > 0) {
258 sio->sio_data = *sc->sc_tba;
259 sc->sc_tba++;
260 sc->sc_tbc--;
261 } else {
262 if (sc->sc_tx_busy) {
263 sc->sc_tx_busy = false;
264 sc->sc_tx_done = true;
265 }
266 }
267 }
268 softint_schedule(sc->sc_si);
269 }
270
271 static void
272 siottysoft(void *arg)
273 {
274 struct siotty_softc *sc;
275 struct tty *tp;
276
277 sc = arg;
278 tp = sc->sc_tty;
279
280 if (sc->sc_rx_ready) {
281 sc->sc_rx_ready = false;
282 siotty_rxsoft(sc, tp);
283 }
284 if (sc->sc_tx_done) {
285 sc->sc_tx_done = false;
286 siotty_txsoft(sc, tp);
287 }
288 }
289
290 static void
291 siotty_rxsoft(struct siotty_softc *sc, struct tty *tp)
292 {
293 uint8_t *get, *end;
294 u_int cc, scc;
295 unsigned int code;
296 uint8_t stat;
297 int s;
298
299 end = sc->sc_rbufend;
300 get = sc->sc_rbget;
301 scc = cc = siotty_rbuf_size - sc->sc_rbavail;
302
303 if (cc == siotty_rbuf_size) {
304 printf("%s: rx buffer overflow\n", device_xname(sc->sc_dev));
305 }
306
307 while (cc > 0) {
308 code = get[0];
309 stat = get[1];
310 if ((stat & RR_FRAMING) != 0)
311 code |= TTY_FE;
312 else if ((stat & RR_PARITY) != 0)
313 code |= TTY_PE;
314
315 (*tp->t_linesw->l_rint)(code, tp);
316 get += 2;
317 if (get >= end)
318 get = sc->sc_rbuf;
319 cc--;
320 }
321
322 if (cc != scc) {
323 s = splserial();
324 sc->sc_rbget = get;
325 sc->sc_rbavail += scc - cc;
326 splx(s);
327 }
328 }
329
330 static void
331 siotty_txsoft(struct siotty_softc *sc, struct tty *tp)
332 {
333
334 tp->t_state &= ~TS_BUSY;
335 if ((tp->t_state & TS_FLUSH) != 0)
336 tp->t_state &= ~TS_FLUSH;
337 else
338 ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
339 (*tp->t_linesw->l_start)(tp);
340 }
341
342 static void
343 siostart(struct tty *tp)
344 {
345 struct siotty_softc *sc;
346 int s;
347 uint8_t *tba;
348 int tbc;
349
350 sc = device_lookup_private(&siotty_cd, minor(tp->t_dev));
351 s = splserial();
352 if ((tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP)) != 0)
353 goto out;
354 if (!ttypull(tp))
355 goto out;
356 tp->t_state |= TS_BUSY;
357
358 tba = tp->t_outq.c_cf;
359 tbc = ndqb(&tp->t_outq, 0);
360
361 sc->sc_tba = tba;
362 sc->sc_tbc = tbc;
363 sc->sc_tx_busy = true;
364
365 sc->sc_ctl->sio_data = *sc->sc_tba;
366 sc->sc_tba++;
367 sc->sc_tbc--;
368 out:
369 splx(s);
370 }
371
372 void
373 siostop(struct tty *tp, int flag)
374 {
375 int s;
376
377 s = splserial();
378 if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) {
379 /*
380 * Device is transmitting; must stop it.
381 */
382 tp->t_state |= TS_FLUSH;
383 }
384 splx(s);
385 }
386
387 static int
388 sioparam(struct tty *tp, struct termios *t)
389 {
390 struct siotty_softc *sc;
391 int wr4, s;
392
393 sc = device_lookup_private(&siotty_cd, minor(tp->t_dev));
394 if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
395 return EINVAL;
396 wr4 = ttspeedtab(t->c_ospeed, siospeedtab);
397 if (wr4 < 0)
398 return EINVAL;
399
400 if ((sc->sc_flags & TIOCFLAG_SOFTCAR) != 0) {
401 t->c_cflag |= CLOCAL;
402 t->c_cflag &= ~HUPCL;
403 }
404 if ((sc->sc_flags & TIOCFLAG_CLOCAL) != 0)
405 t->c_cflag |= CLOCAL;
406
407 /*
408 * If there were no changes, don't do anything. This avoids dropping
409 * input and improves performance when all we did was frob things like
410 * VMIN and VTIME.
411 */
412 if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag)
413 return 0;
414
415 tp->t_ispeed = t->c_ispeed;
416 tp->t_ospeed = t->c_ospeed;
417 tp->t_cflag = t->c_cflag;
418
419 sc->sc_wr[WR3] &= 0x3f;
420 sc->sc_wr[WR5] &= 0x9f;
421 switch (tp->t_cflag & CSIZE) {
422 case CS7:
423 sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT;
424 break;
425 case CS8:
426 sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT;
427 break;
428 }
429 if ((tp->t_cflag & PARENB) != 0) {
430 wr4 |= WR4_PARENAB;
431 if ((tp->t_cflag & PARODD) == 0)
432 wr4 |= WR4_EPARITY;
433 }
434 wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1;
435 sc->sc_wr[WR4] = wr4;
436
437 s = splserial();
438 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
439 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
440 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
441 splx(s);
442
443 return 0;
444 }
445
446 static int
447 siomctl(struct siotty_softc *sc, int control, int op)
448 {
449 int val, s;
450 uint8_t wr5;
451 uint16_t rr;
452
453 val = 0;
454 if ((control & TIOCM_BREAK) != 0)
455 val |= WR5_BREAK;
456 if ((control & TIOCM_DTR) != 0)
457 val |= WR5_DTR;
458 if ((control & TIOCM_RTS) != 0)
459 val |= WR5_RTS;
460 s = splserial();
461 wr5 = sc->sc_wr[WR5];
462 switch (op) {
463 case DMSET:
464 wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS);
465 /* FALLTHRU */
466 case DMBIS:
467 wr5 |= val;
468 break;
469 case DMBIC:
470 wr5 &= ~val;
471 break;
472 case DMGET:
473 val = 0;
474 rr = getsiocsr(sc->sc_ctl);
475 if ((wr5 & WR5_DTR) != 0)
476 val |= TIOCM_DTR;
477 if ((wr5 & WR5_RTS) != 0)
478 val |= TIOCM_RTS;
479 if ((rr & RR_CTS) != 0)
480 val |= TIOCM_CTS;
481 if ((rr & RR_DCD) != 0)
482 val |= TIOCM_CD;
483 goto done;
484 }
485 sc->sc_wr[WR5] = wr5;
486 setsioreg(sc->sc_ctl, WR5, wr5);
487 val = 0;
488 done:
489 splx(s);
490 return val;
491 }
492
493 /*-------------------- cdevsw[] interface --------------------*/
494
495 int
496 sioopen(dev_t dev, int flag, int mode, struct lwp *l)
497 {
498 struct siotty_softc *sc;
499 struct tty *tp;
500 int error;
501 int s;
502
503 sc = device_lookup_private(&siotty_cd, minor(dev));
504 if (sc == NULL)
505 return ENXIO;
506
507 tp = sc->sc_tty;
508
509 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
510 return EBUSY;
511
512 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
513 struct termios t;
514
515 tp->t_dev = dev;
516 t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
517 t.c_cflag = TTYDEF_CFLAG;
518 tp->t_ospeed = 0; /* force register update */
519 (void)sioparam(tp, &t);
520 tp->t_iflag = TTYDEF_IFLAG;
521 tp->t_oflag = TTYDEF_OFLAG;
522 tp->t_lflag = TTYDEF_LFLAG;
523 ttychars(tp);
524 ttsetwater(tp);
525 /* raise RTS and DTR here; but, DTR lead is not wired */
526 /* then check DCD condition; but, DCD lead is not wired */
527 #if 0
528 if ((sc->sc_flags & TIOCFLAG_SOFTCAR) != 0
529 || (tp->t_cflag & MDMBUF) != 0
530 || (getsiocsr(sc->sc_ctl) & RR_DCD) != 0)
531 tp->t_state |= TS_CARR_ON;
532 else
533 tp->t_state &= ~TS_CARR_ON;
534 #else
535 tp->t_state |= TS_CARR_ON; /* assume detected all the time */
536 #endif
537
538 s = splserial();
539 sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
540 sc->sc_rbavail = siotty_rbuf_size;
541 splx(s);
542 }
543
544 error = ttyopen(tp, 0, (flag & O_NONBLOCK));
545 if (error > 0)
546 return error;
547 return (*tp->t_linesw->l_open)(dev, tp);
548 }
549
550 int
551 sioclose(dev_t dev, int flag, int mode, struct lwp *l)
552 {
553 struct siotty_softc *sc = device_lookup_private(&siotty_cd,minor(dev));
554 struct tty *tp = sc->sc_tty;
555 int s;
556
557 (*tp->t_linesw->l_close)(tp, flag);
558
559 s = splserial();
560 siomctl(sc, TIOCM_BREAK, DMBIC);
561 #if 0 /* because unable to feed DTR signal */
562 if ((tp->t_cflag & HUPCL) != 0
563 || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) {
564 siomctl(sc, TIOCM_DTR, DMBIC);
565 /* Yield CPU time to others for 1 second, then ... */
566 siomctl(sc, TIOCM_DTR, DMBIS);
567 }
568 #endif
569 splx(s);
570 return ttyclose(tp);
571 }
572
573 int
574 sioread(dev_t dev, struct uio *uio, int flag)
575 {
576 struct siotty_softc *sc;
577 struct tty *tp;
578
579 sc = device_lookup_private(&siotty_cd, minor(dev));
580 tp = sc->sc_tty;
581 return (*tp->t_linesw->l_read)(tp, uio, flag);
582 }
583
584 int
585 siowrite(dev_t dev, struct uio *uio, int flag)
586 {
587 struct siotty_softc *sc;
588 struct tty *tp;
589
590 sc = device_lookup_private(&siotty_cd, minor(dev));
591 tp = sc->sc_tty;
592 return (*tp->t_linesw->l_write)(tp, uio, flag);
593 }
594
595 int
596 siopoll(dev_t dev, int events, struct lwp *l)
597 {
598 struct siotty_softc *sc;
599 struct tty *tp;
600
601 sc = device_lookup_private(&siotty_cd, minor(dev));
602 tp = sc->sc_tty;
603 return ((*tp->t_linesw->l_poll)(tp, events, l));
604 }
605
606 int
607 sioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
608 {
609 struct siotty_softc *sc;
610 struct tty *tp;
611 int error;
612
613 sc = device_lookup_private(&siotty_cd, minor(dev));
614 tp = sc->sc_tty;
615 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
616 if (error != EPASSTHROUGH)
617 return error;
618
619 error = ttioctl(tp, cmd, data, flag, l);
620 if (error != EPASSTHROUGH)
621 return error;
622
623 /* the last resort for TIOC ioctl tranversing */
624 switch (cmd) {
625 case TIOCSBRK: /* Set the hardware into BREAK condition */
626 siomctl(sc, TIOCM_BREAK, DMBIS);
627 break;
628 case TIOCCBRK: /* Clear the hardware BREAK condition */
629 siomctl(sc, TIOCM_BREAK, DMBIC);
630 break;
631 case TIOCSDTR: /* Assert DTR signal */
632 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS);
633 break;
634 case TIOCCDTR: /* Clear DTR signal */
635 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC);
636 break;
637 case TIOCMSET: /* Set modem state replacing current one */
638 siomctl(sc, *(int *)data, DMSET);
639 break;
640 case TIOCMGET: /* Return current modem state */
641 *(int *)data = siomctl(sc, 0, DMGET);
642 break;
643 case TIOCMBIS: /* Set individual bits of modem state */
644 siomctl(sc, *(int *)data, DMBIS);
645 break;
646 case TIOCMBIC: /* Clear individual bits of modem state */
647 siomctl(sc, *(int *)data, DMBIC);
648 break;
649 case TIOCSFLAGS: /* Instruct how serial port behaves */
650 sc->sc_flags = *(int *)data;
651 break;
652 case TIOCGFLAGS: /* Return current serial port state */
653 *(int *)data = sc->sc_flags;
654 break;
655 default:
656 return EPASSTHROUGH;
657 }
658 return 0;
659 }
660
661 /* ARSGUSED */
662 struct tty *
663 siotty(dev_t dev)
664 {
665 struct siotty_softc *sc;
666
667 sc = device_lookup_private(&siotty_cd, minor(dev));
668 return sc->sc_tty;
669 }
670
671 /*-------------------- miscelleneous routine --------------------*/
672
673 /* EXPORT */ void
674 setsioreg(struct sioreg *sio, int regno, int val)
675 {
676
677 if (regno != 0)
678 sio->sio_cmd = regno; /* DELAY(); */
679 sio->sio_cmd = val; /* DELAY(); */
680 }
681
682 /* EXPORT */ uint16_t
683 getsiocsr(struct sioreg *sio)
684 {
685 int val;
686
687 val = sio->sio_stat << 8; /* DELAY(); */
688 sio->sio_cmd = 1; /* DELAY(); */
689 val |= sio->sio_stat; /* DELAY(); */
690 return val;
691 }
692
693 /*--------------------- console interface ----------------------*/
694
695 void syscnattach(int);
696 int syscngetc(dev_t);
697 void syscnputc(dev_t, int);
698
699 struct consdev syscons = {
700 NULL,
701 NULL,
702 syscngetc,
703 syscnputc,
704 nullcnpollc,
705 NULL,
706 NULL,
707 NULL,
708 NODEV,
709 CN_REMOTE,
710 };
711
712 /* EXPORT */ void
713 syscnattach(int channel)
714 {
715 /*
716 * Channel A is immediately initialized with 9600N1 right after cold
717 * boot/reset/poweron. ROM monitor emits one line message on CH.A.
718 */
719 struct sioreg *sio;
720 sio = (struct sioreg *)0x51000000 + channel;
721
722 syscons.cn_dev = makedev(cdevsw_lookup_major(&siotty_cdevsw),
723 channel);
724 cn_tab = &syscons;
725 cn_init_magic(&siotty_cnm_state);
726 cn_set_magic("\047\001");
727
728 setsioreg(sio, WR0, WR0_CHANRST);
729 setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1);
730 setsioreg(sio, WR2B, 0);
731 setsioreg(sio, WR0, ch0_regs[WR0]);
732 setsioreg(sio, WR4, ch0_regs[WR4]);
733 setsioreg(sio, WR3, ch0_regs[WR3]);
734 setsioreg(sio, WR5, ch0_regs[WR5]);
735 setsioreg(sio, WR0, ch0_regs[WR0]);
736 }
737
738 /* EXPORT */ int
739 syscngetc(dev_t dev)
740 {
741 struct sioreg *sio;
742 int s, c;
743
744 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1);
745 s = splhigh();
746 while ((getsiocsr(sio) & RR_RXRDY) == 0)
747 continue;
748 c = sio->sio_data;
749 splx(s);
750
751 return c;
752 }
753
754 /* EXPORT */ void
755 syscnputc(dev_t dev, int c)
756 {
757 struct sioreg *sio;
758 int s;
759
760 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1);
761 s = splhigh();
762 while ((getsiocsr(sio) & RR_TXRDY) == 0)
763 continue;
764 sio->sio_cmd = WR0_RSTPEND;
765 sio->sio_data = c;
766 splx(s);
767 }
768