xref: /freebsd/sys/dev/ofw/ofw_console.c (revision 74bf4e164ba5851606a27d4feff27717452583e5)
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_comconsole.h"
30 #include "opt_ofw.h"
31 
32 #include <sys/param.h>
33 #include <sys/kdb.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/cons.h>
39 #include <sys/consio.h>
40 #include <sys/tty.h>
41 
42 #include <dev/ofw/openfirm.h>
43 
44 #include <ddb/ddb.h>
45 
46 #ifndef	OFWCONS_POLL_HZ
47 #define	OFWCONS_POLL_HZ	4	/* 50-100 works best on Ultra2 */
48 #endif
49 #define OFBURSTLEN	128	/* max number of bytes to write in one chunk */
50 
51 static d_open_t		ofw_dev_open;
52 static d_close_t	ofw_dev_close;
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_name =	"ofw",
59 	.d_flags =	D_TTY | D_NEEDGIANT,
60 };
61 
62 static struct tty		*ofw_tp = NULL;
63 static int			polltime;
64 static struct callout_handle	ofw_timeouthandle
65     = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
66 
67 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
68 static int			alt_break_state;
69 #endif
70 
71 static void	ofw_tty_start(struct tty *);
72 static int	ofw_tty_param(struct tty *, struct termios *);
73 static void	ofw_tty_stop(struct tty *, int);
74 static void	ofw_timeout(void *);
75 
76 static cn_probe_t	ofw_cons_probe;
77 static cn_init_t	ofw_cons_init;
78 static cn_getc_t	ofw_cons_getc;
79 static cn_checkc_t 	ofw_cons_checkc;
80 static cn_putc_t	ofw_cons_putc;
81 
82 CONS_DRIVER(ofw, ofw_cons_probe, ofw_cons_init, NULL, ofw_cons_getc,
83     ofw_cons_checkc, ofw_cons_putc, NULL);
84 
85 static void
86 cn_drvinit(void *unused)
87 {
88 	phandle_t options;
89 	char output[32];
90 	struct cdev *dev;
91 
92 	if (ofw_consdev.cn_pri != CN_DEAD &&
93 	    ofw_consdev.cn_name[0] != '\0') {
94 		if ((options = OF_finddevice("/options")) == -1 ||
95 		    OF_getprop(options, "output-device", output,
96 		    sizeof(output)) == -1)
97 			return;
98 		dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
99 		    output);
100 		make_dev_alias(dev, "ofwcons");
101 	}
102 }
103 
104 SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL)
105 
106 static int	stdin;
107 static int	stdout;
108 
109 static int
110 ofw_dev_open(struct cdev *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 = ttyld_open(tp, dev);
143 
144 	if (error == 0 && setuptimeout) {
145 		polltime = hz / OFWCONS_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(struct cdev *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 	/* XXX Should be replaced with callout_stop(9) */
170 	untimeout(ofw_timeout, tp, ofw_timeouthandle);
171 	ttyld_close(tp, flag);
172 	tty_close(tp);
173 
174 	return (0);
175 }
176 
177 
178 static int
179 ofw_tty_param(struct tty *tp, struct termios *t)
180 {
181 
182 	return (0);
183 }
184 
185 static void
186 ofw_tty_start(struct tty *tp)
187 {
188 	struct clist *cl;
189 	int len;
190 	u_char buf[OFBURSTLEN];
191 
192 
193 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
194 		return;
195 
196 	tp->t_state |= TS_BUSY;
197 	cl = &tp->t_outq;
198 	len = q_to_b(cl, buf, OFBURSTLEN);
199 	OF_write(stdout, buf, len);
200 	tp->t_state &= ~TS_BUSY;
201 
202 	ttwwakeup(tp);
203 }
204 
205 static void
206 ofw_tty_stop(struct tty *tp, int flag)
207 {
208 
209 	if (tp->t_state & TS_BUSY) {
210 		if ((tp->t_state & TS_TTSTOP) == 0) {
211 			tp->t_state |= TS_FLUSH;
212 		}
213 	}
214 }
215 
216 static void
217 ofw_timeout(void *v)
218 {
219 	struct	tty *tp;
220 	int 	c;
221 
222 	tp = (struct tty *)v;
223 
224 	while ((c = ofw_cons_checkc(NULL)) != -1) {
225 		if (tp->t_state & TS_ISOPEN) {
226 			ttyld_rint(tp, c);
227 		}
228 	}
229 
230 	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
231 }
232 
233 static void
234 ofw_cons_probe(struct consdev *cp)
235 {
236 	int chosen;
237 
238 	if ((chosen = OF_finddevice("/chosen")) == -1) {
239 		cp->cn_pri = CN_DEAD;
240 		return;
241 	}
242 
243 	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
244 		cp->cn_pri = CN_DEAD;
245 		return;
246 	}
247 
248 	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
249 		cp->cn_pri = CN_DEAD;
250 		return;
251 	}
252 
253 	cp->cn_pri = CN_LOW;
254 }
255 
256 static void
257 ofw_cons_init(struct consdev *cp)
258 {
259 
260 	/* XXX: This is the alias, but that should be good enough */
261 	sprintf(cp->cn_name, "ofwcons");
262 	cp->cn_tp = ofw_tp;
263 }
264 
265 static int
266 ofw_cons_getc(struct consdev *cp)
267 {
268 	unsigned char ch;
269 	int l;
270 
271 	ch = '\0';
272 
273 	while ((l = OF_read(stdin, &ch, 1)) != 1) {
274 		if (l != -2 && l != 0) {
275 			return (-1);
276 		}
277 	}
278 
279 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
280 	if (kdb_alt_break(ch, &alt_break_state))
281 		kdb_enter("Break sequence on console");
282 #endif
283 
284 	return (ch);
285 }
286 
287 static int
288 ofw_cons_checkc(struct consdev *cp)
289 {
290 	unsigned char ch;
291 
292 	if (OF_read(stdin, &ch, 1) > 0) {
293 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
294 		if (kdb_alt_break(ch, &alt_break_state))
295 			kdb_enter("Break sequence on console");
296 #endif
297 		return (ch);
298 	}
299 
300 	return (-1);
301 }
302 
303 static void
304 ofw_cons_putc(struct consdev *cp, int c)
305 {
306 	char cbuf;
307 
308 	if (c == '\n') {
309 		cbuf = '\r';
310 		OF_write(stdout, &cbuf, 1);
311 	}
312 
313 	cbuf = c;
314 	OF_write(stdout, &cbuf, 1);
315 }
316