Ejemplo n.º 1
0
int
clmpccclose(dev_t dev, int flag, int mode, struct lwp *l)
{
	struct clmpcc_softc	*sc =
		device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
	struct clmpcc_chan	*ch = &sc->sc_chans[CLMPCCCHAN(dev)];
	struct tty		*tp = ch->ch_tty;
	int s;

	if ( ISCLR(tp->t_state, TS_ISOPEN) )
		return 0;

	(*tp->t_linesw->l_close)(tp, flag);

	s = spltty();

	if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {
		/*
		 * Although we got a last close, the device may still be in
		 * use; e.g. if this was the dialout node, and there are still
		 * processes waiting for carrier on the non-dialout node.
		 */
		clmpcc_shutdown(ch);
	}

	ttyclose(tp);

	splx(s);

	return 0;
}
Ejemplo n.º 2
0
static void
clmpcc_start(struct tty *tp)
{
	struct clmpcc_softc *sc =
	    device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
	struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
	u_int oldch;
	int s;

	s = spltty();

	if ( ISCLR(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY) ) {
		ttypull(tp);
		if ( ISSET(ch->ch_flags, CLMPCC_FLG_START_BREAK |
					 CLMPCC_FLG_END_BREAK) ||
		     tp->t_outq.c_cc > 0 ) {

			if ( ISCLR(ch->ch_flags, CLMPCC_FLG_START_BREAK |
						 CLMPCC_FLG_END_BREAK) ) {
				ch->ch_obuf_addr = tp->t_outq.c_cf;
				ch->ch_obuf_size = ndqb(&tp->t_outq, 0);
			}

			/* Enable TX empty interrupts */
			oldch = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
			clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
				clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) |
					     CLMPCC_IER_TX_EMPTY);
			clmpcc_select_channel(ch->ch_sc, oldch);
			SET(tp->t_state, TS_BUSY);
		}
	}

	splx(s);
}
Ejemplo n.º 3
0
int ttecho(int c,TTY tp)
{
	USHORT lflag = tp->t_lflag;
	CQ qp = &tp->outq;

	if ((ISCLR(lflag, ECHO) && (ISCLR(lflag, ECHONL) || c==LF)) ||
	    ISSET(lflag, EXTPROC))
		return 0;

	return ttoutput(c, tp);
}
Ejemplo n.º 4
0
/*
 * Stop output on a line.
 */
void
clmpccstop(struct tty *tp, int flag)
{
	struct clmpcc_softc *sc =
	    device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
	struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
	int s;

	s = splserial();

	if ( ISSET(tp->t_state, TS_BUSY) ) {
		if ( ISCLR(tp->t_state, TS_TTSTOP) )
			SET(tp->t_state, TS_FLUSH);
		ch->ch_obuf_size = 0;
	}
	splx(s);
}
Ejemplo n.º 5
0
/*
 * Perform OPOST processing, queue character into outq,
 * return -1 if outq full
 */
