ofw_console.c revision 111194
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#ifndef lint
27static const char rcsid[] =
28  "$FreeBSD: head/sys/dev/ofw/ofw_console.c 111194 2003-02-20 20:54:45Z phk $";
29#endif /* not lint */
30
31#include "opt_ddb.h"
32#include "opt_comconsole.h"
33
34#include <sys/param.h>
35#include <sys/kernel.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#define	OFW_POLL_HZ	4
48
49static d_open_t		ofw_dev_open;
50static d_close_t	ofw_dev_close;
51static d_ioctl_t	ofw_dev_ioctl;
52
53#define	CDEV_MAJOR	97
54
55static struct cdevsw ofw_cdevsw = {
56	/* open */	ofw_dev_open,
57	/* close */	ofw_dev_close,
58	/* read */	ttyread,
59	/* write */	ttywrite,
60	/* ioctl */	ofw_dev_ioctl,
61	/* poll */	ttypoll,
62	/* mmap */	nommap,
63	/* strategy */	nostrategy,
64	/* name */	"ofw",
65	/* major */	CDEV_MAJOR,
66	/* dump */	nodump,
67	/* psize */	nopsize,
68	/* flags */	0,
69};
70
71static struct tty		*ofw_tp = NULL;
72static int			polltime;
73static struct callout_handle	ofw_timeouthandle
74    = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
75
76#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
77static int			alt_break_state;
78#endif
79
80static void	ofw_tty_start(struct tty *);
81static int	ofw_tty_param(struct tty *, struct termios *);
82static void	ofw_tty_stop(struct tty *, int);
83static void	ofw_timeout(void *);
84
85static cn_probe_t	ofw_cons_probe;
86static cn_init_t	ofw_cons_init;
87static cn_getc_t	ofw_cons_getc;
88static cn_checkc_t 	ofw_cons_checkc;
89static cn_putc_t	ofw_cons_putc;
90
91CONS_DRIVER(ofw, ofw_cons_probe, ofw_cons_init, NULL, ofw_cons_getc,
92    ofw_cons_checkc, ofw_cons_putc, NULL);
93
94static void
95cn_drvinit(void *unused)
96{
97	phandle_t options;
98	char output[32];
99
100	if (ofw_consdev.cn_dev != NULL) {
101		if ((options = OF_finddevice("/options")) == -1 ||
102		    OF_getprop(options, "output-device", output,
103		    sizeof(output)) == -1)
104			return;
105		make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
106		    output);
107		make_dev_alias(ofw_consdev.cn_dev, "ofwcons");
108	}
109}
110
111SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE + CDEV_MAJOR, cn_drvinit, NULL)
112
113static int	stdin;
114static int	stdout;
115
116static int
117ofw_dev_open(dev_t dev, int flag, int mode, struct thread *td)
118{
119	struct	tty *tp;
120	int	unit;
121	int	error, setuptimeout;
122
123	error = 0;
124	setuptimeout = 0;
125	unit = minor(dev);
126
127	tp = ofw_tp = dev->si_tty = ttymalloc(ofw_tp);
128
129	tp->t_oproc = ofw_tty_start;
130	tp->t_param = ofw_tty_param;
131	tp->t_stop = ofw_tty_stop;
132	tp->t_dev = dev;
133
134	if ((tp->t_state & TS_ISOPEN) == 0) {
135		tp->t_state |= TS_CARR_ON;
136		ttychars(tp);
137		tp->t_iflag = TTYDEF_IFLAG;
138		tp->t_oflag = TTYDEF_OFLAG;
139		tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
140		tp->t_lflag = TTYDEF_LFLAG;
141		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
142		ttsetwater(tp);
143
144		setuptimeout = 1;
145	} else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
146		return (EBUSY);
147	}
148
149	error = (*linesw[tp->t_line].l_open)(dev, tp);
150
151	if (error == 0 && setuptimeout) {
152		polltime = hz / OFW_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(dev_t dev, int flag, int mode, struct thread *td)
165{
166	int	unit;
167	struct	tty *tp;
168
169	unit = minor(dev);
170	tp = ofw_tp;
171
172	if (unit != 0) {
173		return (ENXIO);
174	}
175
176	(*linesw[tp->t_line].l_close)(tp, flag);
177	ttyclose(tp);
178
179	return (0);
180}
181
182static int
183ofw_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
184{
185	int	unit;
186	struct	tty *tp;
187	int	error;
188
189	unit = minor(dev);
190	tp = ofw_tp;
191
192	if (unit != 0) {
193		return (ENXIO);
194	}
195
196	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
197	if (error != ENOIOCTL) {
198		return (error);
199	}
200
201	error = ttioctl(tp, cmd, data, flag);
202	if (error != ENOIOCTL) {
203		return (error);
204	}
205
206	return (ENOTTY);
207}
208
209static int
210ofw_tty_param(struct tty *tp, struct termios *t)
211{
212
213	return (0);
214}
215
216static void
217ofw_tty_start(struct tty *tp)
218{
219
220	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
221		ttwwakeup(tp);
222		return;
223	}
224
225	tp->t_state |= TS_BUSY;
226	while (tp->t_outq.c_cc != 0) {
227		ofw_cons_putc(NULL, getc(&tp->t_outq));
228	}
229	tp->t_state &= ~TS_BUSY;
230
231	ttwwakeup(tp);
232}
233
234static void
235ofw_tty_stop(struct tty *tp, int flag)
236{
237
238	if (tp->t_state & TS_BUSY) {
239		if ((tp->t_state & TS_TTSTOP) == 0) {
240			tp->t_state |= TS_FLUSH;
241		}
242	}
243}
244
245static void
246ofw_timeout(void *v)
247{
248	struct	tty *tp;
249	int 	c;
250
251	tp = (struct tty *)v;
252
253	while ((c = ofw_cons_checkc(NULL)) != -1) {
254		if (tp->t_state & TS_ISOPEN) {
255			(*linesw[tp->t_line].l_rint)(c, tp);
256		}
257	}
258
259	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
260}
261
262static void
263ofw_cons_probe(struct consdev *cp)
264{
265	int chosen;
266
267	if ((chosen = OF_finddevice("/chosen")) == -1) {
268		cp->cn_pri = CN_DEAD;
269		return;
270	}
271
272	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
273		cp->cn_pri = CN_DEAD;
274		return;
275	}
276
277	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
278		cp->cn_pri = CN_DEAD;
279		return;
280	}
281
282	cp->cn_dev = NULL;
283	cp->cn_pri = CN_INTERNAL;
284}
285
286static void
287ofw_cons_init(struct consdev *cp)
288{
289
290	cp->cn_dev = makedev(CDEV_MAJOR, 0);
291	cp->cn_tp = ofw_tp;
292}
293
294static int
295ofw_cons_getc(struct consdev *cp)
296{
297	unsigned char ch;
298	int l;
299
300	ch = '\0';
301
302	while ((l = OF_read(stdin, &ch, 1)) != 1) {
303		if (l != -2 && l != 0) {
304			return (-1);
305		}
306	}
307
308#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
309	if (db_alt_break(ch, &alt_break_state))
310		breakpoint();
311#endif
312
313	return (ch);
314}
315
316static int
317ofw_cons_checkc(struct consdev *cp)
318{
319	unsigned char ch;
320
321	if (OF_read(stdin, &ch, 1) > 0) {
322#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
323		if (db_alt_break(ch, &alt_break_state))
324			breakpoint();
325#endif
326		return (ch);
327	}
328
329	return (-1);
330}
331
332static void
333ofw_cons_putc(struct consdev *cp, int c)
334{
335	char cbuf;
336
337	if (c == '\n') {
338		cbuf = '\r';
339		OF_write(stdout, &cbuf, 1);
340	}
341
342	cbuf = c;
343	OF_write(stdout, &cbuf, 1);
344}
345