xref: /freebsd/sys/dev/ofw/ofw_console.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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 <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/types.h>
35 #include <sys/conf.h>
36 #include <sys/cons.h>
37 #include <sys/consio.h>
38 #include <sys/tty.h>
39 
40 #include <dev/ofw/openfirm.h>
41 
42 #define	OFW_POLL_HZ	50
43 
44 static d_open_t		ofw_dev_open;
45 static d_close_t	ofw_dev_close;
46 static d_ioctl_t	ofw_dev_ioctl;
47 
48 #define	CDEV_MAJOR	97
49 
50 static struct cdevsw ofw_cdevsw = {
51 	/* open */	ofw_dev_open,
52 	/* close */	ofw_dev_close,
53 	/* read */	ttyread,
54 	/* write */	ttywrite,
55 	/* ioctl */	ofw_dev_ioctl,
56 	/* poll */	ttypoll,
57 	/* mmap */	nommap,
58 	/* strategy */	nostrategy,
59 	/* name */	"ofw",
60 	/* major */	CDEV_MAJOR,
61 	/* dump */	nodump,
62 	/* psize */	nopsize,
63 	/* flags */	0,
64 };
65 
66 static struct tty		*ofw_tp = NULL;
67 static int			polltime;
68 static struct callout_handle	ofw_timeouthandle
69     = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
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 int	stdin;
86 static int	stdout;
87 
88 static int
89 ofw_dev_open(dev_t dev, int flag, int mode, struct proc *p)
90 {
91 	struct	tty *tp;
92 	int	unit;
93 	int	error, setuptimeout;
94 
95 	error = 0;
96 	setuptimeout = 0;
97 	unit = minor(dev);
98 
99 	tp = ofw_tp = dev->si_tty = ttymalloc(ofw_tp);
100 
101 	tp->t_oproc = ofw_tty_start;
102 	tp->t_param = ofw_tty_param;
103 	tp->t_stop = ofw_tty_stop;
104 	tp->t_dev = dev;
105 
106 	if ((tp->t_state & TS_ISOPEN) == 0) {
107 		tp->t_state |= TS_CARR_ON;
108 		ttychars(tp);
109 		tp->t_iflag = TTYDEF_IFLAG;
110 		tp->t_oflag = TTYDEF_OFLAG;
111 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
112 		tp->t_lflag = TTYDEF_LFLAG;
113 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
114 		ttsetwater(tp);
115 
116 		setuptimeout = 1;
117 	} else if ((tp->t_state & TS_XCLUDE) && suser(p)) {
118 		return (EBUSY);
119 	}
120 
121 	error = (*linesw[tp->t_line].l_open)(dev, tp);
122 
123 	if (error == 0 && setuptimeout) {
124 		polltime = hz / OFW_POLL_HZ;
125 		if (polltime < 1) {
126 			polltime = 1;
127 		}
128 
129 		ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
130 	}
131 
132 	return (error);
133 }
134 
135 static int
136 ofw_dev_close(dev_t dev, int flag, int mode, struct proc *p)
137 {
138 	int	unit;
139 	struct	tty *tp;
140 
141 	unit = minor(dev);
142 	tp = ofw_tp;
143 
144 	if (unit != 0) {
145 		return (ENXIO);
146 	}
147 
148 	(*linesw[tp->t_line].l_close)(tp, flag);
149 	ttyclose(tp);
150 
151 	return (0);
152 }
153 
154 static int
155 ofw_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
156 {
157 	int	unit;
158 	struct	tty *tp;
159 	int	error;
160 
161 	unit = minor(dev);
162 	if (unit != 0) {
163 		return (ENXIO);
164 	}
165 
166 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
167 	if (error != ENOIOCTL) {
168 		return (error);
169 	}
170 
171 	error = ttioctl(tp, cmd, data, flag);
172 	if (error != ENOIOCTL) {
173 		return (error);
174 	}
175 
176 	return (ENOTTY);
177 }
178 
179 static int
180 ofw_tty_param(struct tty *tp, struct termios *t)
181 {
182 
183 	return (0);
184 }
185 
186 static void
187 ofw_tty_start(struct tty *tp)
188 {
189 
190 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
191 		ttwwakeup(tp);
192 		return;
193 	}
194 
195 	tp->t_state |= TS_BUSY;
196 	while (tp->t_outq.c_cc != 0) {
197 		ofw_cons_putc(tp->t_dev, getc(&tp->t_outq));
198 	}
199 	tp->t_state &= ~TS_BUSY;
200 
201 	ttwwakeup(tp);
202 }
203 
204 static void
205 ofw_tty_stop(struct tty *tp, int flag)
206 {
207 
208 	if (tp->t_state & TS_BUSY) {
209 		if ((tp->t_state & TS_TTSTOP) == 0) {
210 			tp->t_state |= TS_FLUSH;
211 		}
212 	}
213 }
214 
215 static void
216 ofw_timeout(void *v)
217 {
218 	struct	tty *tp;
219 	int 	c;
220 
221 	tp = (struct tty *)v;
222 
223 	while ((c = ofw_cons_checkc(tp->t_dev)) != -1) {
224 		if (tp->t_state & TS_ISOPEN) {
225 			(*linesw[tp->t_line].l_rint)(c, tp);
226 		}
227 	}
228 
229 	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
230 }
231 
232 static void
233 ofw_cons_probe(struct consdev *cp)
234 {
235 	int chosen;
236 
237 	if ((chosen = OF_finddevice("/chosen")) == -1) {
238 		cp->cn_pri = CN_DEAD;
239 		return;
240 	}
241 
242 	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
243 		cp->cn_pri = CN_DEAD;
244 		return;
245 	}
246 
247 	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
248 		cp->cn_pri = CN_DEAD;
249 		return;
250 	}
251 
252 	cp->cn_dev = makedev(CDEV_MAJOR, 0);
253 	cp->cn_pri = CN_INTERNAL;
254 	cp->cn_tp = ofw_tp;
255 	make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ofwcons");
256 }
257 
258 static void
259 ofw_cons_init(struct consdev *cp)
260 {
261 
262 	return;
263 }
264 
265 static int
266 ofw_cons_getc(dev_t dev)
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 	return (ch);
280 }
281 
282 static int
283 ofw_cons_checkc(dev_t dev)
284 {
285 	unsigned char ch;
286 
287 	if (OF_read(stdin, &ch, 1) != 0) {
288 		return (ch);
289 	}
290 
291 	return (-1);
292 }
293 
294 static void
295 ofw_cons_putc(dev_t dev, int c)
296 {
297 	char cbuf;
298 
299 	if (c == '\n') {
300 		cbuf = '\r';
301 		OF_write(stdout, &cbuf, 1);
302 	}
303 
304 	cbuf = c;
305 	OF_write(stdout, &cbuf, 1);
306 }
307