int ttoutput(int c, TTY tp)
{
	CQ qp = &tp->outq;
	USHORT oflag = tp->t_oflag;

	if (ISSET(oflag, OPOST))
	{
		/* OPOST: do tab expansion */
		if (c==TAB && ISSET(oflag,OXTABS) && ISCLR(tp->t_lflag,EXTPROC))
		{
			for (c = 8-(tp->ocol & 7); c>0; c--)
				if (putc(' ',qp) < 0)
					break;
			tp->ocol += 8-(tp->ocol & 7)-c;
			return c ? -1 : TAB;
		}
		
		if (CCEQ(tp->t_cc[VEOF],c) && ISSET(oflag,ONOEOT))
			return c;
			
		if (c == LF && ISSET(oflag,ONLCR))
		{
			tp->ocol = 0;
			if (putc(CR,qp) < 0) return -1;
			return putc(LF,qp) < 0 ? -1 : c;
		}
		
		if (c == CR && ISSET(oflag,OCRNL))
		{
			tp->ocol = 0;
			return putc(LF,qp) < 0 ? -1 : c;
		}
		
		if (c >= 'a' && c <= 'z' && ISSET(oflag,OLCUC))
			c -= ('a'-'A');
	}

	if (putc(c,qp) < 0)
		return -1;
	tp->ocol++;
	return c;
}
Ejemplo n.º 6
0
static void
clmpcc_shutdown(struct clmpcc_chan *ch)
{
	int oldch;

	oldch = clmpcc_select_channel(ch->ch_sc, ch->ch_car);

	/* Turn off interrupts. */
	clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER, 0);

	if ( ISCLR(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
		/* Disable the transmitter and receiver */
		clmpcc_channel_cmd(ch->ch_sc, ch->ch_car, CLMPCC_CCR_T0_RX_DIS |
							  CLMPCC_CCR_T0_TX_DIS);

		/* Drop RTS and DTR */
		clmpcc_modem_control(ch, TIOCM_RTS | TIOCM_DTR, DMBIS);
	}

	clmpcc_select_channel(ch->ch_sc, oldch);
}
Ejemplo n.º 7
0
static int
clmpcc_param(struct tty *tp, struct termios *t)
{
	struct clmpcc_softc *sc =
	    device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
	struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
	u_char cor;
	u_char oldch;
	int oclk = 0, obpr = 0;
	int iclk = 0, ibpr = 0;
	int s;

	/* Check requested parameters. */
	if ( t->c_ospeed && clmpcc_speed(sc, t->c_ospeed, &oclk, &obpr) < 0 )
		return EINVAL;

	if ( t->c_ispeed && clmpcc_speed(sc, t->c_ispeed, &iclk, &ibpr) < 0 )
		return EINVAL;

	/*
	 * For the console, always force CLOCAL and !HUPCL, so that the port
	 * is always active.
	 */
	if ( ISSET(ch->ch_openflags, TIOCFLAG_SOFTCAR) ||
	     ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
		SET(t->c_cflag, CLOCAL);
		CLR(t->c_cflag, HUPCL);
	}

	CLR(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);

	/* If ospeed it zero, hangup the line */
	clmpcc_modem_control(ch, TIOCM_DTR, t->c_ospeed == 0 ? DMBIC : DMBIS);

	if ( t->c_ospeed ) {
		ch->ch_tcor = CLMPCC_TCOR_CLK(oclk);
		ch->ch_tbpr = obpr;
	} else {
		ch->ch_tcor = 0;
		ch->ch_tbpr = 0;
	}

	if ( t->c_ispeed ) {
		ch->ch_rcor = CLMPCC_RCOR_CLK(iclk);
		ch->ch_rbpr = ibpr;
	} else {
		ch->ch_rcor = 0;
		ch->ch_rbpr = 0;
	}

	/* Work out value to use for COR1 */
	cor = 0;
	if ( ISSET(t->c_cflag, PARENB) ) {
		cor |= CLMPCC_COR1_NORM_PARITY;
		if ( ISSET(t->c_cflag, PARODD) )
			cor |= CLMPCC_COR1_ODD_PARITY;
	}

	if ( ISCLR(t->c_cflag, INPCK) )
		cor |= CLMPCC_COR1_IGNORE_PAR;

	switch ( t->c_cflag & CSIZE ) {
	  case CS5:
		cor |= CLMPCC_COR1_CHAR_5BITS;
		break;

	  case CS6:
		cor |= CLMPCC_COR1_CHAR_6BITS;
		break;

	  case CS7:
		cor |= CLMPCC_COR1_CHAR_7BITS;
		break;

	  case CS8:
		cor |= CLMPCC_COR1_CHAR_8BITS;
		break;
	}

	ch->ch_cor1 = cor;

	/*
	 * The only interesting bit in COR2 is 'CTS Automatic Enable'
	 * when hardware flow control is in effect.
	 */
	ch->ch_cor2 = ISSET(t->c_cflag, CRTSCTS) ? CLMPCC_COR2_CtsAE : 0;

	/* COR3 needs to be set to the number of stop bits... */
	ch->ch_cor3 = ISSET(t->c_cflag, CSTOPB) ? CLMPCC_COR3_STOP_2 :
						  CLMPCC_COR3_STOP_1;

	/*
	 * COR4 contains the FIFO threshold setting.
	 * We adjust the threshold depending on the input speed...
	 */
	if ( t->c_ispeed <= 1200 )
		ch->ch_cor4 = CLMPCC_COR4_FIFO_LOW;
	else if ( t->c_ispeed <= 19200 )
		ch->ch_cor4 = CLMPCC_COR4_FIFO_MED;
	else
		ch->ch_cor4 = CLMPCC_COR4_FIFO_HIGH;

	/*
	 * If chip is used with CTS and DTR swapped, we can enable
	 * automatic hardware flow control.
	 */
	if ( sc->sc_swaprtsdtr && ISSET(t->c_cflag, CRTSCTS) )
		ch->ch_cor5 = CLMPCC_COR5_FLOW_NORM;
	else
		ch->ch_cor5 = 0;

	s = splserial();
	oldch = clmpcc_select_channel(sc, ch->ch_car);

	/*
	 * COR2 needs to be set immediately otherwise we might never get
	 * a Tx EMPTY interrupt to change the other parameters.
	 */
	if ( clmpcc_rdreg(sc, CLMPCC_REG_COR2) != ch->ch_cor2 )
		clmpcc_wrreg(sc, CLMPCC_REG_COR2, ch->ch_cor2);

	if ( ISCLR(ch->ch_tty->t_state, TS_BUSY) )
		clmpcc_set_params(ch);
	else
		SET(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);

	clmpcc_select_channel(sc, oldch);

	splx(s);

	return 0;
}
Ejemplo n.º 8
0
int
clmpcc_modem_control(struct clmpcc_chan *ch, int bits, int howto)
{
	struct clmpcc_softc *sc = ch->ch_sc;
	struct tty *tp = ch->ch_tty;
	int oldch;
	int msvr;
	int rbits = 0;

	oldch = clmpcc_select_channel(sc, ch->ch_car);

	switch ( howto ) {
	case DMGET:
		msvr = clmpcc_rd_msvr(sc);

		if ( sc->sc_swaprtsdtr ) {
			rbits |= (msvr & CLMPCC_MSVR_RTS) ? TIOCM_DTR : 0;
			rbits |= (msvr & CLMPCC_MSVR_DTR) ? TIOCM_RTS : 0;
		} else {
			rbits |= (msvr & CLMPCC_MSVR_RTS) ? TIOCM_RTS : 0;
			rbits |= (msvr & CLMPCC_MSVR_DTR) ? TIOCM_DTR : 0;
		}

		rbits |= (msvr & CLMPCC_MSVR_CTS) ? TIOCM_CTS : 0;
		rbits |= (msvr & CLMPCC_MSVR_CD)  ? TIOCM_CD  : 0;
		rbits |= (msvr & CLMPCC_MSVR_DSR) ? TIOCM_DSR : 0;
		break;

	case DMSET:
		if ( sc->sc_swaprtsdtr ) {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR,
					bits & TIOCM_RTS ? CLMPCC_MSVR_DTR : 0);
		    clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS,
				bits & TIOCM_DTR ? CLMPCC_MSVR_RTS : 0);
		} else {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS,
					bits & TIOCM_RTS ? CLMPCC_MSVR_RTS : 0);
		    clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR,
				bits & TIOCM_DTR ? CLMPCC_MSVR_DTR : 0);
		}
		break;

	case DMBIS:
		if ( sc->sc_swaprtsdtr ) {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) && ISSET(bits, TIOCM_RTS) )
			clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_DTR, CLMPCC_MSVR_DTR);
		    if ( ISSET(bits, TIOCM_DTR) )
			clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_RTS, CLMPCC_MSVR_RTS);
		} else {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) && ISSET(bits, TIOCM_RTS) )
			clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_RTS, CLMPCC_MSVR_RTS);
		    if ( ISSET(bits, TIOCM_DTR) )
			clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_DTR, CLMPCC_MSVR_DTR);
		}
		break;

	case DMBIC:
		if ( sc->sc_swaprtsdtr ) {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) && ISCLR(bits, TIOCM_RTS) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR, 0);
		    if ( ISCLR(bits, TIOCM_DTR) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS, 0);
		} else {
		    if ( ISCLR(tp->t_cflag, CRTSCTS) && ISCLR(bits, TIOCM_RTS) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS, 0);
		    if ( ISCLR(bits, TIOCM_DTR) )
			clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR, 0);
		}
		break;
	}

	clmpcc_select_channel(sc, oldch);

	return rbits;
}
Ejemplo n.º 9
0
int
clmpccopen(dev_t dev, int flag, int mode, struct lwp *l)
{
	struct clmpcc_softc *sc;
	struct clmpcc_chan *ch;
	struct tty *tp;
	int oldch;
	int error;

	sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
	if (sc == NULL)
		return (ENXIO);

	ch = &sc->sc_chans[CLMPCCCHAN(dev)];

	tp = ch->ch_tty;

	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
		return EBUSY;

	/*
	 * Do the following iff this is a first open.
	 */
	if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {

		ttychars(tp);

		tp->t_dev = dev;
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_lflag = TTYDEF_LFLAG;
		tp->t_cflag = TTYDEF_CFLAG;
		tp->t_ospeed = tp->t_ispeed = TTYDEF_SPEED;

		if ( ISSET(ch->ch_openflags, TIOCFLAG_CLOCAL) )
			SET(tp->t_cflag, CLOCAL);
		if ( ISSET(ch->ch_openflags, TIOCFLAG_CRTSCTS) )
			SET(tp->t_cflag, CRTSCTS);
		if ( ISSET(ch->ch_openflags, TIOCFLAG_MDMBUF) )
			SET(tp->t_cflag, MDMBUF);

		/*
		 * Override some settings if the channel is being
		 * used as the console.
		 */
		if ( ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
			tp->t_ospeed = tp->t_ispeed = cons_rate;
			SET(tp->t_cflag, CLOCAL);
			CLR(tp->t_cflag, CRTSCTS);
			CLR(tp->t_cflag, HUPCL);
		}

		ch->ch_control = 0;

		clmpcc_param(tp, &tp->t_termios);
		ttsetwater(tp);

		/* Clear the input ring */
		ch->ch_ibuf_rd = ch->ch_ibuf_wr = ch->ch_ibuf;

		/* Select the channel */
		oldch = clmpcc_select_channel(sc, ch->ch_car);

		/* Reset it */
		clmpcc_channel_cmd(sc, ch->ch_car, CLMPCC_CCR_T0_CLEAR |
						   CLMPCC_CCR_T0_RX_EN |
						   CLMPCC_CCR_T0_TX_EN);

		/* Enable receiver and modem change interrupts. */
		clmpcc_wrreg(sc, CLMPCC_REG_IER, CLMPCC_IER_MODEM |
						 CLMPCC_IER_RET |
						 CLMPCC_IER_RX_FIFO);

		/* Raise RTS and DTR */
		clmpcc_modem_control(ch, TIOCM_RTS | TIOCM_DTR, DMBIS);

		clmpcc_select_channel(sc, oldch);
	}

	error = ttyopen(tp, CLMPCCDIALOUT(dev), ISSET(flag, O_NONBLOCK));
	if (error)
		goto bad;

	error = (*tp->t_linesw->l_open)(dev, tp);
	if (error)
		goto bad;

	return 0;

bad:
	if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {
		/*
		 * We failed to open the device, and nobody else had it opened.
		 * Clean up the state as appropriate.
		 */
		clmpcc_shutdown(ch);
	}

	return error;
}
Ejemplo n.º 10
0
/*
 * RX interrupt routine
 */
