xref: /freebsd/sys/dev/ofw/ofw_console.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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	4
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 void
86 cn_drvinit(void *unused)
87 {
88 
89 	make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ofwcons");
90 }
91 
92 SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
93 
94 static int	stdin;
95 static int	stdout;
96 
97 static int
98 ofw_dev_open(dev_t dev, int flag, int mode, struct thread *td)
99 {
100 	struct	tty *tp;
101 	int	unit;
102 	int	error, setuptimeout;
103 
104 	error = 0;
105 	setuptimeout = 0;
106 	unit = minor(dev);
107 
108 	tp = ofw_tp = dev->si_tty = ttymalloc(ofw_tp);
109 
110 	tp->t_oproc = ofw_tty_start;
111 	tp->t_param = ofw_tty_param;
112 	tp->t_stop = ofw_tty_stop;
113 	tp->t_dev = dev;
114 
115 	if ((tp->t_state & TS_ISOPEN) == 0) {
116 		tp->t_state |= TS_CARR_ON;
117 		ttychars(tp);
118 		tp->t_iflag = TTYDEF_IFLAG;
119 		tp->t_oflag = TTYDEF_OFLAG;
120 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
121 		tp->t_lflag = TTYDEF_LFLAG;
122 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
123 		ttsetwater(tp);
124 
125 		setuptimeout = 1;
126 	} else if ((tp->t_state & TS_XCLUDE) && suser_td(td)) {
127 		return (EBUSY);
128 	}
129 
130 	error = (*linesw[tp->t_line].l_open)(dev, tp);
131 
132 	if (error == 0 && setuptimeout) {
133 		polltime = hz / OFW_POLL_HZ;
134 		if (polltime < 1) {
135 			polltime = 1;
136 		}
137 
138 		ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
139 	}
140 
141 	return (error);
142 }
143 
144 static int
145 ofw_dev_close(dev_t dev, int flag, int mode, struct thread *td)
146 {
147 	int	unit;
148 	struct	tty *tp;
149 
150 	unit = minor(dev);
151 	tp = ofw_tp;
152 
153 	if (unit != 0) {
154 		return (ENXIO);
155 	}
156 
157 	(*linesw[tp->t_line].l_close)(tp, flag);
158 	ttyclose(tp);
159 
160 	return (0);
161 }
162 
163 static int
164 ofw_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
165 {
166 	int	unit;
167 	struct	tty *tp;
168 	int	error;
169 
170 	unit = minor(dev);
171 	tp = ofw_tp;
172 
173 	if (unit != 0) {
174 		return (ENXIO);
175 	}
176 
177 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
178 	if (error != ENOIOCTL) {
179 		return (error);
180 	}
181 
182 	error = ttioctl(tp, cmd, data, flag);
183 	if (error != ENOIOCTL) {
184 		return (error);
185 	}
186 
187 	return (ENOTTY);
188 }
189 
190 static int
191 ofw_tty_param(struct tty *tp, struct termios *t)
192 {
193 
194 	return (0);
195 }
196 
197 static void
198 ofw_tty_start(struct tty *tp)
199 {
200 
201 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
202 		ttwwakeup(tp);
203 		return;
204 	}
205 
206 	tp->t_state |= TS_BUSY;
207 	while (tp->t_outq.c_cc != 0) {
208 		ofw_cons_putc(tp->t_dev, getc(&tp->t_outq));
209 	}
210 	tp->t_state &= ~TS_BUSY;
211 
212 	ttwwakeup(tp);
213 }
214 
215 static void
216 ofw_tty_stop(struct tty *tp, int flag)
217 {
218 
219 	if (tp->t_state & TS_BUSY) {
220 		if ((tp->t_state & TS_TTSTOP) == 0) {
221 			tp->t_state |= TS_FLUSH;
222 		}
223 	}
224 }
225 
226 static void
227 ofw_timeout(void *v)
228 {
229 	struct	tty *tp;
230 	int 	c;
231 
232 	tp = (struct tty *)v;
233 
234 	while ((c = ofw_cons_checkc(tp->t_dev)) != -1) {
235 		if (tp->t_state & TS_ISOPEN) {
236 			(*linesw[tp->t_line].l_rint)(c, tp);
237 		}
238 	}
239 
240 	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
241 }
242 
243 static void
244 ofw_cons_probe(struct consdev *cp)
245 {
246 	int chosen;
247 
248 	if ((chosen = OF_finddevice("/chosen")) == -1) {
249 		cp->cn_pri = CN_DEAD;
250 		return;
251 	}
252 
253 	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
254 		cp->cn_pri = CN_DEAD;
255 		return;
256 	}
257 
258 	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
259 		cp->cn_pri = CN_DEAD;
260 		return;
261 	}
262 
263 	cp->cn_dev = makedev(CDEV_MAJOR, 0);
264 	cp->cn_pri = CN_INTERNAL;
265 	cp->cn_tp = ofw_tp;
266 }
267 
268 static void
269 ofw_cons_init(struct consdev *cp)
270 {
271 
272 	return;
273 }
274 
275 static int
276 ofw_cons_getc(dev_t dev)
277 {
278 	unsigned char ch;
279 	int l;
280 
281 	ch = '\0';
282 
283 	while ((l = OF_read(stdin, &ch, 1)) != 1) {
284 		if (l != -2 && l != 0) {
285 			return (-1);
286 		}
287 	}
288 
289 	return (ch);
290 }
291 
292 static int
293 ofw_cons_checkc(dev_t dev)
294 {
295 	unsigned char ch;
296 
297 	if (OF_read(stdin, &ch, 1) > 0) {
298 		return (ch);
299 	}
300 
301 	return (-1);
302 }
303 
304 static void
305 ofw_cons_putc(dev_t dev, int c)
306 {
307 	char cbuf;
308 
309 	if (c == '\n') {
310 		cbuf = '\r';
311 		OF_write(stdout, &cbuf, 1);
312 	}
313 
314 	cbuf = c;
315 	OF_write(stdout, &cbuf, 1);
316 }
317