pcons.c revision 1.19
1/*	$OpenBSD: pcons.c,v 1.19 2013/10/18 17:38:33 miod 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(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
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, p);
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	}
363	splx(s);
364}
365
366int
367pconsparam(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
377void
378pcons_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
396int
397pconsprobe()
398{
399	if (!stdin) stdin = OF_stdin();
400	if (!stdout) stdout = OF_stdout();
401
402	return (stdin && stdout);
403}
404
405void
406pcons_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
434static unsigned short cn_magic[CNS_LEN];
435
436/*
437 * Initialize a cnm_state_t.
438 */
439void
440cn_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 */
449void
450cn_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 */
460int
461cn_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 */
518int
519cn_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
555int	pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
556	    int *, int *, long *);
557void	pcons_cursor(void *, int, int, int);
558void	pcons_free_screen(void *, void *);
559int	pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
560int	pcons_mapchar(void *, int, unsigned int *);
561paddr_t	pcons_mmap(void *, off_t, int);
562int	pcons_putchar(void *, int, int, u_int, long);
563int	pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
564	    void *);
565
566struct wsdisplay_emulops pcons_emulops = {
567	NULL,
568	pcons_mapchar,
569	pcons_putchar
570};
571
572struct wsscreen_descr pcons_stdscreen = {
573	"dumb", 80, 34, &pcons_emulops, 12, 22, 0
574};
575
576const struct wsscreen_descr *pcons_scrlist[] = {
577	&pcons_stdscreen
578};
579
580struct wsscreen_list pcons_screenlist = {
581	1, pcons_scrlist
582};
583
584struct wsdisplay_accessops pcons_accessops = {
585	pcons_ioctl,
586	pcons_mmap,
587	pcons_alloc_screen,
588	pcons_free_screen,
589	pcons_show_screen
590};
591
592int
593pcons_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
620void
621pcons_free_screen(void *v, void *cookie)
622{
623	struct pconssoftc *sc = v;
624
625	sc->sc_nscreens--;
626}
627
628int
629pcons_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
642paddr_t
643pcons_mmap(void *v, off_t off, int prot)
644{
645	return ((paddr_t)-1);
646}
647
648int
649pcons_show_screen(void *v, void *cookie, int waitok,
650    void (*cb)(void *, int, int), void *arg)
651{
652	return (0);
653}
654
655int
656pcons_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
667int
668pcons_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
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