int
clmpcc_rxintr(void *arg)
{
	struct clmpcc_softc *sc = (struct clmpcc_softc *)arg;
	struct clmpcc_chan *ch;
	u_int8_t *put, *end, rxd;
	u_char errstat;
	u_char fc, tc;
	u_char risr;
	u_char rir;
#ifdef DDB
	int saw_break = 0;
#endif

	/* Receive interrupt active? */
	rir = clmpcc_rdreg(sc, CLMPCC_REG_RIR);

	/*
	 * If we're using auto-vectored interrupts, we have to
	 * verify if the chip is generating the interrupt.
	 */
	if ( sc->sc_vector_base == 0 && (rir & CLMPCC_RIR_RACT) == 0 )
		return 0;

	/* Get pointer to interrupting channel's data structure */
	ch = &sc->sc_chans[rir & CLMPCC_RIR_RCN_MASK];

	/* Get the interrupt status register */
	risr = clmpcc_rdreg(sc, CLMPCC_REG_RISRl);
	if ( risr & CLMPCC_RISR_TIMEOUT ) {
		u_char reg;
		/*
		 * Set the FIFO threshold to zero, and disable
		 * further receive timeout interrupts.
		 */
		reg = clmpcc_rdreg(sc, CLMPCC_REG_COR4);
		clmpcc_wrreg(sc, CLMPCC_REG_COR4, reg & ~CLMPCC_COR4_FIFO_MASK);
		reg = clmpcc_rdreg(sc, CLMPCC_REG_IER);
		clmpcc_wrreg(sc, CLMPCC_REG_IER, reg & ~CLMPCC_IER_RET);
		clmpcc_wrreg(sc, CLMPCC_REG_REOIR, CLMPCC_REOIR_NO_TRANS);
		SET(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR);
		return 1;
	}

	/* How many bytes are waiting in the FIFO?  */
	fc = tc = clmpcc_rdreg(sc, CLMPCC_REG_RFOC) & CLMPCC_RFOC_MASK;

#ifdef DDB
	/*
	 * Allow BREAK on the console to drop to the debugger.
	 */
	if ( ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) &&
	     risr & CLMPCC_RISR_BREAK ) {
		saw_break = 1;
	}
