xref: /openbsd/sys/arch/sparc64/dev/pcons.c (revision 09467b48)
1 /*	$OpenBSD: pcons.c,v 1.25 2020/05/25 09:55:48 jsg Exp $	*/
2 /*	$NetBSD: pcons.c,v 1.7 2001/05/02 10:32:20 scw Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 Eduardo E. Horvath
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * Default console driver.  Uses the PROM or whatever
34  * driver(s) are appropriate.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/conf.h>
40 #include <sys/device.h>
41 #include <sys/ioctl.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/tty.h>
45 #include <sys/time.h>
46 #include <sys/syslog.h>
47 
48 #include <machine/autoconf.h>
49 #include <machine/openfirm.h>
50 #include <machine/conf.h>
51 #include <machine/cpu.h>
52 #include <machine/psl.h>
53 
54 #include <dev/cons.h>
55 
56 #include "wsdisplay.h"
57 
58 #if NWSDISPLAY > 0
59 #include <dev/wscons/wsconsio.h>
60 #include <dev/wscons/wsdisplayvar.h>
61 #endif
62 
63 struct pconssoftc {
64 	struct device of_dev;
65 
66 #if NWSDISPLAY > 0
67 	int	sc_wsdisplay;
68 	u_int	sc_nscreens;
69 #endif
70 
71 	struct tty *of_tty;
72 	struct timeout sc_poll_to;
73 	int of_flags;
74 };
75 /* flags: */
76 #define	OFPOLL		1
77 
78 #define	OFBURSTLEN	128	/* max number of bytes to write in one chunk */
79 
80 /* XXXXXXXX - this is in MI code in NetBSD */
81 /*
82  * Stuff to handle debugger magic key sequences.
83  */
84 #define CNS_LEN                 128
85 #define CNS_MAGIC_VAL(x)        ((x)&0x1ff)
86 #define CNS_MAGIC_NEXT(x)       (((x)>>9)&0x7f)
87 #define CNS_TERM                0x7f    /* End of sequence */
88 
89 typedef struct cnm_state {
90 	int	cnm_state;
91 	u_short	*cnm_magic;
92 } cnm_state_t;
93 #ifdef DDB
94 #include <ddb/db_var.h>
95 #define cn_trap()	do { if (db_console) db_enter(); } while (0)
96 #else
97 #define cn_trap()
98 #endif
99 #define cn_isconsole(d)	((d) == cn_tab->cn_dev)
100 void cn_init_magic(cnm_state_t *cnm);
101 void cn_destroy_magic(cnm_state_t *cnm);
102 int cn_set_magic(char *magic);
103 int cn_get_magic(char *magic, int len);
104 /* This should be called for each byte read */
105 #ifndef cn_check_magic
106 #define cn_check_magic(d, k, s)                                         \
107         do {                                                            \
108 		if (cn_isconsole(d)) {                                  \
109 			int v = (s).cnm_magic[(s).cnm_state];           \
110 			if ((k) == CNS_MAGIC_VAL(v)) {                  \
111 				(s).cnm_state = CNS_MAGIC_NEXT(v);      \
112 				if ((s).cnm_state == CNS_TERM) {        \
113 					cn_trap();                      \
114                                         (s).cnm_state = 0;              \
115 				}                                       \
116 			} else {                                        \
117 				(s).cnm_state = 0;                      \
118 			}                                               \
119 		}                                                       \
120 	} while (/* CONSTCOND */ 0)
121 #endif
122 
123 /* Encode out-of-band events this way when passing to cn_check_magic() */
124 #define CNC_BREAK               0x100
125 
126 /* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */
127 
128 #include <sparc64/dev/cons.h>
129 
130 int pconsmatch(struct device *, void *, void *);
131 void pconsattach(struct device *, struct device *, void *);
132 
133 struct cfattach pcons_ca = {
134 	sizeof(struct pconssoftc), pconsmatch, pconsattach
135 };
136 
137 struct cfdriver pcons_cd = {
138 	NULL, "pcons", DV_TTY
139 };
140 
141 extern struct cfdriver pcons_cd;
142 static struct cnm_state pcons_cnm_state;
143 
144 static int pconsprobe(void);
145 static void pcons_wsdisplay_init(struct pconssoftc *);
146 extern struct consdev *cn_tab;
147 
148 cons_decl(prom_);
149 
150 int
151 pconsmatch(parent, match, aux)
152 	struct device *parent;
153 	void *match;
154 	void *aux;
155 {
156 	struct mainbus_attach_args *ma = aux;
157 
158 	/* Only attach if no other console has attached. */
159 	return (strcmp("pcons", ma->ma_name) == 0 &&
160 	    cn_tab->cn_getc == prom_cngetc);
161 }
162 
163 void pcons_poll(void *);
164 
165 void
166 pconsattach(parent, self, aux)
167 	struct device *parent, *self;
168 	void *aux;
169 {
170 	struct pconssoftc *sc = (struct pconssoftc *) self;
171 #if NWSDISPLAY > 0
172 	char buffer[128];
173 	extern struct consdev wsdisplay_cons;
174 	extern int wsdisplay_getc_dummy(dev_t);
175 #endif
176 
177 	printf("\n");
178 	if (!pconsprobe())
179 		return;
180 
181 #if NWSDISPLAY > 0
182 	/*
183 	 * Attach a dumb wsdisplay device if a wscons input driver has
184 	 * registered as the console, or is about to do so (usb keyboards).
185 	 */
186 	if (wsdisplay_cons.cn_getc != wsdisplay_getc_dummy)
187 		sc->sc_wsdisplay = 1;
188 	else {
189 		if (OF_getprop(OF_instance_to_package(stdin), "compatible",
190 		    buffer, sizeof(buffer)) != -1 &&
191 		   strncmp("usb", buffer, 3) == 0)
192 		sc->sc_wsdisplay = 1;
193 	}
194 
195 	if (sc->sc_wsdisplay != 0) {
196 		pcons_wsdisplay_init(sc);
197 		return;
198 	}
199 #endif
200 	cn_init_magic(&pcons_cnm_state);
201 	cn_set_magic("+++++");
202 	timeout_set(&sc->sc_poll_to, pcons_poll, sc);
203 }
204 
205 void pconsstart(struct tty *);
206 int pconsparam(struct tty *, struct termios *);
207 
208 int
209 pconsopen(dev, flag, mode, p)
210 	dev_t dev;
211 	int flag, mode;
212 	struct proc *p;
213 {
214 	struct pconssoftc *sc;
215 	int unit = minor(dev);
216 	struct tty *tp;
217 
218 	if (unit >= pcons_cd.cd_ndevs)
219 		return ENXIO;
220 	sc = pcons_cd.cd_devs[unit];
221 	if (!sc)
222 		return ENXIO;
223 #if NWSDISPLAY > 0
224 	if (sc->sc_wsdisplay != 0)
225 		return ENXIO;
226 #endif
227 	if (!(tp = sc->of_tty)) {
228 		sc->of_tty = tp = ttymalloc(0);
229 	}
230 	tp->t_oproc = pconsstart;
231 	tp->t_param = pconsparam;
232 	tp->t_dev = dev;
233 	cn_tab->cn_dev = dev;
234 	if (!(tp->t_state & TS_ISOPEN)) {
235 		ttychars(tp);
236 		tp->t_iflag = TTYDEF_IFLAG;
237 		tp->t_oflag = TTYDEF_OFLAG;
238 		tp->t_cflag = TTYDEF_CFLAG;
239 		tp->t_lflag = TTYDEF_LFLAG;
240 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
241 		pconsparam(tp, &tp->t_termios);
242 		ttsetwater(tp);
243 	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
244 		return EBUSY;
245 	tp->t_state |= TS_CARR_ON;
246 
247 	if (!(sc->of_flags & OFPOLL)) {
248 		sc->of_flags |= OFPOLL;
249 		timeout_add(&sc->sc_poll_to, 1);
250 	}
251 
252 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
253 }
254 
255 int
256 pconsclose(dev, flag, mode, p)
257 	dev_t dev;
258 	int flag, mode;
259 	struct proc *p;
260 {
261 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
262 	struct tty *tp = sc->of_tty;
263 
264 	timeout_del(&sc->sc_poll_to);
265 	sc->of_flags &= ~OFPOLL;
266 	(*linesw[tp->t_line].l_close)(tp, flag, p);
267 	ttyclose(tp);
268 	return 0;
269 }
270 
271 int
272 pconsread(dev, uio, flag)
273 	dev_t dev;
274 	struct uio *uio;
275 	int flag;
276 {
277 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
278 	struct tty *tp = sc->of_tty;
279 
280 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
281 }
282 
283 int
284 pconswrite(dev, uio, flag)
285 	dev_t dev;
286 	struct uio *uio;
287 	int flag;
288 {
289 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
290 	struct tty *tp = sc->of_tty;
291 
292 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
293 }
294 
295 int
296 pconsioctl(dev, cmd, data, flag, p)
297 	dev_t dev;
298 	u_long cmd;
299 	caddr_t data;
300 	int flag;
301 	struct proc *p;
302 {
303 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
304 	struct tty *tp = sc->of_tty;
305 	int error;
306 
307 	if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
308 		return error;
309 	if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
310 		return error;
311 	return ENOTTY;
312 }
313 
314 struct tty *
315 pconstty(dev)
316 	dev_t dev;
317 {
318 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
319 
320 	return sc->of_tty;
321 }
322 
323 int
324 pconsstop(tp, flag)
325 	struct tty *tp;
326 	int flag;
327 {
328 	return 0;
329 }
330 
331 void
332 pconsstart(tp)
333 	struct tty *tp;
334 {
335 	struct clist *cl;
336 	int s, len;
337 	u_char buf[OFBURSTLEN];
338 
339 	s = spltty();
340 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
341 		splx(s);
342 		return;
343 	}
344 	tp->t_state |= TS_BUSY;
345 	splx(s);
346 	cl = &tp->t_outq;
347 	len = q_to_b(cl, buf, OFBURSTLEN);
348 	OF_write(stdout, buf, len);
349 	s = spltty();
350 	tp->t_state &= ~TS_BUSY;
351 	if (cl->c_cc) {
352 		tp->t_state |= TS_TIMEOUT;
353 		timeout_add(&tp->t_rstrt_to, 1);
354 	}
355 	if (cl->c_cc <= tp->t_lowat) {
356 		if (tp->t_state & TS_ASLEEP) {
357 			tp->t_state &= ~TS_ASLEEP;
358 			wakeup(cl);
359 		}
360 		selwakeup(&tp->t_wsel);
361 	}
362 	splx(s);
363 }
364 
365 int
366 pconsparam(tp, t)
367 	struct tty *tp;
368 	struct termios *t;
369 {
370 	tp->t_ispeed = t->c_ispeed;
371 	tp->t_ospeed = t->c_ospeed;
372 	tp->t_cflag = t->c_cflag;
373 	return 0;
374 }
375 
376 void
377 pcons_poll(aux)
378 	void *aux;
379 {
380 	struct pconssoftc *sc = aux;
381 	struct tty *tp = sc->of_tty;
382 	char ch;
383 
384 	while (OF_read(stdin, &ch, 1) > 0) {
385 		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
386 		if (tp && (tp->t_state & TS_ISOPEN)) {
387 			if (ch == '\b')
388 				ch = '\177';
389 			(*linesw[tp->t_line].l_rint)(ch, tp);
390 		}
391 	}
392 	timeout_add(&sc->sc_poll_to, 1);
393 }
394 
395 int
396 pconsprobe(void)
397 {
398 	if (!stdin) stdin = OF_stdin();
399 	if (!stdout) stdout = OF_stdout();
400 
401 	return (stdin && stdout);
402 }
403 
404 void
405 pcons_cnpollc(dev, on)
406 	dev_t dev;
407 	int on;
408 {
409 	struct pconssoftc *sc = NULL;
410 
411 	if (pcons_cd.cd_devs)
412 		sc = pcons_cd.cd_devs[minor(dev)];
413 
414 	if (sc == NULL)
415 		return;
416 
417 	if (on) {
418 		if (sc->of_flags & OFPOLL)
419 			timeout_del(&sc->sc_poll_to);
420 		sc->of_flags &= ~OFPOLL;
421 	} else {
422                 /* Resuming kernel. */
423 		if (!(sc->of_flags & OFPOLL)) {
424 			sc->of_flags |= OFPOLL;
425 			timeout_add(&sc->sc_poll_to, 1);
426 		}
427 	}
428 }
429 
430 /* XXXXXXXX --- more cnmagic stuff. */
431 #define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
432 
433 static unsigned short cn_magic[CNS_LEN];
434 
435 /*
436  * Initialize a cnm_state_t.
437  */
438 void
439 cn_init_magic(cnm_state_t *cnm)
440 {
441 	cnm->cnm_state = 0;
442 	cnm->cnm_magic = cn_magic;
443 }
444 
445 /*
446  * Destroy a cnm_state_t.
447  */
448 void
449 cn_destroy_magic(cnm_state_t *cnm)
450 {
451 	cnm->cnm_state = 0;
452 	cnm->cnm_magic = NULL;
453 }
454 
455 /*
456  * Translate a magic string to a state
457  * machine table.
458  */
459 int
460 cn_set_magic(char *magic)
461 {
462 	unsigned int i, c, n;
463 	unsigned short m[CNS_LEN];
464 
465 	for (i=0; i<CNS_LEN; i++) {
466 		c = (*magic++)&0xff;
467 		n = *magic ? i+1 : CNS_TERM;
468 		switch (c) {
469 		case 0:
470 			/* End of string */
471 			if (i == 0) {
472 				/* empty string? */
473 				cn_magic[0] = 0;
474 #ifdef DEBUG
475 				printf("cn_set_magic(): empty!\n");
476 #endif
477 				return (0);
478 			}
479 			do {
480 				cn_magic[i] = m[i];
481 			} while (i--);
482 			return(0);
483 		case 0x27:
484 			/* Escape sequence */
485 			c = (*magic++)&0xff;
486 			n = *magic ? i+1 : CNS_TERM;
487 			switch (c) {
488 			case 0x27:
489 				break;
490 			case 0x01:
491 				/* BREAK */
492 				c = CNC_BREAK;
493 				break;
494 			case 0x02:
495 				/* NUL */
496 				c = 0;
497 				break;
498 			}
499 			/* FALLTHROUGH */
500 		default:
501 			/* Transition to the next state. */
502 #ifdef DEBUG
503 			if (!cold)
504 				printf("mag %d %x:%x\n", i, c, n);
505 #endif
506 			m[i] = ENCODE_STATE(c, n);
507 			break;
508 		}
509 	}
510 	return (EINVAL);
511 }
512 
513 /*
514  * Translate a state machine table back to
515  * a magic string.
516  */
517 int
518 cn_get_magic(char *magic, int maglen) {
519 	unsigned int i, c;
520 
521 	for (i=0; i<CNS_LEN; i++) {
522 		c = cn_magic[i];
523 		/* Translate a character */
524 		switch (CNS_MAGIC_VAL(c)) {
525 		case CNC_BREAK:
526 			*magic++ = 0x27;
527 			*magic++ = 0x01;
528 			break;
529 		case 0:
530 			*magic++ = 0x27;
531 			*magic++ = 0x02;
532 			break;
533 		case 0x27:
534 			*magic++ = 0x27;
535 			*magic++ = 0x27;
536 			break;
537 		default:
538 			*magic++ = (c&0x0ff);
539 			break;
540 		}
541 		/* Now go to the next state */
542 		i = CNS_MAGIC_NEXT(c);
543 		if (i == CNS_TERM || i == 0) {
544 			/* Either termination state or empty machine */
545 			*magic++ = 0;
546 			return (0);
547 		}
548 	}
549 	return (EINVAL);
550 }
551 
552 #if NWSDISPLAY > 0
553 
554 int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
555 	    int *, int *, uint32_t *);
556 void	pcons_cursor(void *, int, int, int);
557 void	pcons_free_screen(void *, void *);
558 int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
559 int	pcons_mapchar(void *, int, unsigned int *);
560 paddr_t	pcons_mmap(void *, off_t, int);
561 int	pcons_putchar(void *, int, int, u_int, uint32_t);
562 int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
563 	    void *);
564 
565 struct wsdisplay_emulops pcons_emulops = {
566 	NULL,
567 	pcons_mapchar,
568 	pcons_putchar
569 };
570 
571 struct wsscreen_descr pcons_stdscreen = {
572 	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
573 };
574 
575 const struct wsscreen_descr *pcons_scrlist[] = {
576 	&pcons_stdscreen
577 };
578 
579 struct wsscreen_list pcons_screenlist = {
580 	1, pcons_scrlist
581 };
582 
583 struct wsdisplay_accessops pcons_accessops = {
584 	.ioctl = pcons_ioctl,
585 	.mmap = pcons_mmap,
586 	.alloc_screen = pcons_alloc_screen,
587 	.free_screen = pcons_free_screen,
588 	.show_screen = pcons_show_screen
589 };
590 
591 int
592 pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
593     int *curxp, int *curyp, uint32_t *attrp)
594 {
595 	struct pconssoftc *sc = v;
596 	int *rowp, *colp;
597 	int row, col;
598 
599 	if (sc->sc_nscreens > 0)
600 		return (ENOMEM);
601 
602 	row = col = 0;
603 	if (romgetcursoraddr(&rowp, &colp) == 0) {
604 		if (rowp != NULL)
605 			row = *rowp;
606 		if (colp != NULL)
607 			col = *colp;
608 	}
609 
610 	*cookiep = v;
611 	*attrp = 0;
612 	*curxp = col;
613 	*curyp = row;
614 
615 	sc->sc_nscreens++;
616 	return (0);
617 }
618 
619 void
620 pcons_free_screen(void *v, void *cookie)
621 {
622 	struct pconssoftc *sc = v;
623 
624 	sc->sc_nscreens--;
625 }
626 
627 int
628 pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
629 {
630 	switch (cmd) {
631 	case WSDISPLAYIO_GTYPE:
632 		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
633 		break;
634 	default:
635 		return (-1);
636 	}
637 
638 	return (0);
639 }
640 
641 paddr_t
642 pcons_mmap(void *v, off_t off, int prot)
643 {
644 	return ((paddr_t)-1);
645 }
646 
647 int
648 pcons_show_screen(void *v, void *cookie, int waitok,
649     void (*cb)(void *, int, int), void *arg)
650 {
651 	return (0);
652 }
653 
654 int
655 pcons_mapchar(void *v, int uc, unsigned int *idx)
656 {
657 	if ((uc & 0xff) == uc) {
658 		*idx = uc;
659 		return (1);
660 	} else {
661 		*idx = '?';
662 		return (0);
663 	}
664 }
665 
666 int
667 pcons_putchar(void *v, int row, int col, u_int uc, uint32_t attr)
668 {
669 	u_char buf[1];
670 	int s;
671 
672 	buf[0] = (u_char)uc;
673 	s = splhigh();
674 	OF_write(stdout, &buf, 1);
675 	splx(s);
676 
677 	return 0;
678 }
679 
680 void
681 pcons_wsdisplay_init(struct pconssoftc *sc)
682 {
683 	struct wsemuldisplaydev_attach_args waa;
684 	int *rowp, *colp;
685 	int options, row, col;
686 
687 	row = col = 0;
688 	if (romgetcursoraddr(&rowp, &colp) == 0) {
689 		if (rowp != NULL)
690 			row = *rowp;
691 		if (colp != NULL)
692 			col = *colp;
693 	}
694 
695 	options = OF_finddevice("/options");
696 	pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
697 	pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);
698 
699 	/*
700 	 * We claim console here, because we can only get there if stdin
701 	 * is a keyboard. However, the PROM could have been configured with
702 	 * stdin being a keyboard and stdout being a serial sink.
703 	 * But since this combination is not supported under OpenBSD at the
704 	 * moment, it is reasonably safe to attach a dumb display as console
705 	 * here.
706 	 */
707 	wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);
708 
709 	waa.console = 1;
710 	waa.scrdata = &pcons_screenlist;
711 	waa.accessops = &pcons_accessops;
712 	waa.accesscookie = sc;
713 	waa.defaultscreens = 1;
714 
715 	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
716 }
717 #endif
718