1/*	$OpenBSD: pcons.c,v 1.29 2024/05/14 08:26:13 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_free_screen(void *, void *);
526int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
527int	pcons_mapchar(void *, int, unsigned int *);
528paddr_t	pcons_mmap(void *, off_t, int);
529int	pcons_putchar(void *, int, int, u_int, uint32_t);
530int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
531	    void *);
532
533struct wsdisplay_emulops pcons_emulops = {
534	NULL,
535	pcons_mapchar,
536	pcons_putchar
537};
538
539struct wsscreen_descr pcons_stdscreen = {
540	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
541};
542
543const struct wsscreen_descr *pcons_scrlist[] = {
544	&pcons_stdscreen
545};
546
547struct wsscreen_list pcons_screenlist = {
548	1, pcons_scrlist
549};
550
551struct wsdisplay_accessops pcons_accessops = {
552	.ioctl = pcons_ioctl,
553	.mmap = pcons_mmap,
554	.alloc_screen = pcons_alloc_screen,
555	.free_screen = pcons_free_screen,
556	.show_screen = pcons_show_screen
557};
558
559int
560pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
561    int *curxp, int *curyp, uint32_t *attrp)
562{
563	struct pconssoftc *sc = v;
564	int *rowp, *colp;
565	int row, col;
566
567	if (sc->sc_nscreens > 0)
568		return (ENOMEM);
569
570	row = col = 0;
571	if (romgetcursoraddr(&rowp, &colp) == 0) {
572		if (rowp != NULL)
573			row = *rowp;
574		if (colp != NULL)
575			col = *colp;
576	}
577
578	*cookiep = v;
579	*attrp = 0;
580	*curxp = col;
581	*curyp = row;
582
583	sc->sc_nscreens++;
584	return (0);
585}
586
587void
588pcons_free_screen(void *v, void *cookie)
589{
590	struct pconssoftc *sc = v;
591
592	sc->sc_nscreens--;
593}
594
595int
596pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
597{
598	switch (cmd) {
599	case WSDISPLAYIO_GTYPE:
600		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
601		break;
602	default:
603		return (-1);
604	}
605
606	return (0);
607}
608
609paddr_t
610pcons_mmap(void *v, off_t off, int prot)
611{
612	return ((paddr_t)-1);
613}
614
615int
616pcons_show_screen(void *v, void *cookie, int waitok,
617    void (*cb)(void *, int, int), void *arg)
618{
619	return (0);
620}
621
622int
623pcons_mapchar(void *v, int uc, unsigned int *idx)
624{
625	if ((uc & 0xff) == uc) {
626		*idx = uc;
627		return (1);
628	} else {
629		*idx = '?';
630		return (0);
631	}
632}
633
634int
635pcons_putchar(void *v, int row, int col, u_int uc, uint32_t attr)
636{
637	u_char buf[1];
638	int s;
639
640	buf[0] = (u_char)uc;
641	s = splhigh();
642	OF_write(stdout, &buf, 1);
643	splx(s);
644
645	return 0;
646}
647
648void
649pcons_wsdisplay_init(struct pconssoftc *sc)
650{
651	struct wsemuldisplaydev_attach_args waa;
652	int *rowp, *colp;
653	int options, row, col;
654
655	row = col = 0;
656	if (romgetcursoraddr(&rowp, &colp) == 0) {
657		if (rowp != NULL)
658			row = *rowp;
659		if (colp != NULL)
660			col = *colp;
661	}
662
663	options = OF_finddevice("/options");
664	pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
665	pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);
666
667	/*
668	 * We claim console here, because we can only get there if stdin
669	 * is a keyboard. However, the PROM could have been configured with
670	 * stdin being a keyboard and stdout being a serial sink.
671	 * But since this combination is not supported under OpenBSD at the
672	 * moment, it is reasonably safe to attach a dumb display as console
673	 * here.
674	 */
675	wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);
676
677	waa.console = 1;
678	waa.scrdata = &pcons_screenlist;
679	waa.accessops = &pcons_accessops;
680	waa.accesscookie = sc;
681	waa.defaultscreens = 1;
682
683	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
684}
685#endif
686