]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ofw/ofw_console.c
This commit was generated by cvs2svn to compensate for changes in r155429,
[FreeBSD/FreeBSD.git] / sys / dev / ofw / ofw_console.c
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                 /*
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) && suser(td)) {
145                 return (EBUSY);
146         }
147
148         error = ttyld_open(tp, dev);
149
150         if (error == 0 && setuptimeout) {
151                 polltime = hz / OFWCONS_POLL_HZ;
152                 if (polltime < 1) {
153                         polltime = 1;
154                 }
155
156                 ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
157         }
158
159         return (error);
160 }
161
162 static int
163 ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td)
164 {
165         int     unit;
166         struct  tty *tp;
167
168         unit = minor(dev);
169         tp = dev->si_tty;
170
171         if (unit != 0) {
172                 return (ENXIO);
173         }
174
175         /* XXX Should be replaced with callout_stop(9) */
176         untimeout(ofw_timeout, tp, ofw_timeouthandle);
177         ttyld_close(tp, flag);
178         tty_close(tp);
179
180         return (0);
181 }
182
183
184 static int
185 ofw_tty_param(struct tty *tp, struct termios *t)
186 {
187
188         return (0);
189 }
190
191 static void
192 ofw_tty_start(struct tty *tp)
193 {
194         struct clist *cl;
195         int len;
196         u_char buf[OFBURSTLEN];
197
198
199         if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
200                 return;
201
202         tp->t_state |= TS_BUSY;
203         cl = &tp->t_outq;
204         len = q_to_b(cl, buf, OFBURSTLEN);
205         OF_write(stdout, buf, len);
206         tp->t_state &= ~TS_BUSY;
207
208         ttwwakeup(tp);
209 }
210
211 static void
212 ofw_tty_stop(struct tty *tp, int flag)
213 {
214
215         if (tp->t_state & TS_BUSY) {
216                 if ((tp->t_state & TS_TTSTOP) == 0) {
217                         tp->t_state |= TS_FLUSH;
218                 }
219         }
220 }
221
222 static void
223 ofw_timeout(void *v)
224 {
225         struct  tty *tp;
226         int     c;
227
228         tp = (struct tty *)v;
229
230         while ((c = ofw_cons_checkc(NULL)) != -1) {
231                 if (tp->t_state & TS_ISOPEN) {
232                         ttyld_rint(tp, c);
233                 }
234         }
235
236         ofw_timeouthandle = timeout(ofw_timeout, tp, polltime);
237 }
238
239 static void
240 ofw_cons_probe(struct consdev *cp)
241 {
242         int chosen;
243
244         if ((chosen = OF_finddevice("/chosen")) == -1) {
245                 cp->cn_pri = CN_DEAD;
246                 return;
247         }
248
249         if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) {
250                 cp->cn_pri = CN_DEAD;
251                 return;
252         }
253
254         if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) {
255                 cp->cn_pri = CN_DEAD;
256                 return;
257         }
258
259         cp->cn_pri = CN_LOW;
260 }
261
262 static void
263 ofw_cons_init(struct consdev *cp)
264 {
265
266         /* XXX: This is the alias, but that should be good enough */
267         sprintf(cp->cn_name, "ofwcons");
268         cp->cn_tp = ofw_tp;
269 }
270
271 static int
272 ofw_cons_getc(struct consdev *cp)
273 {
274         unsigned char ch;
275         int l;
276
277         ch = '\0';
278
279         while ((l = OF_read(stdin, &ch, 1)) != 1) {
280                 if (l != -2 && l != 0) {
281                         return (-1);
282                 }
283         }
284
285 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
286         if (kdb_alt_break(ch, &alt_break_state))
287                 kdb_enter("Break sequence on console");
288 #endif
289
290         return (ch);
291 }
292
293 static int
294 ofw_cons_checkc(struct consdev *cp)
295 {
296         unsigned char ch;
297
298         if (OF_read(stdin, &ch, 1) > 0) {
299 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
300                 if (kdb_alt_break(ch, &alt_break_state))
301                         kdb_enter("Break sequence on console");
302 #endif
303                 return (ch);
304         }
305
306         return (-1);
307 }
308
309 static void
310 ofw_cons_putc(struct consdev *cp, int c)
311 {
312         char cbuf;
313
314         if (c == '\n') {
315                 cbuf = '\r';
316                 OF_write(stdout, &cbuf, 1);
317         }
318
319         cbuf = c;
320         OF_write(stdout, &cbuf, 1);
321 }