pcons.c revision 1.2
1/*	$OpenBSD: pcons.c,v 1.2 2001/09/04 13:22:13 art 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/* XXXXXXXX - this is in MI code in NetBSD */
60/*
61 * Stuff to handle debugger magic key sequences.
62 */
63#define CNS_LEN                 128
64#define CNS_MAGIC_VAL(x)        ((x)&0x1ff)
65#define CNS_MAGIC_NEXT(x)       (((x)>>9)&0x7f)
66#define CNS_TERM                0x7f    /* End of sequence */
67
68typedef struct cnm_state {
69	int	cnm_state;
70	u_short	*cnm_magic;
71} cnm_state_t;
72extern int db_console;
73#ifdef DDB
74#define cn_trap()	do { if (db_console) Debugger(); } while (0)
75#else
76#define cn_trap()
77#endif
78#define cn_isconsole(d)	((d) == cn_tab->cn_dev)
79void cn_init_magic __P((cnm_state_t *cnm));
80void cn_destroy_magic __P((cnm_state_t *cnm));
81int cn_set_magic __P((char *magic));
82int cn_get_magic __P((char *magic, int len));
83/* This should be called for each byte read */
84#ifndef cn_check_magic
85#define cn_check_magic(d, k, s)                                         \
86        do {                                                            \
87		if (cn_isconsole(d)) {                                  \
88			int v = (s).cnm_magic[(s).cnm_state];           \
89			if ((k) == CNS_MAGIC_VAL(v)) {                  \
90				(s).cnm_state = CNS_MAGIC_NEXT(v);      \
91				if ((s).cnm_state == CNS_TERM) {        \
92					cn_trap();                      \
93                                        (s).cnm_state = 0;              \
94				}                                       \
95			} else {                                        \
96				(s).cnm_state = 0;                      \
97			}                                               \
98		}                                                       \
99	} while (/* CONSTCOND */ 0)
100#endif
101
102/* Encode out-of-band events this way when passing to cn_check_magic() */
103#define CNC_BREAK               0x100
104
105/* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */
106
107#include <sparc64/dev/cons.h>
108
109static int pconsmatch __P((struct device *, void *, void *));
110static void pconsattach __P((struct device *, struct device *, void *));
111
112struct cfattach pcons_ca = {
113	sizeof(struct pconssoftc), pconsmatch, pconsattach
114};
115
116struct cfdriver pcons_cd = {
117	NULL, "pcons", DV_TTY
118};
119
120extern struct cfdriver pcons_cd;
121static struct cnm_state pcons_cnm_state;
122
123static int pconsprobe __P((void));
124extern struct consdev *cn_tab;
125
126static int
127pconsmatch(parent, match, aux)
128	struct device *parent;
129	void *match;
130	void *aux;
131{
132	struct mainbus_attach_args *ma = aux;
133	extern int  prom_cngetc __P((dev_t));
134
135	/* Only attach if no other console has attached. */
136	return ((strcmp("pcons", ma->ma_name) == 0) &&
137		(cn_tab->cn_getc == prom_cngetc));
138
139}
140
141static void pcons_poll __P((void *));
142
143static void
144pconsattach(parent, self, aux)
145	struct device *parent, *self;
146	void *aux;
147{
148	struct pconssoftc *sc = (struct pconssoftc *) self;
149
150	printf("\n");
151	if (!pconsprobe())
152		return;
153
154	cn_init_magic(&pcons_cnm_state);
155	cn_set_magic("+++++");
156	timeout_set(&sc->sc_poll_to, pcons_poll, sc);
157}
158
159static void pconsstart __P((struct tty *));
160static int pconsparam __P((struct tty *, struct termios *));
161
162int
163pconsopen(dev, flag, mode, p)
164	dev_t dev;
165	int flag, mode;
166	struct proc *p;
167{
168	struct pconssoftc *sc;
169	int unit = minor(dev);
170	struct tty *tp;
171
172	if (unit >= pcons_cd.cd_ndevs)
173		return ENXIO;
174	sc = pcons_cd.cd_devs[unit];
175	if (!sc)
176		return ENXIO;
177	if (!(tp = sc->of_tty)) {
178		sc->of_tty = tp = ttymalloc();
179		tty_attach(tp);
180	}
181	tp->t_oproc = pconsstart;
182	tp->t_param = pconsparam;
183	tp->t_dev = dev;
184	cn_tab->cn_dev = dev;
185	if (!(tp->t_state & TS_ISOPEN)) {
186		ttychars(tp);
187		tp->t_iflag = TTYDEF_IFLAG;
188		tp->t_oflag = TTYDEF_OFLAG;
189		tp->t_cflag = TTYDEF_CFLAG;
190		tp->t_lflag = TTYDEF_LFLAG;
191		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
192		pconsparam(tp, &tp->t_termios);
193		ttsetwater(tp);
194	} else if ((tp->t_state&TS_XCLUDE) && suser(p->p_ucred, &p->p_acflag))
195		return EBUSY;
196	tp->t_state |= TS_CARR_ON;
197
198	if (!(sc->of_flags & OFPOLL)) {
199		sc->of_flags |= OFPOLL;
200		timeout_add(&sc->sc_poll_to, 1);
201	}
202
203	return (*linesw[tp->t_line].l_open)(dev, tp);
204}
205
206int
207pconsclose(dev, flag, mode, p)
208	dev_t dev;
209	int flag, mode;
210	struct proc *p;
211{
212	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
213	struct tty *tp = sc->of_tty;
214
215	timeout_del(&sc->sc_poll_to);
216	sc->of_flags &= ~OFPOLL;
217	(*linesw[tp->t_line].l_close)(tp, flag);
218	ttyclose(tp);
219	return 0;
220}
221
222int
223pconsread(dev, uio, flag)
224	dev_t dev;
225	struct uio *uio;
226	int flag;
227{
228	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
229	struct tty *tp = sc->of_tty;
230
231	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
232}
233
234int
235pconswrite(dev, uio, flag)
236	dev_t dev;
237	struct uio *uio;
238	int flag;
239{
240	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
241	struct tty *tp = sc->of_tty;
242
243	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
244}
245
246int
247pconsioctl(dev, cmd, data, flag, p)
248	dev_t dev;
249	u_long cmd;
250	caddr_t data;
251	int flag;
252	struct proc *p;
253{
254	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
255	struct tty *tp = sc->of_tty;
256	int error;
257
258	if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
259		return error;
260	if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
261		return error;
262	return ENOTTY;
263}
264
265struct tty *
266pconstty(dev)
267	dev_t dev;
268{
269	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
270
271	return sc->of_tty;
272}
273
274int
275pconsstop(tp, flag)
276	struct tty *tp;
277	int flag;
278{
279	return 0;
280}
281
282static void
283pconsstart(tp)
284	struct tty *tp;
285{
286	struct clist *cl;
287	int s, len;
288	u_char buf[OFBURSTLEN];
289
290	s = spltty();
291	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
292		splx(s);
293		return;
294	}
295	tp->t_state |= TS_BUSY;
296	splx(s);
297	cl = &tp->t_outq;
298	len = q_to_b(cl, buf, OFBURSTLEN);
299	OF_write(stdout, buf, len);
300	s = spltty();
301	tp->t_state &= ~TS_BUSY;
302	if (cl->c_cc) {
303		tp->t_state |= TS_TIMEOUT;
304		timeout_add(&tp->t_rstrt_to, 1);
305	}
306	if (cl->c_cc <= tp->t_lowat) {
307		if (tp->t_state & TS_ASLEEP) {
308			tp->t_state &= ~TS_ASLEEP;
309			wakeup(cl);
310		}
311		selwakeup(&tp->t_wsel);
312	}
313	splx(s);
314}
315
316static int
317pconsparam(tp, t)
318	struct tty *tp;
319	struct termios *t;
320{
321	tp->t_ispeed = t->c_ispeed;
322	tp->t_ospeed = t->c_ospeed;
323	tp->t_cflag = t->c_cflag;
324	return 0;
325}
326
327static void
328pcons_poll(aux)
329	void *aux;
330{
331	struct pconssoftc *sc = aux;
332	struct tty *tp = sc->of_tty;
333	char ch;
334
335	while (OF_read(stdin, &ch, 1) > 0) {
336		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
337		if (tp && (tp->t_state & TS_ISOPEN))
338			(*linesw[tp->t_line].l_rint)(ch, tp);
339	}
340	timeout_add(&sc->sc_poll_to, 1);
341}
342
343int
344pconsprobe()
345{
346	if (!stdin) stdin = OF_stdin();
347	if (!stdout) stdout = OF_stdout();
348
349	return (stdin && stdout);
350}
351
352void
353pcons_cnpollc(dev, on)
354	dev_t dev;
355	int on;
356{
357	struct pconssoftc *sc = NULL;
358
359	if (pcons_cd.cd_devs)
360		sc = pcons_cd.cd_devs[minor(dev)];
361
362	if (on) {
363		if (!sc) return;
364		if (sc->of_flags & OFPOLL)
365			timeout_del(&sc->sc_poll_to);
366		sc->of_flags &= ~OFPOLL;
367	} else {
368                /* Resuming kernel. */
369		if (sc && !(sc->of_flags & OFPOLL)) {
370			sc->of_flags |= OFPOLL;
371			timeout_add(&sc->sc_poll_to, 1);
372		}
373	}
374}
375
376void pcons_dopoll __P((void));
377void
378pcons_dopoll() {
379		pcons_poll((void*)pcons_cd.cd_devs[0]);
380}
381
382/* XXXXXXXX --- more cnmagic stuff. */
383#define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
384
385static unsigned short cn_magic[CNS_LEN];
386
387/*
388 * Initialize a cnm_state_t.
389 */
390void
391cn_init_magic(cnm_state_t *cnm)
392{
393	cnm->cnm_state = 0;
394	cnm->cnm_magic = cn_magic;
395}
396
397/*
398 * Destroy a cnm_state_t.
399 */
400void
401cn_destroy_magic(cnm_state_t *cnm)
402{
403	cnm->cnm_state = 0;
404	cnm->cnm_magic = NULL;
405}
406
407/*
408 * Translate a magic string to a state
409 * machine table.
410 */
411int
412cn_set_magic(char *magic)
413{
414	unsigned int i, c, n;
415	unsigned short m[CNS_LEN];
416
417	for (i=0; i<CNS_LEN; i++) {
418		c = (*magic++)&0xff;
419		n = *magic ? i+1 : CNS_TERM;
420		switch (c) {
421		case 0:
422			/* End of string */
423			if (i == 0) {
424				/* empty string? */
425				cn_magic[0] = 0;
426#ifdef DEBUG
427				printf("cn_set_magic(): empty!\n");
428#endif
429				return (0);
430			}
431			do {
432				cn_magic[i] = m[i];
433			} while (i--);
434			return(0);
435		case 0x27:
436			/* Escape sequence */
437			c = (*magic++)&0xff;
438			n = *magic ? i+1 : CNS_TERM;
439			switch (c) {
440			case 0x27:
441				break;
442			case 0x01:
443				/* BREAK */
444				c = CNC_BREAK;
445				break;
446			case 0x02:
447				/* NUL */
448				c = 0;
449				break;
450			}
451			/* FALLTHROUGH */
452		default:
453			/* Transition to the next state. */
454#ifdef DEBUG
455			if (!cold)
456				printf("mag %d %x:%x\n", i, c, n);
457#endif
458			m[i] = ENCODE_STATE(c, n);
459			break;
460		}
461	}
462	return (EINVAL);
463}
464
465/*
466 * Translatea state machine table back to
467 * a magic string.
468 */
469int
470cn_get_magic(char *magic, int maglen) {
471	unsigned int i, c;
472
473	for (i=0; i<CNS_LEN; i++) {
474		c = cn_magic[i];
475		/* Translate a character */
476		switch (CNS_MAGIC_VAL(c)) {
477		case CNC_BREAK:
478			*magic++ = 0x27;
479			*magic++ = 0x01;
480			break;
481		case 0:
482			*magic++ = 0x27;
483			*magic++ = 0x02;
484			break;
485		case 0x27:
486			*magic++ = 0x27;
487			*magic++ = 0x27;
488			break;
489		default:
490			*magic++ = (c&0x0ff);
491			break;
492		}
493		/* Now go to the next state */
494		i = CNS_MAGIC_NEXT(c);
495		if (i == CNS_TERM || i == 0) {
496			/* Either termination state or empty machine */
497			*magic++ = 0;
498			return (0);
499		}
500	}
501	return (EINVAL);
502}
503
504