ofw_console.c revision 178766
1/*-
2 * Copyright (C) 2001 Benno Rice.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_console.c 178766 2008-05-04 23:29:38Z peter $");
28
29#include "opt_comconsole.h"
30#include "opt_ofw.h"
31
32#include <sys/param.h>
33#include <sys/kdb.h>
34#include <sys/kernel.h>
35#include <sys/priv.h>
36#include <sys/systm.h>
37#include <sys/types.h>
38#include <sys/conf.h>
39#include <sys/cons.h>
40#include <sys/consio.h>
41#include <sys/tty.h>
42
43#include <dev/ofw/openfirm.h>
44
45#include <ddb/ddb.h>
46
47#ifndef	OFWCONS_POLL_HZ
48#define	OFWCONS_POLL_HZ	4	/* 50-100 works best on Ultra2 */
49#endif
50#define OFBURSTLEN	128	/* max number of bytes to write in one chunk */
51
52static d_open_t		ofw_dev_open;
53static d_close_t	ofw_dev_close;
54
55static struct cdevsw ofw_cdevsw = {
56	.d_version =	D_VERSION,
57	.d_open =	ofw_dev_open,
58	.d_close =	ofw_dev_close,
59	.d_name =	"ofw",
60	.d_flags =	D_TTY | D_NEEDGIANT,
61};
62
63static struct tty		*ofw_tp = NULL;
64static int			polltime;
65static struct callout_handle	ofw_timeouthandle
66    = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
67
68#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
69static int			alt_break_state;
70#endif
71
72static void	ofw_tty_start(struct tty *);
73static int	ofw_tty_param(struct tty *, struct termios *);
74static void	ofw_tty_stop(struct tty *, int);
75static void	ofw_timeout(void *);
76
77static cn_probe_t	ofw_cnprobe;
78static cn_init_t	ofw_cninit;
79static cn_term_t	ofw_cnterm;
80static cn_getc_t	ofw_cngetc;
81static cn_putc_t	ofw_cnputc;
82
83CONSOLE_DRIVER(ofw);
84
85static void
86cn_drvinit(void *unused)
87{
88	phandle_t options;
89	char output[32];
90	struct cdev *dev;
91
92	if (ofw_consdev.cn_pri != CN_DEAD &&
93	    ofw_consdev.cn_name[0] != '\0') {
94		if ((options = OF_finddevice("/options")) == -1 ||
95		    OF_getprop(options, "output-device", output,
96		    sizeof(output)) == -1)
97			return;
98		/*
99		 * XXX: This is a hack and it may result in two /dev/ttya
100		 * XXX: devices on platforms where the sab driver works.
101		 */
102		dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
103		    output);
104		make_dev_alias(dev, "ofwcons");
105	}
106}
107
108SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL);
109
110static int	stdin;
111static int	stdout;
112
113static int
114ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td)
115{
116	struct	tty *tp;
117	int	unit;
118	int	error, setuptimeout;
119
120	error = 0;
121	setuptimeout = 0;
122	unit = minor(dev);
123
124	/*
125	 * XXX: BAD, should happen at attach time
126	 */
127	if (dev->si_tty == NULL) {
128		ofw_tp = ttyalloc();
129		dev->si_tty = ofw_tp;
130		ofw_tp->t_dev = dev;
131	}
132	tp = dev->si_tty;
133
134	tp->t_oproc = ofw_tty_start;
135	tp->t_param = ofw_tty_param;
136	tp->t_stop = ofw_tty_stop;
137	tp->t_dev = dev;
138
139	if ((tp->t_state & TS_ISOPEN) == 0) {
140		tp->t_state |= TS_CARR_ON;
141		ttyconsolemode(tp, 0);
142
143		setuptimeout = 1;
144	} else if ((tp->t_state & TS_XCLUDE) &&
145	    priv_check(td, PRIV_TTY_EXCLUSIVE)) {
146		return (EBUSY);
147	}
148
149	error = ttyld_open(tp, dev);
150
151	if (error == 0 && setuptimeout) {
152		polltime = hz / OFWCONS_POLL_HZ;
153		if (polltime < 1) {
154			polltime = 1;
155		}
156
157		ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
158	}
159
160	return (error);
161}
162
163static int
164ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td)
165{
166	int	unit;
167	struct	tty *tp;
168
169	unit = minor(dev);
170	tp = dev->si_tty;
171
172	if (unit != 0) {
173		return (ENXIO);
174	}
175
176	/* XXX Should be replaced with callout_stop(9) */
177	untimeout(ofw_timeout, tp, ofw_timeouthandle);
178	ttyld_close(tp, flag);
179	tty_close(tp);
180
181	return (0);
182}
183
184
185static int
186ofw_tty_param(struct tty *tp, struct termios *t)
187{
188
189	return (0);
190}
191
192static void
193ofw_tty_start(struct tty *tp)
194{
195	struct clist *cl;
196	int len;
197	u_char buf[OFBURSTLEN];
198
199
200	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
201		return;
202
203	tp->t_state |= TS_BUSY;
204	cl = &tp->t_outq;
205	len = q_to_b(cl, buf, OFBURSTLEN);
206	OF_write(stdout, buf, len);
207	tp->t_state &= ~TS_BUSY;
208
209	ttwwakeup(tp);
210}
211
212static void
213ofw_tty_stop(struct tty *tp, int flag)
214{
215
216	if (tp->t_state & TS_BUSY) {
217		if ((tp->t_state & TS_TTSTOP) == 0) {
218			tp->t_state |= TS_FLUSH;
219		}
220	}
221}
222
223static void
224ofw_timeout(void *v)
225{
226	struct	tty *tp;
227	int 	c;
228
229	tp = (struct tty *)v;
230
231	while ((c = ofw_cngetc(NULL)) != -1) {
232		if (tp->t_state & TS_ISOPEN) {
233			ttyld_rint(tp, c);
234		}
235	}
236
237	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
238}
239
240static void
241ofw_cnprobe(struct consdev *cp)
242{
243	int chosen;
244
245	if ((chosen = OF_finddevice("/chosen")) == -1) {
246		cp->cn_pri = CN_DEAD;
247		return;
248	}
249
250	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
251		cp->cn_pri = CN_DEAD;
252		return;
253	}
254
255	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
256		cp->cn_pri = CN_DEAD;
257		return;
258	}
259
260	cp->cn_pri = CN_LOW;
261}
262
263static void
264ofw_cninit(struct consdev *cp)
265{
266
267	/* XXX: This is the alias, but that should be good enough */
268	sprintf(cp->cn_name, "ofwcons");
269	cp->cn_tp = ofw_tp;
270}
271
272static void
273ofw_cnterm(struct consdev *cp)
274{
275}
276
277static int
278ofw_cngetc(struct consdev *cp)
279{
280	unsigned char ch;
281
282	if (OF_read(stdin, &ch, 1) > 0) {
283#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
284		int kdb_brk;
285
286		if ((kdb_brk = kdb_alt_break(ch, &alt_break_state)) != 0) {
287			switch (kdb_brk) {
288			case KDB_REQ_DEBUGGER:
289				kdb_enter(KDB_WHY_BREAK,
290				    "Break sequence on console");
291				break;
292			case KDB_REQ_PANIC:
293				kdb_panic("Panic sequence on console");
294				break;
295			case KDB_REQ_REBOOT:
296				kdb_reboot();
297				break;
298
299			}
300		}
301#endif
302		return (ch);
303	}
304
305	return (-1);
306}
307
308static void
309ofw_cnputc(struct consdev *cp, int c)
310{
311	char cbuf;
312
313	if (c == '\n') {
314		cbuf = '\r';
315		OF_write(stdout, &cbuf, 1);
316	}
317
318	cbuf = c;
319	OF_write(stdout, &cbuf, 1);
320}
321