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