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