ofw_console.c revision 159065
1238106Sdes/*-
2238106Sdes * Copyright (C) 2001 Benno Rice.
3238106Sdes * All rights reserved.
4238106Sdes *
5238106Sdes * Redistribution and use in source and binary forms, with or without
6238106Sdes * modification, are permitted provided that the following conditions
7238106Sdes * are met:
8238106Sdes * 1. Redistributions of source code must retain the above copyright
9238106Sdes *    notice, this list of conditions and the following disclaimer.
10238106Sdes * 2. Redistributions in binary form must reproduce the above copyright
11238106Sdes *    notice, this list of conditions and the following disclaimer in the
12238106Sdes *    documentation and/or other materials provided with the distribution.
13238106Sdes *
14238106Sdes * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15238106Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16238106Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17238106Sdes * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18238106Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19238106Sdes * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20238106Sdes * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21238106Sdes * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22238106Sdes * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23238106Sdes * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24266114Sdes */
25266114Sdes
26266114Sdes#include <sys/cdefs.h>
27266114Sdes__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_console.c 159065 2006-05-30 07:56:57Z phk $");
28266114Sdes
29266114Sdes#include "opt_comconsole.h"
30266114Sdes#include "opt_ofw.h"
31266114Sdes
32266114Sdes#include <sys/param.h>
33266114Sdes#include <sys/kdb.h>
34238106Sdes#include <sys/kernel.h>
35238106Sdes#include <sys/systm.h>
36238106Sdes#include <sys/types.h>
37238106Sdes#include <sys/conf.h>
38238106Sdes#include <sys/cons.h>
39238106Sdes#include <sys/consio.h>
40238106Sdes#include <sys/tty.h>
41238106Sdes
42238106Sdes#include <dev/ofw/openfirm.h>
43238106Sdes
44238106Sdes#include <ddb/ddb.h>
45238106Sdes
46238106Sdes#ifndef	OFWCONS_POLL_HZ
47238106Sdes#define	OFWCONS_POLL_HZ	4	/* 50-100 works best on Ultra2 */
48238106Sdes#endif
49238106Sdes#define OFBURSTLEN	128	/* max number of bytes to write in one chunk */
50238106Sdes
51238106Sdesstatic d_open_t		ofw_dev_open;
52238106Sdesstatic d_close_t	ofw_dev_close;
53238106Sdes
54238106Sdesstatic struct cdevsw ofw_cdevsw = {
55238106Sdes	.d_version =	D_VERSION,
56238106Sdes	.d_open =	ofw_dev_open,
57238106Sdes	.d_close =	ofw_dev_close,
58276605Sdes	.d_name =	"ofw",
59276605Sdes	.d_flags =	D_TTY | D_NEEDGIANT,
60276605Sdes};
61238106Sdes
62238106Sdesstatic struct tty		*ofw_tp = NULL;
63238106Sdesstatic int			polltime;
64238106Sdesstatic struct callout_handle	ofw_timeouthandle
65238106Sdes    = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
66238106Sdes
67238106Sdes#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
68238106Sdesstatic int			alt_break_state;
69238106Sdes#endif
70238106Sdes
71238106Sdesstatic void	ofw_tty_start(struct tty *);
72238106Sdesstatic int	ofw_tty_param(struct tty *, struct termios *);
73276605Sdesstatic void	ofw_tty_stop(struct tty *, int);
74238106Sdesstatic void	ofw_timeout(void *);
75238106Sdes
76238106Sdesstatic cn_probe_t	ofw_cnprobe;
77238106Sdesstatic cn_init_t	ofw_cninit;
78238106Sdesstatic cn_term_t	ofw_cnterm;
79238106Sdesstatic cn_getc_t	ofw_cngetc;
80238106Sdesstatic cn_putc_t	ofw_cnputc;
81238106Sdes
82238106SdesCONSOLE_DRIVER(ofw);
83238106Sdes
84238106Sdesstatic void
85238106Sdescn_drvinit(void *unused)
86238106Sdes{
87238106Sdes	phandle_t options;
88238106Sdes	char output[32];
89238106Sdes	struct cdev *dev;
90238106Sdes
91238106Sdes	if (ofw_consdev.cn_pri != CN_DEAD &&
92238106Sdes	    ofw_consdev.cn_name[0] != '\0') {
93238106Sdes		if ((options = OF_finddevice("/options")) == -1 ||
94238106Sdes		    OF_getprop(options, "output-device", output,
95238106Sdes		    sizeof(output)) == -1)
96238106Sdes			return;
97238106Sdes		/*
98238106Sdes		 * XXX: This is a hack and it may result in two /dev/ttya
99238106Sdes		 * XXX: devices on platforms where the sab driver works.
100238106Sdes		 */
101238106Sdes		dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
102238106Sdes		    output);
103238106Sdes		make_dev_alias(dev, "ofwcons");
104238106Sdes	}
105238106Sdes}
106238106Sdes
107238106SdesSYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL)
108238106Sdes
109238106Sdesstatic int	stdin;
110238106Sdesstatic int	stdout;
111238106Sdes
112238106Sdesstatic int
113238106Sdesofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td)
114238106Sdes{
115238106Sdes	struct	tty *tp;
116238106Sdes	int	unit;
117238106Sdes	int	error, setuptimeout;
118238106Sdes
119238106Sdes	error = 0;
120238106Sdes	setuptimeout = 0;
121238106Sdes	unit = minor(dev);
122238106Sdes
123238106Sdes	/*
124238106Sdes	 * XXX: BAD, should happen at attach time
125238106Sdes	 */
126238106Sdes	if (dev->si_tty == NULL) {
127238106Sdes		ofw_tp = ttyalloc();
128238106Sdes		dev->si_tty = ofw_tp;
129238106Sdes		ofw_tp->t_dev = dev;
130238106Sdes	}
131238106Sdes	tp = dev->si_tty;
132238106Sdes
133238106Sdes	tp->t_oproc = ofw_tty_start;
134238106Sdes	tp->t_param = ofw_tty_param;
135238106Sdes	tp->t_stop = ofw_tty_stop;
136238106Sdes	tp->t_dev = dev;
137238106Sdes
138238106Sdes	if ((tp->t_state & TS_ISOPEN) == 0) {
139238106Sdes		tp->t_state |= TS_CARR_ON;
140238106Sdes		ttyconsolemode(tp, 0);
141238106Sdes
142238106Sdes		setuptimeout = 1;
143238106Sdes	} else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
144238106Sdes		return (EBUSY);
145238106Sdes	}
146238106Sdes
147238106Sdes	error = ttyld_open(tp, dev);
148238106Sdes
149238106Sdes	if (error == 0 && setuptimeout) {
150238106Sdes		polltime = hz / OFWCONS_POLL_HZ;
151238106Sdes		if (polltime < 1) {
152238106Sdes			polltime = 1;
153238106Sdes		}
154238106Sdes
155238106Sdes		ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
156238106Sdes	}
157238106Sdes
158238106Sdes	return (error);
159238106Sdes}
160238106Sdes
161238106Sdesstatic int
162238106Sdesofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td)
163238106Sdes{
164238106Sdes	int	unit;
165238106Sdes	struct	tty *tp;
166238106Sdes
167238106Sdes	unit = minor(dev);
168238106Sdes	tp = dev->si_tty;
169238106Sdes
170238106Sdes	if (unit != 0) {
171238106Sdes		return (ENXIO);
172238106Sdes	}
173238106Sdes
174238106Sdes	/* XXX Should be replaced with callout_stop(9) */
175238106Sdes	untimeout(ofw_timeout, tp, ofw_timeouthandle);
176238106Sdes	ttyld_close(tp, flag);
177238106Sdes	tty_close(tp);
178238106Sdes
179238106Sdes	return (0);
180238106Sdes}
181238106Sdes
182238106Sdes
183238106Sdesstatic int
184238106Sdesofw_tty_param(struct tty *tp, struct termios *t)
185238106Sdes{
186238106Sdes
187238106Sdes	return (0);
188238106Sdes}
189238106Sdes
190238106Sdesstatic void
191238106Sdesofw_tty_start(struct tty *tp)
192238106Sdes{
193238106Sdes	struct clist *cl;
194238106Sdes	int len;
195238106Sdes	u_char buf[OFBURSTLEN];
196238106Sdes
197238106Sdes
198238106Sdes	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
199238106Sdes		return;
200238106Sdes
201238106Sdes	tp->t_state |= TS_BUSY;
202238106Sdes	cl = &tp->t_outq;
203238106Sdes	len = q_to_b(cl, buf, OFBURSTLEN);
204238106Sdes	OF_write(stdout, buf, len);
205238106Sdes	tp->t_state &= ~TS_BUSY;
206238106Sdes
207238106Sdes	ttwwakeup(tp);
208238106Sdes}
209238106Sdes
210238106Sdesstatic void
211238106Sdesofw_tty_stop(struct tty *tp, int flag)
212238106Sdes{
213238106Sdes
214238106Sdes	if (tp->t_state & TS_BUSY) {
215238106Sdes		if ((tp->t_state & TS_TTSTOP) == 0) {
216238106Sdes			tp->t_state |= TS_FLUSH;
217238106Sdes		}
218238106Sdes	}
219238106Sdes}
220238106Sdes
221238106Sdesstatic void
222238106Sdesofw_timeout(void *v)
223238106Sdes{
224238106Sdes	struct	tty *tp;
225238106Sdes	int 	c;
226238106Sdes
227238106Sdes	tp = (struct tty *)v;
228238106Sdes
229238106Sdes	while ((c = ofw_cngetc(NULL)) != -1) {
230238106Sdes		if (tp->t_state & TS_ISOPEN) {
231238106Sdes			ttyld_rint(tp, c);
232238106Sdes		}
233238106Sdes	}
234238106Sdes
235238106Sdes	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
236238106Sdes}
237238106Sdes
238238106Sdesstatic void
239238106Sdesofw_cnprobe(struct consdev *cp)
240238106Sdes{
241238106Sdes	int chosen;
242238106Sdes
243238106Sdes	if ((chosen = OF_finddevice("/chosen")) == -1) {
244238106Sdes		cp->cn_pri = CN_DEAD;
245238106Sdes		return;
246238106Sdes	}
247238106Sdes
248238106Sdes	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
249238106Sdes		cp->cn_pri = CN_DEAD;
250238106Sdes		return;
251238106Sdes	}
252238106Sdes
253238106Sdes	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
254238106Sdes		cp->cn_pri = CN_DEAD;
255238106Sdes		return;
256238106Sdes	}
257238106Sdes
258238106Sdes	cp->cn_pri = CN_LOW;
259238106Sdes}
260238106Sdes
261238106Sdesstatic void
262238106Sdesofw_cninit(struct consdev *cp)
263238106Sdes{
264238106Sdes
265238106Sdes	/* XXX: This is the alias, but that should be good enough */
266238106Sdes	sprintf(cp->cn_name, "ofwcons");
267238106Sdes	cp->cn_tp = ofw_tp;
268238106Sdes}
269238106Sdes
270238106Sdesstatic void
271238106Sdesofw_cnterm(struct consdev *cp)
272238106Sdes{
273238106Sdes}
274238106Sdes
275238106Sdesstatic int
276238106Sdesofw_cngetc(struct consdev *cp)
277238106Sdes{
278238106Sdes	unsigned char ch;
279238106Sdes
280238106Sdes	if (OF_read(stdin, &ch, 1) > 0) {
281238106Sdes#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
282238106Sdes		if (kdb_alt_break(ch, &alt_break_state))
283238106Sdes			kdb_enter("Break sequence on console");
284238106Sdes#endif
285238106Sdes		return (ch);
286238106Sdes	}
287238106Sdes
288238106Sdes	return (-1);
289238106Sdes}
290238106Sdes
291238106Sdesstatic void
292238106Sdesofw_cnputc(struct consdev *cp, int c)
293238106Sdes{
294238106Sdes	char cbuf;
295238106Sdes
296238106Sdes	if (c == '\n') {
297		cbuf = '\r';
298		OF_write(stdout, &cbuf, 1);
299	}
300
301	cbuf = c;
302	OF_write(stdout, &cbuf, 1);
303}
304