tty.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,414 行 · 第 1/4 页

C
2,414
字号
				 * the checks in ttywait() since the timeout				 * will save us.				 */				SET(tp->t_state, TS_SO_OCOMPLETE);				ttysleep(tp, TSA_OCOMPLETE(tp), TTOPRI,					 "ttyfls", hz / 10);				/*				 * Don't try sending the stop character again.				 */				CLR(tp->t_state, TS_TBLOCK);				goto again;			}#endif		}	}	if (rw & FWRITE) {		FLUSHQ(&tp->t_outq);		ttwwakeup(tp);	}	splx(s);}/* * Copy in the default termios characters. */voidtermioschars(t)	struct termios *t;{	bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);}/* * Old interface. */voidttychars(tp)	struct tty *tp;{	termioschars(&tp->t_termios);}/* * Handle input high water.  Send stop character for the IXOFF case.  Turn * on our input flow control bit and propagate the changes to the driver. * XXX the stop character should be put in a special high priority queue. */voidttyblock(tp)	struct tty *tp;{	SET(tp->t_state, TS_TBLOCK);	if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE &&	    putc(tp->t_cc[VSTOP], &tp->t_outq) != 0)		CLR(tp->t_state, TS_TBLOCK);	/* try again later */	ttstart(tp);}/* * Handle input low water.  Send start character for the IXOFF case.  Turn * off our input flow control bit and propagate the changes to the driver. * XXX the start character should be put in a special high priority queue. */static voidttyunblock(tp)	struct tty *tp;{	CLR(tp->t_state, TS_TBLOCK);	if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE &&	    putc(tp->t_cc[VSTART], &tp->t_outq) != 0)		SET(tp->t_state, TS_TBLOCK);	/* try again later */	ttstart(tp);}voidttrstrt(tp_arg)	void *tp_arg;{	struct tty *tp;	int s;#ifdef DIAGNOSTIC	if (tp_arg == NULL)		panic("ttrstrt");#endif	tp = tp_arg;	s = spltty();	CLR(tp->t_state, TS_TIMEOUT);	ttstart(tp);	splx(s);}intttstart(tp)	struct tty *tp;{	if (tp->t_oproc != NULL)	/* XXX: Kludge for pty. */		(*tp->t_oproc)(tp);	return (0);}/* * "close" a line discipline */intttylclose(tp, flag)	struct tty *tp;	int flag;{	if (flag & FNONBLOCK || ttywflush(tp))		ttyflush(tp, FREAD | FWRITE);	return (0);}/* * Handle modem control transition on a tty. * Flag indicates new state of carrier. * Returns 0 if the line should be turned off, otherwise 1. */intttymodem(tp, flag)	register struct tty *tp;	int flag;{	if (ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, MDMBUF)) {		/*		 * MDMBUF: do flow control according to carrier flag		 * XXX TS_CAR_OFLOW doesn't do anything yet.  TS_TTSTOP		 * works if IXON and IXANY are clear.		 */		if (flag) {			CLR(tp->t_state, TS_CAR_OFLOW);			CLR(tp->t_state, TS_TTSTOP);			ttstart(tp);		} else if (!ISSET(tp->t_state, TS_CAR_OFLOW)) {			SET(tp->t_state, TS_CAR_OFLOW);			SET(tp->t_state, TS_TTSTOP);#ifdef sun4c						/* XXX */			(*tp->t_stop)(tp, 0);#else			(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);#endif		}	} else if (flag == 0) {		/*		 * Lost carrier.		 */		CLR(tp->t_state, TS_CARR_ON);		if (ISSET(tp->t_state, TS_ISOPEN) &&		    !ISSET(tp->t_cflag, CLOCAL)) {			SET(tp->t_state, TS_ZOMBIE);			CLR(tp->t_state, TS_CONNECTED);			if (tp->t_session && tp->t_session->s_leader)				psignal(tp->t_session->s_leader, SIGHUP);			ttyflush(tp, FREAD | FWRITE);			return (0);		}	} else {		/*		 * Carrier now on.		 */		SET(tp->t_state, TS_CARR_ON);		if (!ISSET(tp->t_state, TS_ZOMBIE))			SET(tp->t_state, TS_CONNECTED);		wakeup(TSA_CARR_ON(tp));		ttwakeup(tp);		ttwwakeup(tp);	}	return (1);}/* * Reinput pending characters after state switch * call at spltty(). */static voidttypend(tp)	register struct tty *tp;{	struct clist tq;	register int c;	CLR(tp->t_lflag, PENDIN);	SET(tp->t_state, TS_TYPEN);	/*	 * XXX this assumes too much about clist internals.  It may even	 * fail if the cblock slush pool is empty.  We can't allocate more	 * cblocks here because we are called from an interrupt handler	 * and clist_alloc_cblocks() can wait.	 */	tq = tp->t_rawq;	bzero(&tp->t_rawq, sizeof tp->t_rawq);	tp->t_rawq.c_cbmax = tq.c_cbmax;	tp->t_rawq.c_cbreserved = tq.c_cbreserved;	while ((c = getc(&tq)) >= 0)		ttyinput(c, tp);	CLR(tp->t_state, TS_TYPEN);}/* * Process a read call on a tty device. */intttread(tp, uio, flag)	register struct tty *tp;	struct uio *uio;	int flag;{	register struct clist *qp;	register int c;	register tcflag_t lflag;	register cc_t *cc = tp->t_cc;	register struct proc *p = curproc;	int s, first, error = 0;	int has_stime = 0, last_cc = 0;	long slp = 0;		/* XXX this should be renamed `timo'. */loop:	s = spltty();	lflag = tp->t_lflag;	/*	 * take pending input first	 */	if (ISSET(lflag, PENDIN)) {		ttypend(tp);		splx(s);	/* reduce latency */		s = spltty();		lflag = tp->t_lflag;	/* XXX ttypend() clobbers it */	}#ifndef OSKIT	/*	 * Hang process if it's in the background.	 */	if (isbackground(p, tp)) {		splx(s);		if ((p->p_sigignore & sigmask(SIGTTIN)) ||		   (p->p_sigmask & sigmask(SIGTTIN)) ||		    p->p_flag & P_PPWAIT || p->p_pgrp->pg_jobc == 0)			return (EIO);		pgsignal(p->p_pgrp, SIGTTIN, 1);		error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg2", 0);		if (error)			return (error);		goto loop;	}#endif /* !OSKIT */	if (ISSET(tp->t_state, TS_ZOMBIE)) {		splx(s);		return (0);	/* EOF */	}	/*	 * If canonical, use the canonical queue,	 * else use the raw queue.	 *	 * (should get rid of clists...)	 */	qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq;	if (flag & IO_NDELAY) {		if (qp->c_cc > 0)			goto read;		if (!ISSET(lflag, ICANON) && cc[VMIN] == 0) {			splx(s);			return (0);		}		splx(s);		return (EWOULDBLOCK);	}	if (!ISSET(lflag, ICANON)) {		int m = cc[VMIN];		long t = cc[VTIME];		struct timeval stime, timecopy;		int x;		/*		 * Check each of the four combinations.		 * (m > 0 && t == 0) is the normal read case.		 * It should be fairly efficient, so we check that and its		 * companion case (m == 0 && t == 0) first.		 * For the other two cases, we compute the target sleep time		 * into slp.		 */		if (t == 0) {			if (qp->c_cc < m)				goto sleep;			if (qp->c_cc > 0)				goto read;			/* m, t and qp->c_cc are all 0.  0 is enough input. */			splx(s);			return (0);		}		t *= 100000;		/* time in us */#define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \			 ((t1).tv_usec - (t2).tv_usec))		if (m > 0) {			if (qp->c_cc <= 0)				goto sleep;			if (qp->c_cc >= m)				goto read;			x = splclock();			timecopy = time;			splx(x);			if (!has_stime) {				/* first character, start timer */				has_stime = 1;				stime = timecopy;				slp = t;			} else if (qp->c_cc > last_cc) {				/* got a character, restart timer */				stime = timecopy;				slp = t;			} else {				/* nothing, check expiration */				slp = t - diff(timecopy, stime);				if (slp <= 0)					goto read;			}			last_cc = qp->c_cc;		} else {	/* m == 0 */			if (qp->c_cc > 0)				goto read;			x = splclock();			timecopy = time;			splx(x);			if (!has_stime) {				has_stime = 1;				stime = timecopy;				slp = t;			} else {				slp = t - diff(timecopy, stime);				if (slp <= 0) {					/* Timed out, but 0 is enough input. */					splx(s);					return (0);				}			}		}#undef diff		/*		 * Rounding down may make us wake up just short		 * of the target, so we round up.		 * The formula is ceiling(slp * hz/1000000).		 * 32-bit arithmetic is enough for hz < 169.		 * XXX see hzto() for how to avoid overflow if hz		 * is large (divide by `tick' and/or arrange to		 * use hzto() if hz is large).		 */		slp = (long) (((u_long)slp * hz) + 999999) / 1000000;		goto sleep;	}	if (qp->c_cc <= 0) {sleep:		/*		 * There is no input, or not enough input and we can block.		 */		error = ttysleep(tp, TSA_HUP_OR_INPUT(tp), TTIPRI | PCATCH,				 ISSET(tp->t_state, TS_CONNECTED) ?				 "ttyin" : "ttyhup", (int)slp);		splx(s);		if (error == EWOULDBLOCK)			error = 0;		else if (error)			return (error);		/*		 * XXX what happens if another process eats some input		 * while we are asleep (not just here)?  It would be		 * safest to detect changes and reset our state variables		 * (has_stime and last_cc).		 */		slp = 0;		goto loop;	}read:	splx(s);	/*	 * Input present, check for input mapping and processing.	 */	first = 1;	if (ISSET(lflag, ICANON | ISIG))		goto slowcase;	for (;;) {		char ibuf[IBUFSIZ];		int icc;		icc = imin(uio->uio_resid, IBUFSIZ);		icc = q_to_b(qp, ibuf, icc);		if (icc <= 0) {			if (first)				goto loop;			break;		}		error = uiomove(ibuf, icc, uio);		/*		 * XXX if there was an error then we should ungetc() the		 * unmoved chars and reduce icc here.		 */#if NSNP > 0		if (ISSET(tp->t_lflag, ECHO) &&		    ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)			snpin((struct snoop *)tp->t_sc, ibuf, icc);#endif		if (error)			break; 		if (uio->uio_resid == 0)			break;		first = 0;	}	goto out;slowcase:	for (;;) {		c = getc(qp);		if (c < 0) {			if (first)				goto loop;			break;		}		/*		 * delayed suspend (^Y)		 */		if (CCEQ(cc[VDSUSP], c) && ISSET(lflag, ISIG)) {			pgsignal(tp->t_pgrp, SIGTSTP, 1);			if (first) {				error = ttysleep(tp, &lbolt, TTIPRI | PCATCH,						 "ttybg3", 0);				if (error)					break;				goto loop;			}			break;		}		/*		 * Interpret EOF only in canonical mode.		 */		if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON))			break;		/*		 * Give user character.		 */ 		error = ureadc(c, uio);		if (error)			/* XXX should ungetc(c, qp). */			break;#if NSNP > 0		/*		 * Only snoop directly on input in echo mode.  Non-echoed		 * input will be snooped later iff the application echoes it.		 */		if (ISSET(tp->t_lflag, ECHO) &&		    ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL)			snpinc((struct snoop *)tp->t_sc, (char)c);#endif 		if (uio->uio_resid == 0)			break;		/*		 * In canonical mode check for a "break character"		 * marking the end of a "line of input".		 */		if (ISSET(lflag, ICANON) && TTBREAKC(c))			break;		first = 0;	}out:	/*	 * Look to unblock input now that (presumably)	 * the input queue has gone down.	 */	s = spltty();	if (ISSET(tp->t_state, TS_TBLOCK) &&	    tp->t_rawq.c_cc + tp->t_canq.c_cc <= I_LOW_WATER)		ttyunblock(tp);	splx(s);	return (error);}#ifndef OSKIT/* * Check the output queue on tp for space for a kernel message (from uprintf * or tprintf).  Allow some space over the normal hiwater mark so we don't * lose messages due to normal flow control, but don't let the tty run amok. * Sleeps here are not interruptible, but we return prematurely if new signals * arrive. */intttycheckoutq(tp, wait)	register struct tty *tp;	int wait;{	int hiwat, s, oldsig;	hiwat = tp->t_hiwat;	s = spltty();	oldsig = wait ? curproc->p_siglist : 0;	if (tp->t_outq.c_cc > hiwat + OBUFSIZ + 100)		while (tp->t_outq.c_cc > hiwat) {			ttstart(tp);			if (tp->t_outq.c_cc <= hiwat)				break;			if (wait == 0 || curproc->p_siglist != oldsig) {				splx(s);				return (0);			}			SET(tp->t_state, TS_SO_OLOWAT);			tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz);		}	splx(s);	return (1);}#endif /* !OSKIT *//* * Process a write call on a tty device. */intttwrite(tp, uio, flag)	register struct tty *tp;	register struct uio *uio;	int flag;{	register char *cp = NULL;	register int cc, ce;	register struct proc *p;	int i, hiwat, cnt, error, s;	char obuf[OBUFSIZ];	hiwat = tp->t_hiwat;	cnt = uio->uio_resid;	error = 0;	cc = 0;loop:	s = spltty();	if (ISSET(tp->t_state, TS_ZOMBIE)) {		splx(s);		if (uio->uio_resid == cnt)			error = EIO;		goto out;	}	if (!ISSET(tp->t_state, TS_CONNECTED)) {		if (flag & IO_NDELAY) {			splx(s);			error = EWOULDBLOCK;			goto out;		}		error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,				 "ttydcd", 0);		splx(s);		if (error)			goto out;		goto loop;	}	splx(s);	/*	 * Hang the process if it's in the background.	 */	p = curproc;#ifndef OSKIT	if (isbackground(p, tp) &&	    ISSET(tp->t_lflag, TOSTOP) && (p->p_flag & P_PPWAIT) == 0 &&	    (p->p_sigignore & sigmask(SIGTTOU)) == 0 &&	    (p->p_sigmask & sigmask(SIGTTOU)) == 0 &&	     p->p_pgrp->pg_jobc) {		pgsignal(p->p_pgrp, SIGTTOU, 1);		error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ttybg4", 0);		if (error)			goto out;		goto loop;	}#endif /* !OSKIT */	/*	 * Process the user's data in at most OBUFSIZ chunks.  Perform any	 * output translation.  Keep track of high water mark, sleep on	 * overflow awaiting device aid in acquiring new space.	 */	while (uio->uio_resid > 0 || cc > 0) {		if (ISSET(tp->t_lflag, FLUSHO)) {			uio->uio_resid = 0;			return (0);		}		if (tp->t_outq.c_cc > hiwat)			goto ovhiwat;		/*		 * Grab a hunk of data from the user, unless we have some		 * leftover from last time.		 */		if (cc == 0) {			cc = imin(uio->uio_resid, OBUFSIZ);			cp = obuf;			error = uiomove(cp, cc, uio);			if (error) {				cc = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?