xref: /csrg-svn/sys/kern/tty.c (revision 191)
1*191Sbill /*	tty.c	3.5	10/14/12	*/
239Sbill 
339Sbill /*
439Sbill  * general TTY subroutines
539Sbill  */
639Sbill #include "../h/param.h"
739Sbill #include "../h/systm.h"
839Sbill #include "../h/dir.h"
939Sbill #include "../h/user.h"
1039Sbill #include "../h/tty.h"
1139Sbill #include "../h/proc.h"
1239Sbill #include "../h/mx.h"
1339Sbill #include "../h/inode.h"
1439Sbill #include "../h/file.h"
1539Sbill #include "../h/reg.h"
1639Sbill #include "../h/conf.h"
1739Sbill #include "../h/buf.h"
1839Sbill 
1939Sbill char	partab[];
2039Sbill 
21146Sbill /*
22146Sbill  * When running dz's using only SAE (silo alarm) on input
23146Sbill  * it is necessary to call dzrint() at clock interrupt time.
24146Sbill  * This is unsafe unless spl5()s in tty code are changed to
25146Sbill  * spl6()s to block clock interrupts.  Note that the dh driver
26146Sbill  * currently in use works the same way as the dz, even though
27146Sbill  * we could try to more intelligently manage its silo.
28146Sbill  * Thus don't take this out if you have no dz's unless you
29146Sbill  * change clock.c and dhtimer().
30146Sbill  */
31146Sbill #define	spl5	spl6
3239Sbill 
3339Sbill /*
3439Sbill  * Input mapping table-- if an entry is non-zero, when the
3539Sbill  * corresponding character is typed preceded by "\" the escape
3639Sbill  * sequence is replaced by the table value.  Mostly used for
3739Sbill  * upper-case only terminals.
3839Sbill  */
3939Sbill 
4039Sbill char	maptab[] ={
4139Sbill 	000,000,000,000,000,000,000,000,
4239Sbill 	000,000,000,000,000,000,000,000,
4339Sbill 	000,000,000,000,000,000,000,000,
4439Sbill 	000,000,000,000,000,000,000,000,
4539Sbill 	000,'|',000,000,000,000,000,'`',
4639Sbill 	'{','}',000,000,000,000,000,000,
4739Sbill 	000,000,000,000,000,000,000,000,
4839Sbill 	000,000,000,000,000,000,000,000,
4939Sbill 	000,000,000,000,000,000,000,000,
5039Sbill 	000,000,000,000,000,000,000,000,
5139Sbill 	000,000,000,000,000,000,000,000,
5239Sbill 	000,000,000,000,000,000,'~',000,
5339Sbill 	000,'A','B','C','D','E','F','G',
5439Sbill 	'H','I','J','K','L','M','N','O',
5539Sbill 	'P','Q','R','S','T','U','V','W',
5639Sbill 	'X','Y','Z',000,000,000,000,000,
5739Sbill };
5839Sbill 
5939Sbill 
6039Sbill /*
6139Sbill  * shorthand
6239Sbill  */
6339Sbill #define	q1	tp->t_rawq
6439Sbill #define	q2	tp->t_canq
6539Sbill #define	q3	tp->t_outq
6639Sbill #define	q4	tp->t_un.t_ctlq
6739Sbill 
6839Sbill #define	OBUFSIZ	100
6939Sbill 
7039Sbill /*
7139Sbill  * routine called on first teletype open.
7239Sbill  * establishes a process group for distribution
7339Sbill  * of quits and interrupts from the tty.
7439Sbill  */
7539Sbill ttyopen(dev, tp)
7639Sbill dev_t dev;
7739Sbill register struct tty *tp;
7839Sbill {
7939Sbill 	register struct proc *pp;
8039Sbill 
8139Sbill 	pp = u.u_procp;
8239Sbill 	tp->t_dev = dev;
8339Sbill 	if(pp->p_pgrp == 0) {
8439Sbill 		u.u_ttyp = tp;
8539Sbill 		u.u_ttyd = dev;
86174Sbill 		tp->t_pgrp = pp->p_pid;
8739Sbill 		pp->p_pgrp = tp->t_pgrp;
8839Sbill 	}
8939Sbill 	tp->t_state &= ~WOPEN;
9039Sbill 	tp->t_state |= ISOPEN;
91174Sbill 	tp->t_line = 0;		/* conservative */
9239Sbill }
9339Sbill 
9439Sbill /*
9539Sbill  * set default control characters.
9639Sbill  */
9739Sbill ttychars(tp)
9839Sbill register struct tty *tp;
9939Sbill {
100174Sbill 
10139Sbill 	tun.t_intrc = CINTR;
10239Sbill 	tun.t_quitc = CQUIT;
10339Sbill 	tun.t_startc = CSTART;
10439Sbill 	tun.t_stopc = CSTOP;
10539Sbill 	tun.t_eofc = CEOT;
10639Sbill 	tun.t_brkc = CBRK;
10739Sbill 	tp->t_erase = CERASE;
10839Sbill 	tp->t_kill = CKILL;
109174Sbill /* begin local */
110174Sbill 	tlun.t_suspc = 0377;
111174Sbill 	tlun.t_dstopc = 0377;
112174Sbill 	tlun.t_rprntc = CTRL(r);
113174Sbill 	tlun.t_flushc = CTRL(o);
114174Sbill 	tlun.t_werasc = CTRL(w);
115174Sbill 	tlun.t_lnextc = CTRL(v);
116174Sbill 	tlun.t_lintr = CTRL(c);
117174Sbill 	tlun.t_lerase = CTRL(h);
118174Sbill 	tlun.t_lkill = CTRL(u);
119174Sbill 	tp->t_local = 0;
120174Sbill 	tp->t_lstate = 0;
121174Sbill /* end local */
12239Sbill }
12339Sbill 
12439Sbill /*
12539Sbill  * clean tp on last close
12639Sbill  */
12739Sbill ttyclose(tp)
12839Sbill register struct tty *tp;
12939Sbill {
13039Sbill 
13139Sbill 	tp->t_pgrp = 0;
13239Sbill 	wflushtty(tp);
13339Sbill 	tp->t_state = 0;
134174Sbill 	tp->t_line = 0;
13539Sbill }
13639Sbill 
13739Sbill /*
13839Sbill  * stty/gtty writearound
13939Sbill  */
14039Sbill stty()
14139Sbill {
14239Sbill 	u.u_arg[2] = u.u_arg[1];
14339Sbill 	u.u_arg[1] = TIOCSETP;
14439Sbill 	ioctl();
14539Sbill }
14639Sbill 
14739Sbill gtty()
14839Sbill {
14939Sbill 	u.u_arg[2] = u.u_arg[1];
15039Sbill 	u.u_arg[1] = TIOCGETP;
15139Sbill 	ioctl();
15239Sbill }
15339Sbill 
15439Sbill /*
155121Sbill  * Do nothing specific version of line
156121Sbill  * discipline specific ioctl command.
157121Sbill  */
158*191Sbill /*ARGSUSED*/
159121Sbill nullioctl(tp, cmd, addr)
160121Sbill register struct tty *tp;
161121Sbill caddr_t addr;
162121Sbill {
163121Sbill 
164121Sbill 	return (cmd);
165121Sbill }
166121Sbill 
167121Sbill /*
16839Sbill  * ioctl system call
16939Sbill  * Check legality, execute common code, and switch out to individual
17039Sbill  * device routine.
17139Sbill  */
17239Sbill ioctl()
17339Sbill {
17439Sbill 	register struct file *fp;
17539Sbill 	register struct inode *ip;
17639Sbill 	register struct a {
17739Sbill 		int	fdes;
17839Sbill 		int	cmd;
17939Sbill 		caddr_t	cmarg;
18039Sbill 	} *uap;
18139Sbill 	register dev_t dev;
18239Sbill 	register fmt;
18339Sbill 
18439Sbill 	uap = (struct a *)u.u_ap;
18539Sbill 	if ((fp = getf(uap->fdes)) == NULL)
18639Sbill 		return;
18739Sbill 	if (uap->cmd==FIOCLEX) {
18839Sbill 		u.u_pofile[uap->fdes] |= EXCLOSE;
18939Sbill 		return;
19039Sbill 	}
19139Sbill 	if (uap->cmd==FIONCLEX) {
19239Sbill 		u.u_pofile[uap->fdes] &= ~EXCLOSE;
19339Sbill 		return;
19439Sbill 	}
19539Sbill 	ip = fp->f_inode;
19639Sbill 	fmt = ip->i_mode & IFMT;
19739Sbill 	if (fmt != IFCHR && fmt != IFMPC) {
198174Sbill /* begin local */
199174Sbill 		if (uap->cmd==FIONREAD && (fmt == IFREG || fmt == IFDIR)) {
200174Sbill 			off_t nread = ip->i_size - fp->f_un.f_offset;
201174Sbill 
202174Sbill 			if (copyout((caddr_t)&nread, uap->cmarg, sizeof(off_t)))
203174Sbill 				u.u_error = EFAULT;
204174Sbill 		} else
205174Sbill /* end local */
206174Sbill 			u.u_error = ENOTTY;
20739Sbill 		return;
20839Sbill 	}
20939Sbill 	dev = ip->i_un.i_rdev;
21039Sbill 	u.u_r.r_val1 = 0;
21139Sbill 	(*cdevsw[major(dev)].d_ioctl)(dev, uap->cmd, uap->cmarg, fp->f_flag);
21239Sbill }
21339Sbill 
21439Sbill /*
21539Sbill  * Common code for several tty ioctl commands
21639Sbill  */
21739Sbill ttioccomm(com, tp, addr, dev)
21839Sbill register struct tty *tp;
21939Sbill caddr_t addr;
22039Sbill {
22139Sbill 	unsigned t;
222174Sbill 	struct sgttyb iocb;
223*191Sbill 	struct clist tq;
22439Sbill 	extern int nldisp;
22539Sbill 
22639Sbill 	switch(com) {
22739Sbill 
22839Sbill 	/*
22939Sbill 	 * get discipline number
23039Sbill 	 */
23139Sbill 	case TIOCGETD:
23239Sbill 		t = tp->t_line;
23339Sbill 		if (copyout((caddr_t)&t, addr, sizeof(t)))
23439Sbill 			u.u_error = EFAULT;
23539Sbill 		break;
23639Sbill 
23739Sbill 	/*
23839Sbill 	 * set line discipline
23939Sbill 	 */
24039Sbill 	case TIOCSETD:
24139Sbill 		if (copyin(addr, (caddr_t)&t, sizeof(t))) {
24239Sbill 			u.u_error = EFAULT;
24339Sbill 			break;
24439Sbill 		}
24539Sbill 		if (t >= nldisp) {
24639Sbill 			u.u_error = ENXIO;
24739Sbill 			break;
24839Sbill 		}
249174Sbill 		(void) spl5();
25039Sbill 		if (tp->t_line)
25139Sbill 			(*linesw[tp->t_line].l_close)(tp);
25239Sbill 		if (t)
25339Sbill 			(*linesw[t].l_open)(dev, tp, addr);
25439Sbill 		if (u.u_error==0)
25539Sbill 			tp->t_line = t;
256174Sbill 		(void) spl0();
25739Sbill 		break;
25839Sbill 
25939Sbill 	/*
26039Sbill 	 * prevent more opens on channel
26139Sbill 	 */
26239Sbill 	case TIOCEXCL:
26339Sbill 		tp->t_state |= XCLUDE;
26439Sbill 		break;
26539Sbill 
26639Sbill 	case TIOCNXCL:
26739Sbill 		tp->t_state &= ~XCLUDE;
26839Sbill 		break;
26939Sbill 
27039Sbill 	/*
27139Sbill 	 * Set new parameters
27239Sbill 	 */
27339Sbill 	case TIOCSETP:
274*191Sbill 	case TIOCSETN:
27539Sbill 		if (copyin(addr, (caddr_t)&iocb, sizeof(iocb))) {
27639Sbill 			u.u_error = EFAULT;
27739Sbill 			return(1);
27839Sbill 		}
279121Sbill 		(void) spl5();
280174Sbill 		if (tp->t_line == 0) {
281174Sbill 			if (com == TIOCSETP)
282174Sbill 				wflushtty(tp);
283174Sbill 			while (canon(tp)>=0)
284174Sbill 				;
285174Sbill 		} else if (tp->t_line == NTTYDISC) {
286174Sbill 			if (tp->t_flags&RAW || iocb.sg_flags&RAW ||
287174Sbill 			    com == TIOCSETP)
288174Sbill 				wflushtty(tp);
289174Sbill 			else if ((tp->t_flags&CBREAK) != (iocb.sg_flags&CBREAK)) {
290174Sbill 				if (iocb.sg_flags & CBREAK) {
291174Sbill 					catq(&tp->t_rawq, &tp->t_canq);
292174Sbill 					tq = tp->t_rawq;
293174Sbill 					tp->t_rawq = tp->t_canq;
294174Sbill 					tp->t_canq = tq;
295174Sbill 				} else {
296174Sbill 					tp->t_local |= LPENDIN;
297174Sbill 					if (tp->t_canq.c_cc)
298174Sbill 						panic("ioccom canq");
299174Sbill 					if (tp->t_chan)
300174Sbill 						(void) sdata(tp->t_chan);
301174Sbill 					else
302174Sbill 						wakeup((caddr_t)&tp->t_rawq);
303174Sbill 				}
304174Sbill 			}
305174Sbill 		}
30639Sbill 		if ((tp->t_state&SPEEDS)==0) {
307174Sbill 			tp->t_ispeed = iocb.sg_ispeed;
308174Sbill 			tp->t_ospeed = iocb.sg_ospeed;
30939Sbill 		}
310174Sbill 		tp->t_erase = iocb.sg_erase;
311174Sbill 		tp->t_kill = iocb.sg_kill;
312174Sbill 		tp->t_flags = iocb.sg_flags;
313121Sbill 		(void) spl0();
31439Sbill 		break;
31539Sbill 
31639Sbill 	/*
31739Sbill 	 * send current parameters to user
31839Sbill 	 */
31939Sbill 	case TIOCGETP:
320174Sbill 		iocb.sg_ispeed = tp->t_ispeed;
321174Sbill 		iocb.sg_ospeed = tp->t_ospeed;
322174Sbill 		iocb.sg_erase = tp->t_erase;
323174Sbill 		iocb.sg_kill = tp->t_kill;
324174Sbill 		iocb.sg_flags = tp->t_flags;
32539Sbill 		if (copyout((caddr_t)&iocb, addr, sizeof(iocb)))
32639Sbill 			u.u_error = EFAULT;
32739Sbill 		break;
32839Sbill 
32939Sbill 	/*
33039Sbill 	 * Hang up line on last close
33139Sbill 	 */
33239Sbill 
33339Sbill 	case TIOCHPCL:
33439Sbill 		tp->t_state |= HUPCLS;
33539Sbill 		break;
33639Sbill 
33739Sbill 	case TIOCFLUSH:
33839Sbill 		flushtty(tp);
33939Sbill 		break;
34039Sbill 
34139Sbill 	/*
34239Sbill 	 * ioctl entries to line discipline
34339Sbill 	 */
34439Sbill 	case DIOCSETP:
34539Sbill 	case DIOCGETP:
346121Sbill 		if ((*linesw[tp->t_line].l_ioctl)(com, tp, addr))
347121Sbill 			u.u_error = ENOTTY;
34839Sbill 		break;
34939Sbill 
35039Sbill 	/*
35139Sbill 	 * set and fetch special characters
35239Sbill 	 */
35339Sbill 	case TIOCSETC:
354174Sbill 		if (copyin(addr, (caddr_t)&tun, sizeof(struct tchars)))
35539Sbill 			u.u_error = EFAULT;
35639Sbill 		break;
35739Sbill 
35839Sbill 	case TIOCGETC:
359174Sbill 		if (copyout((caddr_t)&tun, addr, sizeof(struct tchars)))
36039Sbill 			u.u_error = EFAULT;
36139Sbill 		break;
36239Sbill 
363174Sbill /* local ioctls */
364174Sbill 	case TIOCSLTC:
365174Sbill 		if (copyin(addr, (caddr_t)&tlun, sizeof (struct ltchars)))
366174Sbill 			u.u_error = EFAULT;
367174Sbill 		break;
368174Sbill 
369174Sbill 	case TIOCGLTC:
370174Sbill 		if (copyout((caddr_t)&tlun, addr, sizeof (struct ltchars)))
371174Sbill 			u.u_error = EFAULT;
372174Sbill 		break;
373174Sbill 
374174Sbill 	case FIONREAD: {
375174Sbill 		off_t nread = tp->t_canq.c_cc;
376174Sbill 
377174Sbill 		if (tp->t_flags & (RAW|CBREAK))
378174Sbill 			nread += tp->t_rawq.c_cc;
379174Sbill 		if (copyout((caddr_t)&nread, addr, sizeof (off_t)))
380174Sbill 			u.u_error = EFAULT;
381174Sbill 		break;
382174Sbill 		}
383174Sbill 
384174Sbill 	/*
385174Sbill 	 * Should allow SPGRP and GPGRP only if tty open for reading.
386174Sbill 	 */
387174Sbill 	case TIOCSPGRP:
388174Sbill 		tp->t_pgrp = (int)addr;
389174Sbill 		break;
390174Sbill 
391174Sbill 	case TIOCGPGRP:
392174Sbill 		if (copyout((caddr_t)&tp->t_pgrp, addr, sizeof(tp->t_pgrp)))
393174Sbill 			u.u_error = EFAULT;
394174Sbill 		break;
395174Sbill 
396174Sbill 	/*
397174Sbill 	 * Modify local mode word.
398174Sbill 	 */
399174Sbill 	case TIOCLBIS:
400174Sbill 		tp->t_local |= (int)addr;
401174Sbill 		break;
402174Sbill 
403174Sbill 	case TIOCLBIC:
404174Sbill 		tp->t_local &= ~(int)addr;
405174Sbill 		break;
406174Sbill 
407174Sbill 	case TIOCLSET:
408174Sbill 		tp->t_local = (int)addr;
409174Sbill 		break;
410174Sbill 
411174Sbill 	case TIOCLGET:
412174Sbill 		if (copyout((caddr_t)&tp->t_local, addr, sizeof(tp->t_local)))
413174Sbill 			u.u_error = EFAULT;
414174Sbill 		break;
415174Sbill 
416174Sbill /* end of locals */
41739Sbill 	default:
41839Sbill 		return(0);
41939Sbill 	}
42039Sbill 	return(1);
42139Sbill }
42239Sbill 
42339Sbill /*
42439Sbill  * Wait for output to drain, then flush input waiting.
42539Sbill  */
42639Sbill wflushtty(tp)
42739Sbill register struct tty *tp;
42839Sbill {
42939Sbill 
430121Sbill 	(void) spl5();
43139Sbill 	while (tp->t_outq.c_cc && tp->t_state&CARR_ON) {
43239Sbill 		(*tp->t_oproc)(tp);
43339Sbill 		tp->t_state |= ASLEEP;
43439Sbill 		sleep((caddr_t)&tp->t_outq, TTOPRI);
43539Sbill 	}
43639Sbill 	flushtty(tp);
437121Sbill 	(void) spl0();
43839Sbill }
43939Sbill 
44039Sbill /*
44139Sbill  * flush all TTY queues
44239Sbill  */
44339Sbill flushtty(tp)
44439Sbill register struct tty *tp;
44539Sbill {
44639Sbill 	register s;
44739Sbill 
448121Sbill 	s = spl6();
44939Sbill 	while (getc(&tp->t_canq) >= 0)
45039Sbill 		;
45139Sbill 	wakeup((caddr_t)&tp->t_rawq);
45239Sbill 	wakeup((caddr_t)&tp->t_outq);
45339Sbill 	tp->t_state &= ~TTSTOP;
45439Sbill 	(*cdevsw[major(tp->t_dev)].d_stop)(tp);
45539Sbill 	while (getc(&tp->t_outq) >= 0)
45639Sbill 		;
45739Sbill 	while (getc(&tp->t_rawq) >= 0)
45839Sbill 		;
45939Sbill 	tp->t_delct = 0;
460174Sbill 	tp->t_rocount = 0;		/* local */
461174Sbill 	tp->t_lstate = 0;	/* reset */
46239Sbill 	splx(s);
46339Sbill }
46439Sbill 
46539Sbill 
46639Sbill 
46739Sbill /*
46839Sbill  * transfer raw input list to canonical list,
46939Sbill  * doing erase-kill processing and handling escapes.
47039Sbill  * It waits until a full line has been typed in cooked mode,
47139Sbill  * or until any character has been typed in raw mode.
47239Sbill  */
47339Sbill canon(tp)
47439Sbill register struct tty *tp;
47539Sbill {
47639Sbill 	register char *bp;
47739Sbill 	char *bp1;
47839Sbill 	register int c;
47939Sbill 	int mc;
48039Sbill 	int s;
48139Sbill 
48239Sbill 	if ((tp->t_flags&(RAW|CBREAK))==0 && tp->t_delct==0
48339Sbill 	    || (tp->t_flags&(RAW|CBREAK))!=0 && tp->t_rawq.c_cc==0) {
48439Sbill 		return(-1);
48539Sbill 	}
48639Sbill 	s = spl0();
48739Sbill loop:
48839Sbill 	bp = &canonb[2];
48939Sbill 	while ((c=getc(&tp->t_rawq)) >= 0) {
49039Sbill 		if ((tp->t_flags&(RAW|CBREAK))==0) {
49139Sbill 			if (c==0377) {
49239Sbill 				tp->t_delct--;
49339Sbill 				break;
49439Sbill 			}
49539Sbill 			if (bp[-1]!='\\') {
49639Sbill 				if (c==tp->t_erase) {
49739Sbill 					if (bp > &canonb[2])
49839Sbill 						bp--;
49939Sbill 					continue;
50039Sbill 				}
50139Sbill 				if (c==tp->t_kill)
50239Sbill 					goto loop;
50339Sbill 				if (c==tun.t_eofc)
50439Sbill 					continue;
50539Sbill 			} else {
50639Sbill 				mc = maptab[c];
50739Sbill 				if (c==tp->t_erase || c==tp->t_kill)
50839Sbill 					mc = c;
50939Sbill 				if (mc && (mc==c || (tp->t_flags&LCASE))) {
51039Sbill 					if (bp[-2] != '\\')
51139Sbill 						c = mc;
51239Sbill 					bp--;
51339Sbill 				}
51439Sbill 			}
51539Sbill 		}
51639Sbill 		*bp++ = c;
51739Sbill 		if (bp>=canonb+CANBSIZ)
51839Sbill 			break;
51939Sbill 	}
52039Sbill 	bp1 = &canonb[2];
521121Sbill 	(void) b_to_q(bp1, bp-bp1, &tp->t_canq);
52239Sbill 
52339Sbill 	if (tp->t_state&TBLOCK && tp->t_rawq.c_cc < TTYHOG/5) {
52439Sbill 		if (putc(tun.t_startc, &tp->t_outq)==0) {
52539Sbill 			tp->t_state &= ~TBLOCK;
52639Sbill 			ttstart(tp);
52739Sbill 		}
52839Sbill 		tp->t_char = 0;
52939Sbill 	}
53039Sbill 
53139Sbill 	splx(s);
53239Sbill 	return(0);
53339Sbill }
53439Sbill 
53539Sbill 
53639Sbill /*
53739Sbill  * block transfer input handler.
53839Sbill  */
53939Sbill ttyrend(tp, pb, pe)
54039Sbill register struct tty *tp;
54139Sbill register char *pb, *pe;
54239Sbill {
54339Sbill 	int	tandem;
54439Sbill 
54539Sbill 	tandem = tp->t_flags&TANDEM;
54639Sbill 	if (tp->t_flags&RAW) {
547121Sbill 		(void) b_to_q(pb, pe-pb, &tp->t_rawq);
54839Sbill 		if (tp->t_chan)
549121Sbill 			(void) sdata(tp->t_chan); else
55039Sbill 			wakeup((caddr_t)&tp->t_rawq);
55139Sbill 	} else {
55239Sbill 		tp->t_flags &= ~TANDEM;
55339Sbill 		while (pb < pe)
55439Sbill 			ttyinput(*pb++, tp);
55539Sbill 		tp->t_flags |= tandem;
55639Sbill 	}
55739Sbill 	if (tandem)
55839Sbill 		ttyblock(tp);
55939Sbill }
56039Sbill 
56139Sbill /*
56239Sbill  * Place a character on raw TTY input queue, putting in delimiters
56339Sbill  * and waking up top half as needed.
56439Sbill  * Also echo if required.
56539Sbill  * The arguments are the character and the appropriate
56639Sbill  * tty structure.
56739Sbill  */
56839Sbill ttyinput(c, tp)
56939Sbill register c;
57039Sbill register struct tty *tp;
57139Sbill {
57239Sbill 	register int t_flags;
57339Sbill 	register struct chan *cp;
57439Sbill 
57539Sbill 	tk_nin += 1;
57639Sbill 	c &= 0377;
57739Sbill 	t_flags = tp->t_flags;
57839Sbill 	if (t_flags&TANDEM)
57939Sbill 		ttyblock(tp);
58039Sbill 	if ((t_flags&RAW)==0) {
58139Sbill 		c &= 0177;
58239Sbill 		if (tp->t_state&TTSTOP) {
58339Sbill 			if (c==tun.t_startc) {
58439Sbill 				tp->t_state &= ~TTSTOP;
58539Sbill 				ttstart(tp);
58639Sbill 				return;
58739Sbill 			}
58839Sbill 			if (c==tun.t_stopc)
58939Sbill 				return;
59039Sbill 			tp->t_state &= ~TTSTOP;
59139Sbill 			ttstart(tp);
59239Sbill 		} else {
59339Sbill 			if (c==tun.t_stopc) {
59439Sbill 				tp->t_state |= TTSTOP;
59539Sbill 				(*cdevsw[major(tp->t_dev)].d_stop)(tp);
59639Sbill 				return;
59739Sbill 			}
59839Sbill 			if (c==tun.t_startc)
59939Sbill 				return;
60039Sbill 		}
60139Sbill 		if (c==tun.t_quitc || c==tun.t_intrc) {
60239Sbill 			flushtty(tp);
60339Sbill 			c = (c==tun.t_intrc) ? SIGINT:SIGQUIT;
60439Sbill 			if (tp->t_chan)
60539Sbill 				scontrol(tp->t_chan, M_SIG, c);
60639Sbill 			else
607174Sbill 				gsignal(tp->t_pgrp, c);
60839Sbill 			return;
60939Sbill 		}
61039Sbill 		if (c=='\r' && t_flags&CRMOD)
61139Sbill 			c = '\n';
61239Sbill 	}
61339Sbill 	if (tp->t_rawq.c_cc>TTYHOG) {
61439Sbill 		flushtty(tp);
61539Sbill 		return;
61639Sbill 	}
61739Sbill 	if (t_flags&LCASE && c>='A' && c<='Z')
61839Sbill 		c += 'a'-'A';
619121Sbill 	(void) putc(c, &tp->t_rawq);
62039Sbill 	if (t_flags&(RAW|CBREAK)||(c=='\n'||c==tun.t_eofc||c==tun.t_brkc)) {
62139Sbill 		if ((t_flags&(RAW|CBREAK))==0 && putc(0377, &tp->t_rawq)==0)
62239Sbill 			tp->t_delct++;
62339Sbill 		if ((cp=tp->t_chan)!=NULL)
624121Sbill 			(void) sdata(cp); else
62539Sbill 			wakeup((caddr_t)&tp->t_rawq);
62639Sbill 	}
62739Sbill 	if (t_flags&ECHO) {
62839Sbill 		ttyoutput(c, tp);
62939Sbill 		if (c==tp->t_kill && (t_flags&(RAW|CBREAK))==0)
63039Sbill 			ttyoutput('\n', tp);
63139Sbill 		ttstart(tp);
63239Sbill 	}
63339Sbill }
63439Sbill 
63539Sbill 
63639Sbill /*
63739Sbill  * Send stop character on input overflow.
63839Sbill  */
63939Sbill ttyblock(tp)
64039Sbill register struct tty *tp;
64139Sbill {
64239Sbill 	register x;
64339Sbill 	x = q1.c_cc + q2.c_cc;
64439Sbill 	if (q1.c_cc > TTYHOG) {
64539Sbill 		flushtty(tp);
64639Sbill 		tp->t_state &= ~TBLOCK;
64739Sbill 	}
64839Sbill 	if (x >= TTYHOG/2) {
64939Sbill 		if (putc(tun.t_stopc, &tp->t_outq)==0) {
65039Sbill 			tp->t_state |= TBLOCK;
65139Sbill 			tp->t_char++;
65239Sbill 			ttstart(tp);
65339Sbill 		}
65439Sbill 	}
65539Sbill }
65639Sbill 
65739Sbill /*
65839Sbill  * put character on TTY output queue, adding delays,
65939Sbill  * expanding tabs, and handling the CR/NL bit.
66039Sbill  * It is called both from the top half for output, and from
66139Sbill  * interrupt level for echoing.
66239Sbill  * The arguments are the character and the tty structure.
66339Sbill  */
66439Sbill ttyoutput(c, tp)
66539Sbill register c;
66639Sbill register struct tty *tp;
66739Sbill {
66839Sbill 	register char *colp;
66939Sbill 	register ctype;
67039Sbill 
67139Sbill 	/*
67239Sbill 	 * Ignore EOT in normal mode to avoid hanging up
67339Sbill 	 * certain terminals.
67439Sbill 	 * In raw mode dump the char unchanged.
67539Sbill 	 */
67639Sbill 	if ((tp->t_flags&RAW)==0) {
67739Sbill 		c &= 0177;
67839Sbill 		if ((tp->t_flags&CBREAK)==0 && c==CEOT)
67939Sbill 			return;
68039Sbill 	} else {
681121Sbill 		tk_nout++;
682121Sbill 		(void) putc(c, &tp->t_outq);
68339Sbill 		return;
68439Sbill 	}
68539Sbill 
68639Sbill 	/*
68739Sbill 	 * Turn tabs to spaces as required
68839Sbill 	 */
68939Sbill 	if (c=='\t' && (tp->t_flags&TBDELAY)==XTABS) {
690121Sbill 		c = 8 - (tp->t_col & 7);
691121Sbill 		(void) b_to_q("        ", c, &tp->t_outq);
692121Sbill 		tp->t_col += c;
693121Sbill 		tk_nout += c;
69439Sbill 		return;
69539Sbill 	}
696121Sbill 	tk_nout++;
69739Sbill 	/*
69839Sbill 	 * for upper-case-only terminals,
69939Sbill 	 * generate escapes.
70039Sbill 	 */
70139Sbill 	if (tp->t_flags&LCASE) {
70239Sbill 		colp = "({)}!|^~'`";
70339Sbill 		while(*colp++)
70439Sbill 			if(c == *colp++) {
70539Sbill 				ttyoutput('\\', tp);
70639Sbill 				c = colp[-2];
70739Sbill 				break;
70839Sbill 			}
70939Sbill 		if ('a'<=c && c<='z')
71039Sbill 			c += 'A' - 'a';
71139Sbill 	}
71239Sbill 	/*
71339Sbill 	 * turn <nl> to <cr><lf> if desired.
71439Sbill 	 */
71539Sbill 	if (c=='\n' && tp->t_flags&CRMOD)
71639Sbill 		ttyoutput('\r', tp);
717121Sbill 	(void) putc(c, &tp->t_outq);
71839Sbill 	/*
71939Sbill 	 * Calculate delays.
72039Sbill 	 * The numbers here represent clock ticks
72139Sbill 	 * and are not necessarily optimal for all terminals.
72239Sbill 	 * The delays are indicated by characters above 0200.
72339Sbill 	 * In raw mode there are no delays and the
72439Sbill 	 * transmission path is 8 bits wide.
72539Sbill 	 */
72639Sbill 	colp = &tp->t_col;
72739Sbill 	ctype = partab[c];
72839Sbill 	c = 0;
72939Sbill 	switch (ctype&077) {
73039Sbill 
73139Sbill 	/* ordinary */
73239Sbill 	case 0:
73339Sbill 		(*colp)++;
73439Sbill 
73539Sbill 	/* non-printing */
73639Sbill 	case 1:
73739Sbill 		break;
73839Sbill 
73939Sbill 	/* backspace */
74039Sbill 	case 2:
74139Sbill 		if (*colp)
74239Sbill 			(*colp)--;
74339Sbill 		break;
74439Sbill 
74539Sbill 	/* newline */
74639Sbill 	case 3:
74739Sbill 		ctype = (tp->t_flags >> 8) & 03;
74839Sbill 		if(ctype == 1) { /* tty 37 */
74939Sbill 			if (*colp)
75039Sbill 				c = max(((unsigned)*colp>>4) + 3, (unsigned)6);
75139Sbill 		} else
75239Sbill 		if(ctype == 2) { /* vt05 */
75339Sbill 			c = 6;
75439Sbill 		}
75539Sbill 		*colp = 0;
75639Sbill 		break;
75739Sbill 
75839Sbill 	/* tab */
75939Sbill 	case 4:
76039Sbill 		ctype = (tp->t_flags >> 10) & 03;
76139Sbill 		if(ctype == 1) { /* tty 37 */
76239Sbill 			c = 1 - (*colp | ~07);
76339Sbill 			if(c < 5)
76439Sbill 				c = 0;
76539Sbill 		}
76639Sbill 		*colp |= 07;
76739Sbill 		(*colp)++;
76839Sbill 		break;
76939Sbill 
77039Sbill 	/* vertical motion */
77139Sbill 	case 5:
77239Sbill 		if(tp->t_flags & VTDELAY) /* tty 37 */
77339Sbill 			c = 0177;
77439Sbill 		break;
77539Sbill 
77639Sbill 	/* carriage return */
77739Sbill 	case 6:
77839Sbill 		ctype = (tp->t_flags >> 12) & 03;
77939Sbill 		if(ctype == 1) { /* tn 300 */
78039Sbill 			c = 5;
78139Sbill 		} else if(ctype == 2) { /* ti 700 */
78239Sbill 			c = 10;
78339Sbill 		} else if(ctype == 3) { /* concept 100 */
78439Sbill 			int i;
78539Sbill 			for (i= *colp; i<9; i++)
786121Sbill 				(void) putc(0177, &tp->t_outq);
78739Sbill 		}
78839Sbill 		*colp = 0;
78939Sbill 	}
79039Sbill 	if(c)
791121Sbill 		(void) putc(c|0200, &tp->t_outq);
79239Sbill }
79339Sbill 
79439Sbill /*
79539Sbill  * Restart typewriter output following a delay
79639Sbill  * timeout.
79739Sbill  * The name of the routine is passed to the timeout
79839Sbill  * subroutine and it is called during a clock interrupt.
79939Sbill  */
80039Sbill ttrstrt(tp)
80139Sbill register struct tty *tp;
80239Sbill {
80339Sbill 
80439Sbill 	tp->t_state &= ~TIMEOUT;
80539Sbill 	ttstart(tp);
80639Sbill }
80739Sbill 
80839Sbill /*
80939Sbill  * Start output on the typewriter. It is used from the top half
81039Sbill  * after some characters have been put on the output queue,
81139Sbill  * from the interrupt routine to transmit the next
81239Sbill  * character, and after a timeout has finished.
81339Sbill  */
81439Sbill ttstart(tp)
81539Sbill register struct tty *tp;
81639Sbill {
81739Sbill 	register s;
81839Sbill 
81939Sbill 	s = spl5();
82039Sbill 	if((tp->t_state&(TIMEOUT|TTSTOP|BUSY)) == 0)
82139Sbill 		(*tp->t_oproc)(tp);
82239Sbill 	splx(s);
82339Sbill }
82439Sbill 
82539Sbill /*
82639Sbill  * Called from device's read routine after it has
82739Sbill  * calculated the tty-structure given as argument.
82839Sbill  */
82939Sbill ttread(tp)
83039Sbill register struct tty *tp;
83139Sbill {
83239Sbill register s;
83339Sbill 
83439Sbill 	if ((tp->t_state&CARR_ON)==0)
83539Sbill 		return(-1);
83639Sbill 	s = spl5();
83739Sbill 	if (tp->t_canq.c_cc==0)
83839Sbill 		while (canon(tp)<0)
83939Sbill 			if (tp->t_chan==NULL) {
84039Sbill 				sleep((caddr_t)&tp->t_rawq, TTIPRI);
84139Sbill 			} else {
84239Sbill 				splx(s);
84339Sbill 				return(0);
84439Sbill 			}
84539Sbill 	splx(s);
84639Sbill 	while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0)
84739Sbill 			;
84839Sbill 	return(tp->t_rawq.c_cc+tp->t_canq.c_cc);
84939Sbill }
85039Sbill 
85139Sbill /*
85239Sbill  * Called from the device's write routine after it has
85339Sbill  * calculated the tty-structure given as argument.
85439Sbill  */
85539Sbill caddr_t
85639Sbill ttwrite(tp)
85739Sbill register struct tty *tp;
85839Sbill {
85939Sbill 	/*
86039Sbill 	 * THE POSITIONING OF CP, CC, AND CE ARE CRITICAL
86139Sbill 	 * AND MUST NOT BE CHANGED WITHOUT PATCHING
86239Sbill 	 * THE 'ASM' INLINES BELOW.  WATCH OUT.
86339Sbill 	 */
86439Sbill 	register char *cp;
86539Sbill 	register int cc, ce;
86639Sbill 	register i;
86739Sbill 	char obuf[OBUFSIZ];
86839Sbill 
86939Sbill 	if ((tp->t_state&CARR_ON)==0)
87039Sbill 		return(NULL);
87139Sbill 	while (u.u_count) {
87239Sbill 		cc = MIN(u.u_count, OBUFSIZ);
87339Sbill 		cp = obuf;
87439Sbill 		iomove(cp, (unsigned)cc, B_WRITE);
87539Sbill 		if (u.u_error)
87639Sbill 			break;
877121Sbill 		(void) spl5();
87839Sbill 		while (tp->t_outq.c_cc > TTHIWAT) {
87939Sbill 			ttstart(tp);
88039Sbill 			tp->t_state |= ASLEEP;
88139Sbill 			if (tp->t_chan) {
88239Sbill 				u.u_base -= cc;
88339Sbill 				u.u_offset -= cc;
88439Sbill 				u.u_count += cc;
885121Sbill 				(void) spl0();
88639Sbill 				return((caddr_t)&tp->t_outq);
88739Sbill 			}
88839Sbill 			sleep((caddr_t)&tp->t_outq, TTOPRI);
88939Sbill 		}
890121Sbill 		(void) spl0();
89139Sbill 		if (tp->t_flags&LCASE) {
89239Sbill 			while (cc--)
89339Sbill 				ttyoutput(*cp++,tp);
89439Sbill 			continue;
89539Sbill 		}
89639Sbill 		while (cc) {
89739Sbill 			if (tp->t_flags&RAW)
89839Sbill 				ce=cc;
89939Sbill 			else {
90039Sbill #ifdef VAX
90139Sbill 				asm("	scanc	r9,(r10),_partab,$077");
90239Sbill 				asm("	subl3	r0,r9,r8");
90339Sbill #else
90439Sbill 				ce=0;
90539Sbill 				while(((partab[*(cp+ce)]&077)==0)&&(ce<cc))
90639Sbill 					ce++;
90739Sbill #endif
90839Sbill 				if (ce==0) {
90939Sbill 					ttyoutput(*cp++,tp);
91039Sbill 					cc--;
911121Sbill 					goto check;
91239Sbill 				}
91339Sbill 			}
91439Sbill 			i=b_to_q(cp,ce,&tp->t_outq);
91539Sbill 			ce-=i;
91639Sbill 			tk_nout+=ce;
91739Sbill 			tp->t_col+=ce;
91839Sbill 			cp+=ce;
91939Sbill 			cc-=ce;
920121Sbill check:
921121Sbill 			if (tp->t_outq.c_cc > TTHIWAT) {
922121Sbill 				(void) spl5();
92339Sbill 				while (tp->t_outq.c_cc > TTHIWAT) {
92439Sbill 					ttstart(tp);
92539Sbill 					tp->t_state |= ASLEEP;
92639Sbill 					sleep((caddr_t)&tp->t_outq, TTOPRI);
92739Sbill 				}
928121Sbill 				(void) spl0();
92939Sbill 			}
93039Sbill 		}
93139Sbill 	}
93239Sbill 	ttstart(tp);
93339Sbill 	return(NULL);
93439Sbill }
935