xref: /original-bsd/sys/tahoe/tahoe/cons.c (revision 79cf7955)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)cons.c	7.1 (Berkeley) 05/21/88
7  */
8 
9 /*
10  * Tahoe console processor driver
11  *
12  * Minor device 0 is the CP itself.
13  *	  No real read/writes can be done to him.
14  * Minor 1 is the console terminal.
15  * Minor 2 is the remote line trminal.
16  */
17 #include "param.h"
18 #include "conf.h"
19 #include "dir.h"
20 #include "ioctl.h"
21 #include "user.h"
22 #include "proc.h"
23 #include "tty.h"
24 #include "uio.h"
25 #include "callout.h"
26 #include "systm.h"
27 #include "kernel.h"
28 #include "syslog.h"
29 
30 #include "cp.h"
31 #include "cpu.h"
32 #include "mtpr.h"
33 
34 int	cnrestart();
35 int	timeout();
36 
37 struct	tty CPtty;
38 struct	tty cons;
39 struct	tty RLtty;
40 struct	tty *cntty[3] = { &CPtty, &cons, &RLtty };
41 
42 struct	tty *constty = 0;	/* virtual console */
43 
44 struct	consoftc {
45 	char	cs_flags;
46 #define	CSF_ACTIVE	0x1	/* timeout active */
47 #define	CSF_POLLING	0x2	/* polling for input */
48 	char	cs_lastc;	/* last char sent */
49 	int	cs_timo;	/* timeouts since interrupt */
50 	u_long	cs_wedgecnt;	/* times restarted */
51 } consoftc[3];
52 
53 /*
54  * We check the console periodically to make sure
55  * that it hasn't wedged.  Unfortunately, if an XOFF
56  * is typed on the console, that can't be distinguished
57  * from more catastrophic failure.
58  */
59 #define	CN_TIMERVAL	(hz)		/* frequency at which to check cons */
60 #define	CN_TIMO		(2*60)		/* intervals to allow for output char */
61 
62 struct	cpdcb_o consout[3] = {
63 	{ CPTAKE|CPDONE }, { CPTAKE|CPDONE }, { CPTAKE|CPDONE }
64 };
65 struct	cpdcb_i consin[3] = {
66 	{ CPTAKE|CPDONE }, { CPTAKE|CPDONE }, { CPTAKE|CPDONE }
67 };
68 struct	cphdr *cnlast;
69 
70 int	cnstart();
71 int	ttrstrt();
72 char	partab[];
73 
74 /*
75  * Wait for CP to accept last CP command sent
76  * before setting up next command.
77  */
78 #define	waitforlast(timo) { \
79 	if (cnlast) { \
80 		(timo) = 10000; \
81 		do \
82 			uncache((char *)&cnlast->cp_unit); \
83 		while ((cnlast->cp_unit&CPTAKE) == 0 && --(timo)); \
84 	} \
85 }
86 
87 /*ARGSUSED*/
88 cnopen(dev, flag)
89 	dev_t dev;
90 {
91 	register struct tty *tp;
92 	int unit = minor(dev);
93 
94 	if (unit > CPREMOT)
95 		return (ENXIO);
96 	tp = cntty[unit];
97 	if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
98 		return (EBUSY);
99 	cnpostread(unit);		/* post request for input */
100 	tp->t_oproc = cnstart;
101 	tp->t_dev = dev;
102 	if ((tp->t_state&TS_ISOPEN) == 0) {
103 		ttychars(tp);
104 		tp->t_state = TS_ISOPEN|TS_CARR_ON;
105 		tp->t_flags = EVENP|ECHO|XTABS|CRMOD;
106 	}
107 	return ((*linesw[tp->t_line].l_open)(dev, tp));
108 }
109 
110 cnpostread(unit)
111 	int unit;
112 {
113 	register struct cpdcb_i *cin;
114 	register int timo;
115 
116 	waitforlast(timo);
117 	cin = &consin[unit];
118 	cin->cp_hdr.cp_unit = unit;
119 	cin->cp_hdr.cp_comm = CPREAD;
120 	cin->cp_hdr.cp_count = 1;	/* Get ready for input */
121 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)cin));
122 	cnlast = &cin->cp_hdr;
123 }
124 
125 cnclose(dev)
126 	dev_t dev;
127 {
128 	register struct tty *tp = cntty[minor(dev)];
129 
130 	(*linesw[tp->t_line].l_close)(tp);
131 	ttyclose(tp);
132 }
133 
134 /*ARGSUSED*/
135 cnread(dev, uio)
136 	dev_t dev;
137 	struct uio *uio;
138 {
139 	struct tty *tp = cntty[minor(dev)];
140 
141 	return ((*linesw[tp->t_line].l_read)(tp, uio));
142 }
143 
144 /*ARGSUSED*/
145 cnwrite(dev, uio)
146 	dev_t dev;
147 	struct uio *uio;
148 {
149 	register struct tty *tp = cntty[minor(dev)];
150 
151 	if (tp == &cons && constty &&
152 	    (constty->t_state & (TS_CARR_ON | TS_ISOPEN)) ==
153 	    (TS_CARR_ON | TS_ISOPEN))
154 		tp = constty;
155 	return ((*linesw[tp->t_line].l_write)(tp, uio));
156 }
157 
158 /*
159  * Got a console receive interrupt -
160  * the console processor wants to give us a character.
161  * Catch the character, and see who it goes to.
162  */
163 cnrint(dev)
164 	dev_t dev;
165 {
166 	register int unit, timo;
167 	register struct tty *tp;
168 	int c;
169 
170 	unit = minor(dev);
171 	if (!intenable || consoftc[unit].cs_flags&CSF_POLLING)
172 		return;
173 	/* make sure we dont take it from cache */
174 	uncache(&consin[unit].cpi_buf[0]);
175 	c = consin[unit].cpi_buf[0];
176 	waitforlast(timo);
177 	/* This resets status bits */
178 	consin[unit].cp_hdr.cp_unit = unit;
179 	/* Ready for new character */
180 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)&consin[unit]));
181 	cnlast = &consin[unit].cp_hdr;
182 
183 	tp = cntty[unit];
184 #ifdef KADB
185 	if (unit == CPCONS && kdbrintr(c, tp))
186 		return;
187 #endif
188 	(*linesw[tp->t_line].l_rint)(c, tp);
189 }
190 
191 cnioctl(dev, cmd, addr, flag)
192 	dev_t dev;
193 	caddr_t addr;
194 {
195 	register struct tty *tp = cntty[minor(dev)];
196 	register error;
197 
198 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr);
199 	if (error >= 0)
200 		return error;
201 	if ((error = ttioctl(tp, cmd, addr, flag)) < 0)
202 		error = ENOTTY;
203 	else if (cmd == TIOCSETP || cmd == TIOCSETN)
204 		cnparams(tp);
205 	return (error);
206 }
207 
208 int	consintr = 1;
209 /*
210  * Got a console transmission interrupt -
211  * the console processor wants another character.
212  */
213 cnxint(dev)
214 	dev_t dev;
215 {
216 	register struct tty *tp;
217 	register int unit;
218 
219 	if (!intenable || !consintr)
220 		return;
221 	unit = minor(dev);
222 #ifdef CPPERF
223 	scope_in(unit == CPCONS ? 1 : 2);
224 #endif
225 	tp = cntty[unit];
226 	tp->t_state &= ~TS_BUSY;
227 	consoftc[unit].cs_timo = 0;
228 	if (tp->t_line)
229 		(*linesw[tp->t_line].l_start)(tp);
230 	else
231 		cnstart(tp);
232 }
233 
234 cnstart(tp)
235 	register struct tty *tp;
236 {
237 	register c, s;
238 
239 #ifdef	CPPERF
240 	scope_in(minor(tp->t_dev) == CPCONS ? 3 : 4);
241 #endif
242 	s = spl8();
243 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
244 		goto out;
245 	if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
246 		if (tp->t_state&TS_ASLEEP) {
247 			tp->t_state &= ~TS_ASLEEP;
248 			wakeup((caddr_t)&tp->t_outq);
249 		}
250 		if (tp->t_wsel) {
251 			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
252 			tp->t_wsel = 0;
253 			tp->t_state &= ~TS_WCOLL;
254 		}
255 	}
256 	if (tp->t_outq.c_cc == 0)
257 		goto out;
258 	c = getc(&tp->t_outq) & 0xff;
259 	if ((tp->t_flags & (RAW|LITOUT)) == 0) {
260 		if (c <= 0177)
261 			c |= partab[c] & 0200;
262 		else {
263 			timeout(ttrstrt, (caddr_t)tp, (c&0177));
264 			tp->t_state |= TS_TIMEOUT;
265 			goto out;
266 		}
267 	}
268 	cnputchar(c, tp);
269 	tp->t_state |= TS_BUSY;
270 out:
271 	splx(s);
272 }
273 
274 cnputc(c)
275 	char c;
276 {
277 
278 	if (c == '\n')
279 		cnputchar('\r', (struct tty *)0);
280 	cnputchar(c, (struct tty *)0);
281 }
282 
283 /*
284  * Print a character on console.
285  */
286 cnputchar(c, tp)
287 	char c;
288 	register struct tty *tp;
289 {
290 	register timo;
291 	register struct cpdcb_o *current;
292 	register struct consoftc *cs;
293 	int unit;
294 
295 	/* tp == 0 only in system error messages */
296 	if (tp == 0) {
297 		tp = &cons;
298 		tp->t_dev = CPCONS;		/* may not be open */
299 		c |= partab[c&0177]&0200;
300 	}
301 	unit = minor(tp->t_dev);
302 	current = &consout[unit];
303 	timo = 30000;
304 	/*
305 	 * Try waiting for the console tty to finish previous command
306 	 * on this unit, otherwise give up after a reasonable time.
307 	 */
308 	do
309 		uncache(&current->cp_hdr.cp_unit);
310 	while ((current->cp_hdr.cp_unit&CPDONE) == 0 && --timo);
311 
312 	current->cp_hdr.cp_comm = CPWRITE;
313 	current->cp_hdr.cp_count = 1;
314 	current->cp_buf[0] = c;
315 	/*
316 	 * Try waiting for the console tty
317 	 * to accept previous command.
318 	 */
319 	waitforlast(timo);
320 
321 	/* Reset done bit */
322 	current->cp_hdr.cp_unit = (char)unit;
323 #ifdef	CPPERF
324 	if (intenable != 0)
325 		scope_in(5);
326 #endif
327 	cs = &consoftc[unit];
328 	cs->cs_lastc = c;
329 	cs->cs_timo = CN_TIMO;
330 	if ((cs->cs_flags&CSF_ACTIVE) == 0 && clk_enable) {
331 		cs->cs_flags |= CSF_ACTIVE;
332 		timeout(cnrestart, (caddr_t)tp, CN_TIMERVAL);
333 	}
334 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)current));
335 	cnlast = &current->cp_hdr;
336 }
337 
338 #if defined(KADB) || defined(GENERIC)
339 cngetc()
340 {
341 	register int c, s;
342 
343 	s = spl8();		/* block cnrint while we poll */
344 	c = cngetchar(&cons);
345 	if (c == '\r')
346 		c = '\n';
347 	splx(s);
348 	return (c);
349 }
350 
351 cngetchar(tp)
352 	register struct tty *tp;
353 {
354 	register timo, unit;
355 	register struct cpdcb_i *current;
356 	char c;
357 
358 	unit = minor(tp->t_dev);
359 	current = &consin[unit];
360 	waitforlast(timo);
361 	current->cp_hdr.cp_unit = unit;		/* Resets done bit */
362 	current->cp_hdr.cp_comm = CPREAD;
363 	current->cp_hdr.cp_count = 1;
364 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)current));
365 	while ((current->cp_hdr.cp_unit&CPDONE) == 0)
366 		uncache(&current->cp_hdr.cp_unit);
367 	uncache(&current->cpi_buf[0]);
368 	c = current->cpi_buf[0] & 0x7f;
369 	cnlast = &current->cp_hdr;
370 	return (c);
371 }
372 #endif
373 
374 /*
375  * Restart (if necessary) transfer to CP line.
376  * This way, lost transmit interrupts don't wedge output.
377  */
378 cnrestart(tp)
379 	struct tty *tp;
380 {
381 	register struct consoftc *cs;
382 
383 	cs = &consoftc[minor(tp->t_dev)];
384 	cs->cs_flags &= ~CSF_ACTIVE;
385 	if (cs->cs_timo) {
386 		if (--cs->cs_timo == 0) {
387 			cs->cs_wedgecnt++;
388 			cnreset(tp);
389 			cnputchar(cs->cs_lastc, tp);
390 		} else {
391 			cs->cs_flags |= CSF_ACTIVE;
392 			timeout(cnrestart, (caddr_t)tp, CN_TIMERVAL);
393 		}
394 	}
395 }
396 
397 /*
398  * Reset console.
399  */
400 cnreset(tp)
401 	register struct tty *tp;
402 {
403 	register timo;
404 	register struct cpdcb_o *current;
405 	register unit;
406 	static int failed;
407 
408 	unit = minor(tp->t_dev);
409 	current = &consout[unit];
410 	current->cp_hdr.cp_comm = CPRESET;
411 	current->cp_hdr.cp_count = 0;
412 	current->cp_hdr.cp_unit = unit;
413 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)current));
414 	cnlast = &current->cp_hdr;
415 	timo = 10000;
416 	do
417 		uncache(&current->cp_hdr.cp_unit);
418 	while ((current->cp_hdr.cp_unit&CPTAKE) == 0 && --timo);
419 	if (current->cp_hdr.cp_unit & CPTAKE) {
420 		cnparams(tp);
421 		failed = 0;
422 	} else if (failed++ == 0)
423 		log(LOG_ERR, "Console wedged, reset failed.\n");
424 }
425 
426 /*
427  * Set line parameters
428  */
429 cnparams(tp)
430 	register struct tty *tp;
431 {
432 	register timo;
433 	register struct cpdcb_o *current;
434 	int unit;
435 
436 	unit = minor(tp->t_dev);
437 	current = &consout[unit];
438 	timo = 30000;
439 	/*
440 	 * Try waiting for the console tty to finish any output,
441 	 * otherwise give up after a reasonable time.
442 	 */
443 	do
444 		uncache(&current->cp_hdr.cp_unit);
445 	while ((current->cp_hdr.cp_unit&CPDONE) == 0 && --timo);
446 	current->cp_hdr.cp_comm = CPSTTY;
447 	current->cp_hdr.cp_count = 4;
448 	current->cp_buf[0] = tp->t_ispeed;
449 	/* the rest are defaults */
450 	current->cp_buf[1] = 0;	/* no parity */
451 	current->cp_buf[2] = 0;	/* stop bits */
452 	current->cp_buf[3] = 8;	/* data bits */
453 	/* Reset done bit */
454 	current->cp_hdr.cp_unit = unit;
455 
456 	waitforlast(timo);
457 	mtpr(CPMDCB, vtoph((struct proc *)0, (unsigned)current));
458 	cnlast = &current->cp_hdr;
459 
460 	cnpostread(unit);
461 }
462 
463 #ifdef KADB
464 /*
465  * Turn input polling on/off (used by debugger).
466  */
467 cnpoll(onoff)
468 	int onoff;
469 {
470 
471 	if (!onoff) {
472 		consoftc[CPCONS].cs_flags &= ~CSF_POLLING;
473 		cnpostread(CPCONS);		/* restart input */
474 	} else
475 		consoftc[CPCONS].cs_flags |= CSF_POLLING;
476 }
477 #endif
478