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