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