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