xref: /openbsd/sys/arch/sparc64/dev/pcons.c (revision 4bdff4be)
1 /*	$OpenBSD: pcons.c,v 1.27 2022/10/16 01:22:39 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 const 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(struct device *parent, void *match, void *aux)
152 {
153 	struct mainbus_attach_args *ma = aux;
154 
155 	/* Only attach if no other console has attached. */
156 	return (strcmp("pcons", ma->ma_name) == 0 &&
157 	    cn_tab->cn_getc == prom_cngetc);
158 }
159 
160 void pcons_poll(void *);
161 
162 void
163 pconsattach(struct device *parent, struct device *self, void *aux)
164 {
165 	struct pconssoftc *sc = (struct pconssoftc *) self;
166 #if NWSDISPLAY > 0
167 	char buffer[128];
168 	extern struct consdev wsdisplay_cons;
169 	extern int wsdisplay_getc_dummy(dev_t);
170 #endif
171 
172 	printf("\n");
173 	if (!pconsprobe())
174 		return;
175 
176 #if NWSDISPLAY > 0
177 	/*
178 	 * Attach a dumb wsdisplay device if a wscons input driver has
179 	 * registered as the console, or is about to do so (usb keyboards).
180 	 */
181 	if (wsdisplay_cons.cn_getc != wsdisplay_getc_dummy)
182 		sc->sc_wsdisplay = 1;
183 	else {
184 		if (OF_getprop(OF_instance_to_package(stdin), "compatible",
185 		    buffer, sizeof(buffer)) != -1 &&
186 		   strncmp("usb", buffer, 3) == 0)
187 		sc->sc_wsdisplay = 1;
188 	}
189 
190 	if (sc->sc_wsdisplay != 0) {
191 		pcons_wsdisplay_init(sc);
192 		return;
193 	}
194 #endif
195 	cn_init_magic(&pcons_cnm_state);
196 	cn_set_magic("+++++");
197 	timeout_set(&sc->sc_poll_to, pcons_poll, sc);
198 }
199 
200 void pconsstart(struct tty *);
201 int pconsparam(struct tty *, struct termios *);
202 
203 int
204 pconsopen(dev_t dev, int flag, int mode, struct proc *p)
205 {
206 	struct pconssoftc *sc;
207 	int unit = minor(dev);
208 	struct tty *tp;
209 
210 	if (unit >= pcons_cd.cd_ndevs)
211 		return ENXIO;
212 	sc = pcons_cd.cd_devs[unit];
213 	if (!sc)
214 		return ENXIO;
215 #if NWSDISPLAY > 0
216 	if (sc->sc_wsdisplay != 0)
217 		return ENXIO;
218 #endif
219 	if (!(tp = sc->of_tty)) {
220 		sc->of_tty = tp = ttymalloc(0);
221 	}
222 	tp->t_oproc = pconsstart;
223 	tp->t_param = pconsparam;
224 	tp->t_dev = dev;
225 	cn_tab->cn_dev = dev;
226 	if (!(tp->t_state & TS_ISOPEN)) {
227 		ttychars(tp);
228 		tp->t_iflag = TTYDEF_IFLAG;
229 		tp->t_oflag = TTYDEF_OFLAG;
230 		tp->t_cflag = TTYDEF_CFLAG;
231 		tp->t_lflag = TTYDEF_LFLAG;
232 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
233 		pconsparam(tp, &tp->t_termios);
234 		ttsetwater(tp);
235 	} else if ((tp->t_state & TS_XCLUDE) && suser(p))
236 		return EBUSY;
237 	tp->t_state |= TS_CARR_ON;
238 
239 	if (!(sc->of_flags & OFPOLL)) {
240 		sc->of_flags |= OFPOLL;
241 		timeout_add(&sc->sc_poll_to, 1);
242 	}
243 
244 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
245 }
246 
247 int
248 pconsclose(dev_t dev, int flag, int mode, struct proc *p)
249 {
250 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
251 	struct tty *tp = sc->of_tty;
252 
253 	timeout_del(&sc->sc_poll_to);
254 	sc->of_flags &= ~OFPOLL;
255 	(*linesw[tp->t_line].l_close)(tp, flag, p);
256 	ttyclose(tp);
257 	return 0;
258 }
259 
260 int
261 pconsread(dev_t dev, struct uio *uio, int flag)
262 {
263 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
264 	struct tty *tp = sc->of_tty;
265 
266 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
267 }
268 
269 int
270 pconswrite(dev_t dev, struct uio *uio, int flag)
271 {
272 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
273 	struct tty *tp = sc->of_tty;
274 
275 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
276 }
277 
278 int
279 pconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
280 {
281 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
282 	struct tty *tp = sc->of_tty;
283 	int error;
284 
285 	if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
286 		return error;
287 	if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
288 		return error;
289 	return ENOTTY;
290 }
291 
292 struct tty *
293 pconstty(dev_t dev)
294 {
295 	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
296 
297 	return sc->of_tty;
298 }
299 
300 int
301 pconsstop(struct tty *tp, int flag)
302 {
303 	return 0;
304 }
305 
306 void
307 pconsstart(struct tty *tp)
308 {
309 	struct clist *cl;
310 	int s, len;
311 	u_char buf[OFBURSTLEN];
312 
313 	s = spltty();
314 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
315 		splx(s);
316 		return;
317 	}
318 	tp->t_state |= TS_BUSY;
319 	splx(s);
320 	cl = &tp->t_outq;
321 	len = q_to_b(cl, buf, OFBURSTLEN);
322 	OF_write(stdout, buf, len);
323 	s = spltty();
324 	tp->t_state &= ~TS_BUSY;
325 	if (cl->c_cc) {
326 		tp->t_state |= TS_TIMEOUT;
327 		timeout_add(&tp->t_rstrt_to, 1);
328 	}
329 	if (cl->c_cc <= tp->t_lowat) {
330 		if (tp->t_state & TS_ASLEEP) {
331 			tp->t_state &= ~TS_ASLEEP;
332 			wakeup(cl);
333 		}
334 		selwakeup(&tp->t_wsel);
335 	}
336 	splx(s);
337 }
338 
339 int
340 pconsparam(struct tty *tp, struct termios *t)
341 {
342 	tp->t_ispeed = t->c_ispeed;
343 	tp->t_ospeed = t->c_ospeed;
344 	tp->t_cflag = t->c_cflag;
345 	return 0;
346 }
347 
348 void
349 pcons_poll(void *aux)
350 {
351 	struct pconssoftc *sc = aux;
352 	struct tty *tp = sc->of_tty;
353 	char ch;
354 
355 	while (OF_read(stdin, &ch, 1) > 0) {
356 		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
357 		if (tp && (tp->t_state & TS_ISOPEN)) {
358 			if (ch == '\b')
359 				ch = '\177';
360 			(*linesw[tp->t_line].l_rint)(ch, tp);
361 		}
362 	}
363 	timeout_add(&sc->sc_poll_to, 1);
364 }
365 
366 int
367 pconsprobe(void)
368 {
369 	if (!stdin) stdin = OF_stdin();
370 	if (!stdout) stdout = OF_stdout();
371 
372 	return (stdin && stdout);
373 }
374 
375 void
376 pcons_cnpollc(dev_t dev, int on)
377 {
378 	struct pconssoftc *sc = NULL;
379 
380 	if (pcons_cd.cd_devs)
381 		sc = pcons_cd.cd_devs[minor(dev)];
382 
383 	if (sc == NULL)
384 		return;
385 
386 	if (on) {
387 		if (sc->of_flags & OFPOLL)
388 			timeout_del(&sc->sc_poll_to);
389 		sc->of_flags &= ~OFPOLL;
390 	} else {
391                 /* Resuming kernel. */
392 		if (!(sc->of_flags & OFPOLL)) {
393 			sc->of_flags |= OFPOLL;
394 			timeout_add(&sc->sc_poll_to, 1);
395 		}
396 	}
397 }
398 
399 /* XXXXXXXX --- more cnmagic stuff. */
400 #define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
401 
402 static unsigned short cn_magic[CNS_LEN];
403 
404 /*
405  * Initialize a cnm_state_t.
406  */
407 void
408 cn_init_magic(cnm_state_t *cnm)
409 {
410 	cnm->cnm_state = 0;
411 	cnm->cnm_magic = cn_magic;
412 }
413 
414 /*
415  * Destroy a cnm_state_t.
416  */
417 void
418 cn_destroy_magic(cnm_state_t *cnm)
419 {
420 	cnm->cnm_state = 0;
421 	cnm->cnm_magic = NULL;
422 }
423 
424 /*
425  * Translate a magic string to a state
426  * machine table.
427  */
428 int
429 cn_set_magic(char *magic)
430 {
431 	unsigned int i, c, n;
432 	unsigned short m[CNS_LEN];
433 
434 	for (i=0; i<CNS_LEN; i++) {
435 		c = (*magic++)&0xff;
436 		n = *magic ? i+1 : CNS_TERM;
437 		switch (c) {
438 		case 0:
439 			/* End of string */
440 			if (i == 0) {
441 				/* empty string? */
442 				cn_magic[0] = 0;
443 #ifdef DEBUG
444 				printf("cn_set_magic(): empty!\n");
445 #endif
446 				return (0);
447 			}
448 			do {
449 				cn_magic[i] = m[i];
450 			} while (i--);
451 			return(0);
452 		case 0x27:
453 			/* Escape sequence */
454 			c = (*magic++)&0xff;
455 			n = *magic ? i+1 : CNS_TERM;
456 			switch (c) {
457 			case 0x27:
458 				break;
459 			case 0x01:
460 				/* BREAK */
461 				c = CNC_BREAK;
462 				break;
463 			case 0x02:
464 				/* NUL */
465 				c = 0;
466 				break;
467 			}
468 			/* FALLTHROUGH */
469 		default:
470 			/* Transition to the next state. */
471 #ifdef DEBUG
472 			if (!cold)
473 				printf("mag %d %x:%x\n", i, c, n);
474 #endif
475 			m[i] = ENCODE_STATE(c, n);
476 			break;
477 		}
478 	}
479 	return (EINVAL);
480 }
481 
482 /*
483  * Translate a state machine table back to
484  * a magic string.
485  */
486 int
487 cn_get_magic(char *magic, int maglen) {
488 	unsigned int i, c;
489 
490 	for (i=0; i<CNS_LEN; i++) {
491 		c = cn_magic[i];
492 		/* Translate a character */
493 		switch (CNS_MAGIC_VAL(c)) {
494 		case CNC_BREAK:
495 			*magic++ = 0x27;
496 			*magic++ = 0x01;
497 			break;
498 		case 0:
499 			*magic++ = 0x27;
500 			*magic++ = 0x02;
501 			break;
502 		case 0x27:
503 			*magic++ = 0x27;
504 			*magic++ = 0x27;
505 			break;
506 		default:
507 			*magic++ = (c&0x0ff);
508 			break;
509 		}
510 		/* Now go to the next state */
511 		i = CNS_MAGIC_NEXT(c);
512 		if (i == CNS_TERM || i == 0) {
513 			/* Either termination state or empty machine */
514 			*magic++ = 0;
515 			return (0);
516 		}
517 	}
518 	return (EINVAL);
519 }
520 
521 #if NWSDISPLAY > 0
522 
523 int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
524 	    int *, int *, uint32_t *);
525 void	pcons_cursor(void *, int, int, int);
526 void	pcons_free_screen(void *, void *);
527 int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
528 int	pcons_mapchar(void *, int, unsigned int *);
529 paddr_t	pcons_mmap(void *, off_t, int);
530 int	pcons_putchar(void *, int, int, u_int, uint32_t);
531 int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
532 	    void *);
533 
534 struct wsdisplay_emulops pcons_emulops = {
535 	NULL,
536 	pcons_mapchar,
537 	pcons_putchar
538 };
539 
540 struct wsscreen_descr pcons_stdscreen = {
541 	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
542 };
543 
544 const struct wsscreen_descr *pcons_scrlist[] = {
545 	&pcons_stdscreen
546 };
547 
548 struct wsscreen_list pcons_screenlist = {
549 	1, pcons_scrlist
550 };
551 
552 struct wsdisplay_accessops pcons_accessops = {
553 	.ioctl = pcons_ioctl,
554 	.mmap = pcons_mmap,
555 	.alloc_screen = pcons_alloc_screen,
556 	.free_screen = pcons_free_screen,
557 	.show_screen = pcons_show_screen
558 };
559 
560 int
561 pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
562     int *curxp, int *curyp, uint32_t *attrp)
563 {
564 	struct pconssoftc *sc = v;
565 	int *rowp, *colp;
566 	int row, col;
567 
568 	if (sc->sc_nscreens > 0)
569 		return (ENOMEM);
570 
571 	row = col = 0;
572 	if (romgetcursoraddr(&rowp, &colp) == 0) {
573 		if (rowp != NULL)
574 			row = *rowp;
575 		if (colp != NULL)
576 			col = *colp;
577 	}
578 
579 	*cookiep = v;
580 	*attrp = 0;
581 	*curxp = col;
582 	*curyp = row;
583 
584 	sc->sc_nscreens++;
585 	return (0);
586 }
587 
588 void
589 pcons_free_screen(void *v, void *cookie)
590 {
591 	struct pconssoftc *sc = v;
592 
593 	sc->sc_nscreens--;
594 }
595 
596 int
597 pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
598 {
599 	switch (cmd) {
600 	case WSDISPLAYIO_GTYPE:
601 		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
602 		break;
603 	default:
604 		return (-1);
605 	}
606 
607 	return (0);
608 }
609 
610 paddr_t
611 pcons_mmap(void *v, off_t off, int prot)
612 {
613 	return ((paddr_t)-1);
614 }
615 
616 int
617 pcons_show_screen(void *v, void *cookie, int waitok,
618     void (*cb)(void *, int, int), void *arg)
619 {
620 	return (0);
621 }
622 
623 int
624 pcons_mapchar(void *v, int uc, unsigned int *idx)
625 {
626 	if ((uc & 0xff) == uc) {
627 		*idx = uc;
628 		return (1);
629 	} else {
630 		*idx = '?';
631 		return (0);
632 	}
633 }
634 
635 int
636 pcons_putchar(void *v, int row, int col, u_int uc, uint32_t attr)
637 {
638 	u_char buf[1];
639 	int s;
640 
641 	buf[0] = (u_char)uc;
642 	s = splhigh();
643 	OF_write(stdout, &buf, 1);
644 	splx(s);
645 
646 	return 0;
647 }
648 
649 void
650 pcons_wsdisplay_init(struct pconssoftc *sc)
651 {
652 	struct wsemuldisplaydev_attach_args waa;
653 	int *rowp, *colp;
654 	int options, row, col;
655 
656 	row = col = 0;
657 	if (romgetcursoraddr(&rowp, &colp) == 0) {
658 		if (rowp != NULL)
659 			row = *rowp;
660 		if (colp != NULL)
661 			col = *colp;
662 	}
663 
664 	options = OF_finddevice("/options");
665 	pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
666 	pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);
667 
668 	/*
669 	 * We claim console here, because we can only get there if stdin
670 	 * is a keyboard. However, the PROM could have been configured with
671 	 * stdin being a keyboard and stdout being a serial sink.
672 	 * But since this combination is not supported under OpenBSD at the
673 	 * moment, it is reasonably safe to attach a dumb display as console
674 	 * here.
675 	 */
676 	wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);
677 
678 	waa.console = 1;
679 	waa.scrdata = &pcons_screenlist;
680 	waa.accessops = &pcons_accessops;
681 	waa.accesscookie = sc;
682 	waa.defaultscreens = 1;
683 
684 	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
685 }
686 #endif
687