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