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