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