#endif

	if ( ISCLR(ch->ch_tty->t_state, TS_ISOPEN) && fc ) {
		/* Just get rid of the data */
		while ( fc-- )
			(void) clmpcc_rd_rxdata(sc);
		goto rx_done;
	}

	put = ch->ch_ibuf_wr;
	end = ch->ch_ibuf_end;

	/*
	 * Note: The chip is completely hosed WRT these error
	 *       conditions; there seems to be no way to associate
	 *       the error with the correct character in the FIFO.
	 *       We compromise by tagging the first character we read
	 *       with the error. Not perfect, but there's no other way.
	 */
	errstat = 0;
	if ( risr & CLMPCC_RISR_PARITY )
		errstat |= TTY_PE;
	if ( risr & (CLMPCC_RISR_FRAMING | CLMPCC_RISR_BREAK) )
		errstat |= TTY_FE;

	/*
	 * As long as there are characters in the FIFO, and we
	 * have space for them...
	 */
	while ( fc > 0 ) {

		*put++ = rxd = clmpcc_rd_rxdata(sc);
		*put++ = errstat;

		if ( put >= end )
			put = ch->ch_ibuf;

		if ( put == ch->ch_ibuf_rd ) {
			put -= 2;
			if ( put < ch->ch_ibuf )
				put = end - 2;
		}

		errstat = 0;
		fc--;
	}

	ch->ch_ibuf_wr = put;

