pcons.c revision 1.27
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
63struct 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
89typedef 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)
100void cn_init_magic(cnm_state_t *cnm);
101void cn_destroy_magic(cnm_state_t *cnm);
102int cn_set_magic(char *magic);
103int 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
130int pconsmatch(struct device *, void *, void *);
131void pconsattach(struct device *, struct device *, void *);
132
133const struct cfattach pcons_ca = {
134	sizeof(struct pconssoftc), pconsmatch, pconsattach
135};
136
137struct cfdriver pcons_cd = {
138	NULL, "pcons", DV_TTY
139};
140
141extern struct cfdriver pcons_cd;
142static struct cnm_state pcons_cnm_state;
143
144static int pconsprobe(void);
145static void pcons_wsdisplay_init(struct pconssoftc *);
146extern struct consdev *cn_tab;
147
148cons_decl(prom_);
149
150int
151pconsmatch(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
160void pcons_poll(void *);
161
162void
163pconsattach(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
200void pconsstart(struct tty *);
201int pconsparam(struct tty *, struct termios *);
202
203int
204pconsopen(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
247int
248pconsclose(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
260int
261pconsread(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
269int
270pconswrite(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
278int
279pconsioctl(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
292struct tty *
293pconstty(dev_t dev)
294{
295	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
296
297	return sc->of_tty;
298}
299
300int
301pconsstop(struct tty *tp, int flag)
302{
303	return 0;
304}
305
306void
307pconsstart(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
339int
340pconsparam(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
348void
349pcons_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
366int
367pconsprobe(void)
368{
369	if (!stdin) stdin = OF_stdin();
370	if (!stdout) stdout = OF_stdout();
371
372	return (stdin && stdout);
373}
374
375void
376pcons_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
402static unsigned short cn_magic[CNS_LEN];
403
404/*
405 * Initialize a cnm_state_t.
406 */
407void
408cn_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 */
417void
418cn_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 */
428int
429cn_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 */
486int
487cn_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
523int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
524	    int *, int *, uint32_t *);
525void	pcons_cursor(void *, int, int, int);
526void	pcons_free_screen(void *, void *);
527int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
528int	pcons_mapchar(void *, int, unsigned int *);
529paddr_t	pcons_mmap(void *, off_t, int);
530int	pcons_putchar(void *, int, int, u_int, uint32_t);
531int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
532	    void *);
533
534struct wsdisplay_emulops pcons_emulops = {
535	NULL,
536	pcons_mapchar,
537	pcons_putchar
538};
539
540struct wsscreen_descr pcons_stdscreen = {
541	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
542};
543
544const struct wsscreen_descr *pcons_scrlist[] = {
545	&pcons_stdscreen
546};
547
548struct wsscreen_list pcons_screenlist = {
549	1, pcons_scrlist
550};
551
552struct 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
560int
561pcons_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
588void
589pcons_free_screen(void *v, void *cookie)
590{
591	struct pconssoftc *sc = v;
592
593	sc->sc_nscreens--;
594}
595
596int
597pcons_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
610paddr_t
611pcons_mmap(void *v, off_t off, int prot)
612{
613	return ((paddr_t)-1);
614}
615
616int
617pcons_show_screen(void *v, void *cookie, int waitok,
618    void (*cb)(void *, int, int), void *arg)
619{
620	return (0);
621}
622
623int
624pcons_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
635int
636pcons_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
649void
650pcons_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