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; }
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); }
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); }
/* * 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); }
/* * 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; }
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); }
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; }
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; }
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; }
/* * 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; }
/* * 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; }