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