#if 0
	if ( sc->sc_swaprtsdtr == 0 &&
	     ISSET(cy->cy_tty->t_cflag, CRTSCTS) && cc < ch->ch_r_hiwat) {
		/*
		 * If RTS/DTR are not physically swapped, we have to
		 * do hardware flow control manually
		 */
		clmpcc_wr_msvr(sc, CLMPCC_MSVR_RTS, 0);
	}
#endif

rx_done:
	if ( fc != tc ) {
		if ( ISSET(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR) ) {
			u_char reg;
			/*
			 * Set the FIFO threshold to the preset value,
			 * and enable receive timeout interrupts.
			 */
			reg = clmpcc_rdreg(sc, CLMPCC_REG_COR4);
			reg = (reg & ~CLMPCC_COR4_FIFO_MASK) | ch->ch_cor4;
			clmpcc_wrreg(sc, CLMPCC_REG_COR4, reg);
			reg = clmpcc_rdreg(sc, CLMPCC_REG_IER);
			clmpcc_wrreg(sc, CLMPCC_REG_IER, reg | CLMPCC_IER_RET);
			CLR(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR);
		}

		clmpcc_wrreg(sc, CLMPCC_REG_REOIR, 0);
		softint_schedule(sc->sc_softintr_cookie);
	} else
		clmpcc_wrreg(sc, CLMPCC_REG_REOIR, CLMPCC_REOIR_NO_TRANS);

