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