1 /*
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This software was developed by the Computer Systems Engineering group
6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7 * contributed to Berkeley.
8 *
9 * All advertising materials mentioning features or use of this software
10 * must display the following acknowledgement:
11 * This product includes software developed by the University of
12 * California, Lawrence Berkeley Laboratory.
13 *
14 * %sccs.include.redist.c%
15 *
16 * @(#)cons.c 8.3 (Berkeley) 12/14/93
17 *
18 * from: $Header: cons.c,v 1.12 93/07/20 00:49:45 torek Exp $
19 */
20
21 /*
22 * Console (indirect) driver.
23 */
24
25 #include <sys/param.h>
26 #include <sys/proc.h>
27 #include <sys/systm.h>
28 #include <sys/ioctl.h>
29 #include <sys/tty.h>
30 #include <sys/file.h>
31 #include <sys/conf.h>
32
33 #include <machine/bsd_openprom.h>
34 #include <machine/psl.h>
35
36 #include "zs.h"
37
38 struct tty *constty = 0; /* virtual console output device */
39 struct tty *fbconstty = 0; /* tty structure for frame buffer console */
40 int rom_console_input; /* when set, hardclock calls cnrom() */
41
42 int cons_ocount; /* output byte count */
43
44 extern struct promvec *promvec;
45
46 /*
47 * The output driver may munge the minor number in cons.t_dev.
48 */
49 struct tty cons; /* rom console tty device */
50 static void cnstart __P((struct tty *));
51 static void cnfbstart __P((struct tty *));
52 static void cnfbstop __P((struct tty *, int));
53 static void cnfbdma __P((void *));
54
55 extern const char char_type[];
56
consinit()57 consinit()
58 {
59 register struct tty *tp = &cons;
60 register int in, out;
61 void zsconsole(), bwtwoconsole();
62
63 tp->t_dev = makedev(0, 0); /* /dev/console */
64 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
65 tp->t_param = (int (*)(struct tty *, struct termios *))nullop;
66 in = *promvec->pv_stdin;
67 out = *promvec->pv_stdout;
68 switch (in) {
69
70 #if NZS > 0
71 case PROMDEV_TTYA:
72 zsconsole(tp, 0, 0);
73 break;
74
75 case PROMDEV_TTYB:
76 zsconsole(tp, 1, 0);
77 break;
78 #endif
79
80 case PROMDEV_KBD:
81 /*
82 * Tell the keyboard driver to direct ASCII input here.
83 */
84 kbd_ascii(tp);
85 break;
86
87 default:
88 rom_console_input = 1;
89 printf("unknown console input source %d; using rom\n", in);
90 break;
91 }
92 switch (out) {
93
94 #if NZS > 0
95 case PROMDEV_TTYA:
96 zsconsole(tp, 0, 1);
97 break;
98
99 case PROMDEV_TTYB:
100 zsconsole(tp, 1, 1);
101 break;
102 #endif
103
104 case PROMDEV_SCREEN:
105 fbconstty = tp;
106 tp->t_oproc = cnfbstart;
107 tp->t_stop = cnfbstop;
108 break;
109
110 default:
111 printf("unknown console output sink %d; using rom\n", out);
112 tp->t_oproc = cnstart;
113 tp->t_stop = (void (*)(struct tty *, int))nullop;
114 break;
115 }
116 }
117
118 /* ARGSUSED */
cnopen(dev,flag,mode,p)119 cnopen(dev, flag, mode, p)
120 dev_t dev;
121 int flag, mode;
122 struct proc *p;
123 {
124 register struct tty *tp = &cons;
125
126 if ((tp->t_state & TS_ISOPEN) == 0) {
127 /*
128 * Leave baud rate alone!
129 */
130 ttychars(tp);
131 tp->t_iflag = TTYDEF_IFLAG;
132 tp->t_oflag = TTYDEF_OFLAG;
133 tp->t_lflag = TTYDEF_LFLAG;
134 tp->t_cflag = TTYDEF_CFLAG;
135 tp->t_state = TS_ISOPEN | TS_CARR_ON;
136 (void)(*tp->t_param)(tp, &tp->t_termios);
137 ttsetwater(tp);
138 } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
139 return (EBUSY);
140 return ((*linesw[tp->t_line].l_open)(dev, tp));
141 }
142
143 /* ARGSUSED */
cnclose(dev,flag,mode,p)144 cnclose(dev, flag, mode, p)
145 dev_t dev;
146 int flag, mode;
147 struct proc *p;
148 {
149 register struct tty *tp = &cons;
150
151 (*linesw[tp->t_line].l_close)(tp, flag);
152 ttyclose(tp);
153 return (0);
154 }
155
156 /* ARGSUSED */
cnread(dev,uio,flag)157 cnread(dev, uio, flag)
158 dev_t dev;
159 struct uio *uio;
160 int flag;
161 {
162 register struct tty *tp = &cons;
163
164 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
165 }
166
167 /* ARGSUSED */
cnwrite(dev,uio,flag)168 cnwrite(dev, uio, flag)
169 dev_t dev;
170 struct uio *uio;
171 int flag;
172 {
173 register struct tty *tp;
174
175 if ((tp = constty) == NULL ||
176 (tp->t_state & (TS_CARR_ON|TS_ISOPEN)) != (TS_CARR_ON|TS_ISOPEN))
177 tp = &cons;
178 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
179 }
180
cnioctl(dev,cmd,data,flag,p)181 cnioctl(dev, cmd, data, flag, p)
182 dev_t dev;
183 int cmd;
184 caddr_t data;
185 int flag;
186 struct proc *p;
187 {
188 register struct tty *tp;
189 int error;
190
191 /*
192 * Superuser can always use this to wrest control of console
193 * output from the "virtual" console.
194 */
195 if (cmd == TIOCCONS && constty) {
196 error = suser(p->p_ucred, (u_short *)NULL);
197 if (error)
198 return (error);
199 constty = NULL;
200 return (0);
201 }
202 tp = &cons;
203 if ((error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p)) >= 0)
204 return (error);
205 if ((error = ttioctl(tp, cmd, data, flag)) >= 0)
206 return (error);
207 return (ENOTTY);
208 }
209
cnselect(dev,which,p)210 cnselect(dev, which, p)
211 dev_t dev;
212 int which;
213 struct proc *p;
214 {
215
216 return (ttselect(makedev(major(dev), 0), which, p));
217 }
218
219 /*
220 * The rest of this code is run only when we are using the ROM vectors.
221 */
222
223 /*
224 * Generic output. We just call putchar. (Very bad for performance.)
225 */
226 static void
cnstart(tp)227 cnstart(tp)
228 register struct tty *tp;
229 {
230 register int c, s;
231 register void (*putc)(int);
232
233 s = spltty();
234 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
235 splx(s);
236 return;
237 }
238 putc = promvec->pv_putchar;
239 while (tp->t_outq.c_cc) {
240 c = getc(&tp->t_outq);
241 /*
242 * *%&!*& ROM monitor console putchar is not reentrant!
243 * splhigh/tty around it so as not to run so long with
244 * clock interrupts blocked.
245 */
246 (void) splhigh();
247 (*putc)(c & 0177);
248 (void) spltty();
249 }
250 if (tp->t_state & TS_ASLEEP) { /* can't happen? */
251 tp->t_state &= ~TS_ASLEEP;
252 wakeup((caddr_t)&tp->t_outq);
253 }
254 selwakeup(&tp->t_wsel);
255 splx(s);
256 }
257
258 /*
259 * Frame buffer output.
260 * We use pseudo-DMA, via the ROM `write string' function, called from
261 * software clock interrupts.
262 */
263 static void
cnfbstart(tp)264 cnfbstart(tp)
265 register struct tty *tp;
266 {
267 register int s;
268
269 s = spltty(); /* paranoid: splsoftclock should suffice */
270 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
271 splx(s);
272 return;
273 }
274 /*
275 * If there are sleepers, and output has drained below low
276 * water mark, awaken.
277 */
278 if (tp->t_outq.c_cc <= tp->t_lowat) {
279 if (tp->t_state & TS_ASLEEP) {
280 tp->t_state &= ~TS_ASLEEP;
281 wakeup((caddr_t)&tp->t_outq);
282 }
283 selwakeup(&tp->t_wsel);
284 }
285 if (tp->t_outq.c_cc) {
286 tp->t_state |= TS_BUSY;
287 if (s == 0) {
288 (void) splsoftclock();
289 cnfbdma((void *)tp);
290 } else
291 timeout(cnfbdma, tp, 1);
292 }
293 splx(s);
294 }
295
296 /*
297 * Stop frame buffer output: just assert TS_FLUSH if necessary.
298 */
299 static void
cnfbstop(tp,flag)300 cnfbstop(tp, flag)
301 register struct tty *tp;
302 int flag;
303 {
304 register int s = spltty(); /* paranoid */
305
306 if ((tp->t_state & (TS_BUSY | TS_TTSTOP)) == TS_BUSY)
307 tp->t_state |= TS_FLUSH;
308 splx(s);
309 }
310
311 /*
312 * Do pseudo-dma (called from software interrupt).
313 */
314 static void
cnfbdma(tpaddr)315 cnfbdma(tpaddr)
316 void *tpaddr;
317 {
318 register struct tty *tp = tpaddr;
319 register char *p, *q;
320 register int n, c, s;
321
322 s = spltty(); /* paranoid */
323 if (tp->t_state & TS_FLUSH) {
324 tp->t_state &= ~(TS_BUSY | TS_FLUSH);
325 splx(s);
326 } else {
327 tp->t_state &= ~TS_BUSY;
328 splx(s);
329 p = tp->t_outq.c_cf;
330 n = ndqb(&tp->t_outq, 0);
331 for (q = p, c = n; --c >= 0; q++)
332 if (*q & 0200) /* high bits seem to be bad */
333 *q &= ~0200;
334 (*promvec->pv_putstr)(p, n);
335 ndflush(&tp->t_outq, n);
336 }
337 if (tp->t_line)
338 (*linesw[tp->t_line].l_start)(tp);
339 else
340 cnfbstart(tp);
341 }
342
343 /*
344 * The following is for rom console input. The rom will not call
345 * an `interrupt' routine on console input ready, so we must poll.
346 * This is all rather sad.
347 */
348 volatile int cn_rxc; /* XXX receive `silo' */
349
350 /* called from hardclock, which is above spltty, so no tty calls! */
cnrom()351 cnrom()
352 {
353 register int c;
354
355 if (cn_rxc >= 0)
356 return (1);
357 if ((c = (*promvec->pv_nbgetchar)()) < 0)
358 return (0);
359 cn_rxc = c;
360 return (1);
361 }
362
363 /* pseudo console software interrupt scheduled when cnrom() returns 1 */
cnrint()364 cnrint()
365 {
366 register struct tty *tp;
367 register int c, s;
368
369 s = splclock();
370 c = cn_rxc;
371 cn_rxc = -1;
372 splx(s);
373 if (c < 0)
374 return;
375 tp = &cons;
376 if ((tp->t_cflag & CSIZE) == CS7) {
377 #if 0
378 /* XXX this should be done elsewhere, if at all */
379 if (tp->t_cflag & PARENB)
380 if (tp->t_cflag & PARODD ?
381 (char_type[c & 0177] & 0200) == (c & 0200) :
382 (char_type[c & 0177] & 0200) != (c & 0200))
383 c |= TTY_PE;
384 #endif
385 c &= ~0200;
386 }
387 (*linesw[tp->t_line].l_rint)(c, tp);
388 }
389
cngetc()390 cngetc()
391 {
392 register int s, c;
393
394 s = splhigh();
395 c = (*promvec->pv_getchar)();
396 splx(s);
397 if (c == '\r')
398 c = '\n';
399 return (c);
400 }
401
cnputc(c)402 cnputc(c)
403 register int c;
404 {
405 register int s = splhigh();
406
407 if (c == '\n')
408 (*promvec->pv_putchar)('\r');
409 (*promvec->pv_putchar)(c);
410 splx(s);
411 }
412