xref: /freebsd/sys/dev/ofw/ofw_console.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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/priv.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 #ifndef	OFWCONS_POLL_HZ
48 #define	OFWCONS_POLL_HZ	4	/* 50-100 works best on Ultra2 */
49 #endif
50 #define OFBURSTLEN	128	/* max number of bytes to write in one chunk */
51 
52 static d_open_t		ofw_dev_open;
53 static d_close_t	ofw_dev_close;
54 
55 static struct cdevsw ofw_cdevsw = {
56 	.d_version =	D_VERSION,
57 	.d_open =	ofw_dev_open,
58 	.d_close =	ofw_dev_close,
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(KDB) && 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_cnprobe;
78 static cn_init_t	ofw_cninit;
79 static cn_term_t	ofw_cnterm;
80 static cn_getc_t	ofw_cngetc;
81 static cn_putc_t	ofw_cnputc;
82 
83 CONSOLE_DRIVER(ofw);
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 		/*
99 		 * XXX: This is a hack and it may result in two /dev/ttya
100 		 * XXX: devices on platforms where the sab driver works.
101 		 */
102 		dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
103 		    output);
104 		make_dev_alias(dev, "ofwcons");
105 	}
106 }
107 
108 SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL)
109 
110 static int	stdin;
111 static int	stdout;
112 
113 static int
114 ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td)
115 {
116 	struct	tty *tp;
117 	int	unit;
118 	int	error, setuptimeout;
119 
120 	error = 0;
121 	setuptimeout = 0;
122 	unit = minor(dev);
123 
124 	/*
125 	 * XXX: BAD, should happen at attach time
126 	 */
127 	if (dev->si_tty == NULL) {
128 		ofw_tp = ttyalloc();
129 		dev->si_tty = ofw_tp;
130 		ofw_tp->t_dev = dev;
131 	}
132 	tp = dev->si_tty;
133 
134 	tp->t_oproc = ofw_tty_start;
135 	tp->t_param = ofw_tty_param;
136 	tp->t_stop = ofw_tty_stop;
137 	tp->t_dev = dev;
138 
139 	if ((tp->t_state & TS_ISOPEN) == 0) {
140 		tp->t_state |= TS_CARR_ON;
141 		ttyconsolemode(tp, 0);
142 
143 		setuptimeout = 1;
144 	} else if ((tp->t_state & TS_XCLUDE) &&
145 	    priv_check(td, PRIV_TTY_EXCLUSIVE)) {
146 		return (EBUSY);
147 	}
148 
149 	error = ttyld_open(tp, dev);
150 
151 	if (error == 0 && setuptimeout) {
152 		polltime = hz / OFWCONS_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(struct cdev *dev, int flag, int mode, struct thread *td)
165 {
166 	int	unit;
167 	struct	tty *tp;
168 
169 	unit = minor(dev);
170 	tp = dev->si_tty;
171 
172 	if (unit != 0) {
173 		return (ENXIO);
174 	}
175 
176 	/* XXX Should be replaced with callout_stop(9) */
177 	untimeout(ofw_timeout, tp, ofw_timeouthandle);
178 	ttyld_close(tp, flag);
179 	tty_close(tp);
180 
181 	return (0);
182 }
183 
184 
185 static int
186 ofw_tty_param(struct tty *tp, struct termios *t)
187 {
188 
189 	return (0);
190 }
191 
192 static void
193 ofw_tty_start(struct tty *tp)
194 {
195 	struct clist *cl;
196 	int len;
197 	u_char buf[OFBURSTLEN];
198 
199 
200 	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
201 		return;
202 
203 	tp->t_state |= TS_BUSY;
204 	cl = &tp->t_outq;
205 	len = q_to_b(cl, buf, OFBURSTLEN);
206 	OF_write(stdout, buf, len);
207 	tp->t_state &= ~TS_BUSY;
208 
209 	ttwwakeup(tp);
210 }
211 
212 static void
213 ofw_tty_stop(struct tty *tp, int flag)
214 {
215 
216 	if (tp->t_state & TS_BUSY) {
217 		if ((tp->t_state & TS_TTSTOP) == 0) {
218 			tp->t_state |= TS_FLUSH;
219 		}
220 	}
221 }
222 
223 static void
224 ofw_timeout(void *v)
225 {
226 	struct	tty *tp;
227 	int 	c;
228 
229 	tp = (struct tty *)v;
230 
231 	while ((c = ofw_cngetc(NULL)) != -1) {
232 		if (tp->t_state & TS_ISOPEN) {
233 			ttyld_rint(tp, c);
234 		}
235 	}
236 
237 	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
238 }
239 
240 static void
241 ofw_cnprobe(struct consdev *cp)
242 {
243 	int chosen;
244 
245 	if ((chosen = OF_finddevice("/chosen")) == -1) {
246 		cp->cn_pri = CN_DEAD;
247 		return;
248 	}
249 
250 	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
251 		cp->cn_pri = CN_DEAD;
252 		return;
253 	}
254 
255 	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
256 		cp->cn_pri = CN_DEAD;
257 		return;
258 	}
259 
260 	cp->cn_pri = CN_LOW;
261 }
262 
263 static void
264 ofw_cninit(struct consdev *cp)
265 {
266 
267 	/* XXX: This is the alias, but that should be good enough */
268 	sprintf(cp->cn_name, "ofwcons");
269 	cp->cn_tp = ofw_tp;
270 }
271 
272 static void
273 ofw_cnterm(struct consdev *cp)
274 {
275 }
276 
277 static int
278 ofw_cngetc(struct consdev *cp)
279 {
280 	unsigned char ch;
281 
282 	if (OF_read(stdin, &ch, 1) > 0) {
283 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
284 		if (kdb_alt_break(ch, &alt_break_state))
285 			kdb_enter(KDB_WHY_BREAK, "Break sequence on console");
286 #endif
287 		return (ch);
288 	}
289 
290 	return (-1);
291 }
292 
293 static void
294 ofw_cnputc(struct consdev *cp, int c)
295 {
296 	char cbuf;
297 
298 	if (c == '\n') {
299 		cbuf = '\r';
300 		OF_write(stdout, &cbuf, 1);
301 	}
302 
303 	cbuf = c;
304 	OF_write(stdout, &cbuf, 1);
305 }
306