#ifdef DDB
	/*
	 * Only =after= we write REOIR is it safe to drop to the debugger.
	 */
	if ( saw_break )
		Debugger();
#endif

	return 1;
}
Ejemplo n.º 11
0
/*
 * Process an input character, with input processing
 * return -1, if queue full.
 */
int ttinput(int c, TTY tp)
{
	USHORT lflag = tp->t_lflag;
	char far *cc = tp->t_cc;
	CQ rq = &tp->inq;
	int nocan = ISCLR(lflag,ICANON);
	int ret = 0;

	/* only 8 or 7 bits */
	c &= (tp->t_iflag & ISTRIP ? 0x7f : 0xff);

	/* filter out CR LF sequences, store as LF only */
	if (c==0x0d)
	{
	  if (ISSET(tp->t_iflag, IGNCR))       // Ignore CR on input
	  {
		  BSET(tp->state,ST_CRSEEN);
		  return 0;
		}
		else if (ISSET(tp->t_iflag, ICRNL))  // Translate CR -> NR on input
		  c = 0x0a;
	}
	else if (c != 0x0a && ISSET(tp->state,ST_CRSEEN))
	{
		/* out of band CR, ok */
		BCLR(tp->state,ST_CRSEEN);
		if (putc(0x0d,rq) < 0)
		  return -1;
		if (nocan)
		  ret = ttecho(0x0d, tp);
	}
	else if ( c == 0x0a && ISSET(tp->t_iflag, INLCR))
	{
	  BCLR(tp->state, ST_CRSEEN);
	  c = 0x0d;
	}
	else if (ISCLR(lflag,EXTPROC))
	{
		BCLR(tp->state,ST_CRSEEN);

		/* internal processing */
		if (ISSET(lflag,ISIG))
		{
			if (CCEQ(cc[VINTR],c) /*|| CCEQ(cc[VQUIT],c)*/)
			{
				if (ISCLR(lflag,NOFLSH))
					ttflush(tp,3);
				ttecho(c,tp);
				pgsignal(tp->pgrp, SIGINT);
				goto endproc;
			}
		}
	}
	else
	  /* else was an LF or no CRSEEN */
		BCLR(tp->state,ST_CRSEEN);

	if (ret >= 0)
	{
		/* regular char or CR/LF: put in queue */
		if (putc(c,rq) >= 0)
		{
			if (nocan)
			  ret = ttecho(c,tp);
		}
	}
endproc:
	/* reaches here if something has been processed */
	ttstart(tp);
	return ret;
}