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