pcons.c revision 1.1
1/*	$OpenBSD: pcons.c,v 1.1 2001/08/19 05:21:37 jason 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	tp->t_oproc = pconsstart;
180	tp->t_param = pconsparam;
181	tp->t_dev = dev;
182	cn_tab->cn_dev = dev;
183	if (!(tp->t_state & TS_ISOPEN)) {
184		ttychars(tp);
185		tp->t_iflag = TTYDEF_IFLAG;
186		tp->t_oflag = TTYDEF_OFLAG;
187		tp->t_cflag = TTYDEF_CFLAG;
188		tp->t_lflag = TTYDEF_LFLAG;
189		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
190		pconsparam(tp, &tp->t_termios);
191		ttsetwater(tp);
192	} else if ((tp->t_state&TS_XCLUDE) && suser(p->p_ucred, &p->p_acflag))
193		return EBUSY;
194	tp->t_state |= TS_CARR_ON;
195
196	if (!(sc->of_flags & OFPOLL)) {
197		sc->of_flags |= OFPOLL;
198		timeout_add(&sc->sc_poll_to, 1);
199	}
200
201	return (*linesw[tp->t_line].l_open)(dev, tp);
202}
203
204int
205pconsclose(dev, flag, mode, p)
206	dev_t dev;
207	int flag, mode;
208	struct proc *p;
209{
210	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
211	struct tty *tp = sc->of_tty;
212
213	timeout_del(&sc->sc_poll_to);
214	sc->of_flags &= ~OFPOLL;
215	(*linesw[tp->t_line].l_close)(tp, flag);
216	ttyclose(tp);
217	return 0;
218}
219
220int
221pconsread(dev, uio, flag)
222	dev_t dev;
223	struct uio *uio;
224	int flag;
225{
226	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
227	struct tty *tp = sc->of_tty;
228
229	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
230}
231
232int
233pconswrite(dev, uio, flag)
234	dev_t dev;
235	struct uio *uio;
236	int flag;
237{
238	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
239	struct tty *tp = sc->of_tty;
240
241	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
242}
243
244int
245pconsioctl(dev, cmd, data, flag, p)
246	dev_t dev;
247	u_long cmd;
248	caddr_t data;
249	int flag;
250	struct proc *p;
251{
252	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
253	struct tty *tp = sc->of_tty;
254	int error;
255
256	if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
257		return error;
258	if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
259		return error;
260	return ENOTTY;
261}
262
263struct tty *
264pconstty(dev)
265	dev_t dev;
266{
267	struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
268
269	return sc->of_tty;
270}
271
272int
273pconsstop(tp, flag)
274	struct tty *tp;
275	int flag;
276{
277	return 0;
278}
279
280static void
281pconsstart(tp)
282	struct tty *tp;
283{
284	struct clist *cl;
285	int s, len;
286	u_char buf[OFBURSTLEN];
287
288	s = spltty();
289	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
290		splx(s);
291		return;
292	}
293	tp->t_state |= TS_BUSY;
294	splx(s);
295	cl = &tp->t_outq;
296	len = q_to_b(cl, buf, OFBURSTLEN);
297	OF_write(stdout, buf, len);
298	s = spltty();
299	tp->t_state &= ~TS_BUSY;
300	if (cl->c_cc) {
301		tp->t_state |= TS_TIMEOUT;
302		timeout_add(&tp->t_rstrt_to, 1);
303	}
304	if (cl->c_cc <= tp->t_lowat) {
305		if (tp->t_state & TS_ASLEEP) {
306			tp->t_state &= ~TS_ASLEEP;
307			wakeup(cl);
308		}
309		selwakeup(&tp->t_wsel);
310	}
311	splx(s);
312}
313
314static int
315pconsparam(tp, t)
316	struct tty *tp;
317	struct termios *t;
318{
319	tp->t_ispeed = t->c_ispeed;
320	tp->t_ospeed = t->c_ospeed;
321	tp->t_cflag = t->c_cflag;
322	return 0;
323}
324
325static void
326pcons_poll(aux)
327	void *aux;
328{
329	struct pconssoftc *sc = aux;
330	struct tty *tp = sc->of_tty;
331	char ch;
332
333	while (OF_read(stdin, &ch, 1) > 0) {
334		cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
335		if (tp && (tp->t_state & TS_ISOPEN))
336			(*linesw[tp->t_line].l_rint)(ch, tp);
337	}
338	timeout_add(&sc->sc_poll_to, 1);
339}
340
341int
342pconsprobe()
343{
344	if (!stdin) stdin = OF_stdin();
345	if (!stdout) stdout = OF_stdout();
346
347	return (stdin && stdout);
348}
349
350void
351pcons_cnpollc(dev, on)
352	dev_t dev;
353	int on;
354{
355	struct pconssoftc *sc = NULL;
356
357	if (pcons_cd.cd_devs)
358		sc = pcons_cd.cd_devs[minor(dev)];
359
360	if (on) {
361		if (!sc) return;
362		if (sc->of_flags & OFPOLL)
363			timeout_del(&sc->sc_poll_to);
364		sc->of_flags &= ~OFPOLL;
365	} else {
366                /* Resuming kernel. */
367		if (sc && !(sc->of_flags & OFPOLL)) {
368			sc->of_flags |= OFPOLL;
369			timeout_add(&sc->sc_poll_to, 1);
370		}
371	}
372}
373
374void pcons_dopoll __P((void));
375void
376pcons_dopoll() {
377		pcons_poll((void*)pcons_cd.cd_devs[0]);
378}
379
380/* XXXXXXXX --- more cnmagic stuff. */
381#define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
382
383static unsigned short cn_magic[CNS_LEN];
384
385/*
386 * Initialize a cnm_state_t.
387 */
388void
389cn_init_magic(cnm_state_t *cnm)
390{
391	cnm->cnm_state = 0;
392	cnm->cnm_magic = cn_magic;
393}
394
395/*
396 * Destroy a cnm_state_t.
397 */
398void
399cn_destroy_magic(cnm_state_t *cnm)
400{
401	cnm->cnm_state = 0;
402	cnm->cnm_magic = NULL;
403}
404
405/*
406 * Translate a magic string to a state
407 * machine table.
408 */
409int
410cn_set_magic(char *magic)
411{
412	unsigned int i, c, n;
413	unsigned short m[CNS_LEN];
414
415	for (i=0; i<CNS_LEN; i++) {
416		c = (*magic++)&0xff;
417		n = *magic ? i+1 : CNS_TERM;
418		switch (c) {
419		case 0:
420			/* End of string */
421			if (i == 0) {
422				/* empty string? */
423				cn_magic[0] = 0;
424#ifdef DEBUG
425				printf("cn_set_magic(): empty!\n");
426#endif
427				return (0);
428			}
429			do {
430				cn_magic[i] = m[i];
431			} while (i--);
432			return(0);
433		case 0x27:
434			/* Escape sequence */
435			c = (*magic++)&0xff;
436			n = *magic ? i+1 : CNS_TERM;
437			switch (c) {
438			case 0x27:
439				break;
440			case 0x01:
441				/* BREAK */
442				c = CNC_BREAK;
443				break;
444			case 0x02:
445				/* NUL */
446				c = 0;
447				break;
448			}
449			/* FALLTHROUGH */
450		default:
451			/* Transition to the next state. */
452#ifdef DEBUG
453			if (!cold)
454				printf("mag %d %x:%x\n", i, c, n);
455#endif
456			m[i] = ENCODE_STATE(c, n);
457			break;
458		}
459	}
460	return (EINVAL);
461}
462
463/*
464 * Translatea state machine table back to
465 * a magic string.
466 */
467int
468cn_get_magic(char *magic, int maglen) {
469	unsigned int i, c;
470
471	for (i=0; i<CNS_LEN; i++) {
472		c = cn_magic[i];
473		/* Translate a character */
474		switch (CNS_MAGIC_VAL(c)) {
475		case CNC_BREAK:
476			*magic++ = 0x27;
477			*magic++ = 0x01;
478			break;
479		case 0:
480			*magic++ = 0x27;
481			*magic++ = 0x02;
482			break;
483		case 0x27:
484			*magic++ = 0x27;
485			*magic++ = 0x27;
486			break;
487		default:
488			*magic++ = (c&0x0ff);
489			break;
490		}
491		/* Now go to the next state */
492		i = CNS_MAGIC_NEXT(c);
493		if (i == CNS_TERM || i == 0) {
494			/* Either termination state or empty machine */
495			*magic++ = 0;
496			return (0);
497		}
498	}
499	return (EINVAL);
500}
501
502