xref: /freebsd/sys/dev/ofw/ofw_console.c (revision 6adf353a56a161443406b44a45d00c688ca7b857)
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 	tp = ofw_tp;
163 
164 	if (unit != 0) {
165 		return (ENXIO);
166 	}
167 
168 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
169 	if (error != ENOIOCTL) {
170 		return (error);
171 	}
172 
173 	error = ttioctl(tp, cmd, data, flag);
174 	if (error != ENOIOCTL) {
175 		return (error);
176 	}
177 
178 	return (ENOTTY);
179 }
180 
181 static int
182 ofw_tty_param(struct tty *tp, struct termios *t)
183 {
184 
185 	return (0);
186 }
187 
188 static void
189 ofw_tty_start(struct tty *tp)
190 {
191 
192 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
193 		ttwwakeup(tp);
194 		return;
195 	}
196 
197 	tp->t_state |= TS_BUSY;
198 	while (tp->t_outq.c_cc != 0) {
199 		ofw_cons_putc(tp->t_dev, getc(&tp->t_outq));
200 	}
201 	tp->t_state &= ~TS_BUSY;
202 
203 	ttwwakeup(tp);
204 }
205 
206 static void
207 ofw_tty_stop(struct tty *tp, int flag)
208 {
209 
210 	if (tp->t_state & TS_BUSY) {
211 		if ((tp->t_state & TS_TTSTOP) == 0) {
212 			tp->t_state |= TS_FLUSH;
213 		}
214 	}
215 }
216 
217 static void
218 ofw_timeout(void *v)
219 {
220 	struct	tty *tp;
221 	int 	c;
222 
223 	tp = (struct tty *)v;
224 
225 	while ((c = ofw_cons_checkc(tp->t_dev)) != -1) {
226 		if (tp->t_state & TS_ISOPEN) {
227 			(*linesw[tp->t_line].l_rint)(c, tp);
228 		}
229 	}
230 
231 	ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
232 }
233 
234 static void
235 ofw_cons_probe(struct consdev *cp)
236 {
237 	int chosen;
238 
239 	if ((chosen = OF_finddevice("/chosen")) == -1) {
240 		cp->cn_pri = CN_DEAD;
241 		return;
242 	}
243 
244 	if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
245 		cp->cn_pri = CN_DEAD;
246 		return;
247 	}
248 
249 	if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
250 		cp->cn_pri = CN_DEAD;
251 		return;
252 	}
253 
254 	cp->cn_dev = makedev(CDEV_MAJOR, 0);
255 	cp->cn_pri = CN_INTERNAL;
256 	cp->cn_tp = ofw_tp;
257 	make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ofwcons");
258 }
259 
260 static void
261 ofw_cons_init(struct consdev *cp)
262 {
263 
264 	return;
265 }
266 
267 static int
268 ofw_cons_getc(dev_t dev)
269 {
270 	unsigned char ch;
271 	int l;
272 
273 	ch = '\0';
274 
275 	while ((l = OF_read(stdin, &ch, 1)) != 1) {
276 		if (l != -2 && l != 0) {
277 			return (-1);
278 		}
279 	}
280 
281 	return (ch);
282 }
283 
284 static int
285 ofw_cons_checkc(dev_t dev)
286 {
287 	unsigned char ch;
288 
289 	if (OF_read(stdin, &ch, 1) != 0) {
290 		return (ch);
291 	}
292 
293 	return (-1);
294 }
295 
296 static void
297 ofw_cons_putc(dev_t dev, int c)
298 {
299 	char cbuf;
300 
301 	if (c == '\n') {
302 		cbuf = '\r';
303 		OF_write(stdout, &cbuf, 1);
304 	}
305 
306 	cbuf = c;
307 	OF_write(stdout, &cbuf, 1);
308 }
309