pcons.c revision 1.15
1/*	$OpenBSD: pcons.c,v 1.15 2009/10/31 06:40:16 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
64struct 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
90typedef 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)
101void cn_init_magic(cnm_state_t *cnm);
102void cn_destroy_magic(cnm_state_t *cnm);
103int cn_set_magic(char *magic);
104int 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
131int pconsmatch(struct device *, void *, void *);
132void pconsattach(struct device *, struct device *, void *);
133
134struct cfattach pcons_ca = {
135	sizeof(struct pconssoftc), pconsmatch, pconsattach
136};
137
138struct cfdriver pcons_cd = {
139	NULL, "pcons", DV_TTY
140};
141
142extern struct cfdriver pcons_cd;
143static struct cnm_state pcons_cnm_state;
144
145static int pconsprobe(void);
146static void pcons_wsdisplay_init(struct pconssoftc *);
147extern struct consdev *cn_tab;
148
149cons_decl(prom_);
150
151int
152pconsmatch(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
164void pcons_poll(void *);
165
166void
167pconsattach(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
206void pconsstart(struct tty *);
207int pconsparam(struct tty *, struct termios *);
208
209int
210pconsopen(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();
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);
254}
255
256int
257pconsclose(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);
268	ttyclose(tp);
269	return 0;
270}
271
272int
273pconsread(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
284int
285pconswrite(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
296int
297pconsioctl(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
315struct tty *
316pconstty(dev)
317	dev_t dev;
318{
319	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
320
321	return sc->of_tty;
322}
323
324int
325pconsstop(tp, flag)
326	struct tty *tp;
327	int flag;
328{
329	return 0;
330}
331
332void
333pconsstart(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		KNOTE(&tp->t_wsel.si_note, 0);
363	}
364	splx(s);
365}
366
367int
368pconsparam(tp, t)
369	struct tty *tp;
370	struct termios *t;
371{
372	tp->t_ispeed = t->c_ispeed;
373	tp->t_ospeed = t->c_ospeed;
374	tp->t_cflag = t->c_cflag;
375	return 0;
376}
377
378void
379pcons_poll(aux)
380	void *aux;
381{
382	struct pconssoftc *sc = aux;
383	struct tty *tp = sc->of_tty;
384	char ch;
385
386	while (OF_read(stdin, &ch, 1) > 0) {
387		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
388		if (tp && (tp->t_state & TS_ISOPEN)) {
389			if (ch == '\b')
390				ch = '\177';
391			(*linesw[tp->t_line].l_rint)(ch, tp);
392		}
393	}
394	timeout_add(&sc->sc_poll_to, 1);
395}
396
397int
398pconsprobe()
399{
400	if (!stdin) stdin = OF_stdin();
401	if (!stdout) stdout = OF_stdout();
402
403	return (stdin && stdout);
404}
405
406void
407pcons_cnpollc(dev, on)
408	dev_t dev;
409	int on;
410{
411	struct pconssoftc *sc = NULL;
412
413	if (pcons_cd.cd_devs)
414		sc = pcons_cd.cd_devs[minor(dev)];
415
416	if (sc == NULL)
417		return;
418
419	if (on) {
420		if (sc->of_flags & OFPOLL)
421			timeout_del(&sc->sc_poll_to);
422		sc->of_flags &= ~OFPOLL;
423	} else {
424                /* Resuming kernel. */
425		if (!(sc->of_flags & OFPOLL)) {
426			sc->of_flags |= OFPOLL;
427			timeout_add(&sc->sc_poll_to, 1);
428		}
429	}
430}
431
432/* XXXXXXXX --- more cnmagic stuff. */
433#define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
434
435static unsigned short cn_magic[CNS_LEN];
436
437/*
438 * Initialize a cnm_state_t.
439 */
440void
441cn_init_magic(cnm_state_t *cnm)
442{
443	cnm->cnm_state = 0;
444	cnm->cnm_magic = cn_magic;
445}
446
447/*
448 * Destroy a cnm_state_t.
449 */
450void
451cn_destroy_magic(cnm_state_t *cnm)
452{
453	cnm->cnm_state = 0;
454	cnm->cnm_magic = NULL;
455}
456
457/*
458 * Translate a magic string to a state
459 * machine table.
460 */
461int
462cn_set_magic(char *magic)
463{
464	unsigned int i, c, n;
465	unsigned short m[CNS_LEN];
466
467	for (i=0; i<CNS_LEN; i++) {
468		c = (*magic++)&0xff;
469		n = *magic ? i+1 : CNS_TERM;
470		switch (c) {
471		case 0:
472			/* End of string */
473			if (i == 0) {
474				/* empty string? */
475				cn_magic[0] = 0;
476#ifdef DEBUG
477				printf("cn_set_magic(): empty!\n");
478#endif
479				return (0);
480			}
481			do {
482				cn_magic[i] = m[i];
483			} while (i--);
484			return(0);
485		case 0x27:
486			/* Escape sequence */
487			c = (*magic++)&0xff;
488			n = *magic ? i+1 : CNS_TERM;
489			switch (c) {
490			case 0x27:
491				break;
492			case 0x01:
493				/* BREAK */
494				c = CNC_BREAK;
495				break;
496			case 0x02:
497				/* NUL */
498				c = 0;
499				break;
500			}
501			/* FALLTHROUGH */
502		default:
503			/* Transition to the next state. */
504#ifdef DEBUG
505			if (!cold)
506				printf("mag %d %x:%x\n", i, c, n);
507#endif
508			m[i] = ENCODE_STATE(c, n);
509			break;
510		}
511	}
512	return (EINVAL);
513}
514
515/*
516 * Translate a state machine table back to
517 * a magic string.
518 */
519int
520cn_get_magic(char *magic, int maglen) {
521	unsigned int i, c;
522
523	for (i=0; i<CNS_LEN; i++) {
524		c = cn_magic[i];
525		/* Translate a character */
526		switch (CNS_MAGIC_VAL(c)) {
527		case CNC_BREAK:
528			*magic++ = 0x27;
529			*magic++ = 0x01;
530			break;
531		case 0:
532			*magic++ = 0x27;
533			*magic++ = 0x02;
534			break;
535		case 0x27:
536			*magic++ = 0x27;
537			*magic++ = 0x27;
538			break;
539		default:
540			*magic++ = (c&0x0ff);
541			break;
542		}
543		/* Now go to the next state */
544		i = CNS_MAGIC_NEXT(c);
545		if (i == CNS_TERM || i == 0) {
546			/* Either termination state or empty machine */
547			*magic++ = 0;
548			return (0);
549		}
550	}
551	return (EINVAL);
552}
553
554#if NWSDISPLAY > 0
555
556int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
557	    int *, int *, long *);
558void	pcons_cursor(void *, int, int, int);
559void	pcons_free_screen(void *, void *);
560int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
561int	pcons_mapchar(void *, int, unsigned int *);
562paddr_t	pcons_mmap(void *, off_t, int);
563int	pcons_putchar(void *, int, int, u_int, long);
564int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
565	    void *);
566
567struct wsdisplay_emulops pcons_emulops = {
568	NULL,
569	pcons_mapchar,
570	pcons_putchar
571};
572
573struct wsscreen_descr pcons_stdscreen = {
574	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
575};
576
577const struct wsscreen_descr *pcons_scrlist[] = {
578	&pcons_stdscreen
579};
580
581struct wsscreen_list pcons_screenlist = {
582	1, pcons_scrlist
583};
584
585struct wsdisplay_accessops pcons_accessops = {
586	pcons_ioctl,
587	pcons_mmap,
588	pcons_alloc_screen,
589	pcons_free_screen,
590	pcons_show_screen
591};
592
593int
594pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
595    int *curxp, int *curyp, long *attrp)
596{
597	struct pconssoftc *sc = v;
598	int *rowp, *colp;
599	int row, col;
600
601	if (sc->sc_nscreens > 0)
602		return (ENOMEM);
603
604	row = col = 0;
605	if (romgetcursoraddr(&rowp, &colp) == 0) {
606		if (rowp != NULL)
607			row = *rowp;
608		if (colp != NULL)
609			col = *colp;
610	}
611
612	*cookiep = v;
613	*attrp = 0;
614	*curxp = col;
615	*curyp = row;
616
617	sc->sc_nscreens++;
618	return (0);
619}
620
621void
622pcons_free_screen(void *v, void *cookie)
623{
624	struct pconssoftc *sc = v;
625
626	sc->sc_nscreens--;
627}
628
629int
630pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
631{
632	switch (cmd) {
633	case WSDISPLAYIO_GTYPE:
634		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
635		break;
636	default:
637		return (-1);
638	}
639
640	return (0);
641}
642
643paddr_t
644pcons_mmap(void *v, off_t off, int prot)
645{
646	return ((paddr_t)-1);
647}
648
649int
650pcons_show_screen(void *v, void *cookie, int waitok,
651    void (*cb)(void *, int, int), void *arg)
652{
653	return (0);
654}
655
656int
657pcons_mapchar(void *v, int uc, unsigned int *idx)
658{
659	if ((uc & 0xff) == uc) {
660		*idx = uc;
661		return (1);
662	} else {
663		*idx = ' ';
664		return (0);
665	}
666}
667
668int
669pcons_putchar(void *v, int row, int col, u_int uc, long attr)
670{
671	u_char buf[1];
672	int s;
673
674	buf[0] = (u_char)uc;
675	s = splhigh();
676	OF_write(stdout, &buf, 1);
677	splx(s);
678
679	return 0;
680}
681
682void
683pcons_wsdisplay_init(struct pconssoftc *sc)
684{
685	struct wsemuldisplaydev_attach_args waa;
686	int *rowp, *colp;
687	int options, row, col;
688
689	row = col = 0;
690	if (romgetcursoraddr(&rowp, &colp) == 0) {
691		if (rowp != NULL)
692			row = *rowp;
693		if (colp != NULL)
694			col = *colp;
695	}
696
697	options = OF_finddevice("/options");
698	pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
699	pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);
700
701	/*
702	 * We claim console here, because we can only get there if stdin
703	 * is a keyboard. However, the PROM could have been configured with
704	 * stdin being a keyboard and stdout being a serial sink.
705	 * But since this combination is not supported under OpenBSD at the
706	 * moment, it is reasonably safe to attach a dumb display as console
707	 * here.
708	 */
709	wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);
710
711	waa.console = 1;
712	waa.scrdata = &pcons_screenlist;
713	waa.accessops = &pcons_accessops;
714	waa.accesscookie = sc;
715	waa.defaultscreens = 1;
716
717	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
718}
719#endif
720