/*
- * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver.
+ * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous
+ * tty devices.
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Drew D. Perkins
* Carnegie Mellon University
* Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
* Cleaned up a lot of the mbuf-related code to fix bugs that
* caused system crashes and packet corruption. Changed pppstart
- * so that it doesn't just give up with a collision if the whole
+ * so that it doesn't just give up with a "collision" if the whole
* packet doesn't fit in the output ring buffer.
*
* Added priority queueing for interactive IP packets, following
* Robert Olsson <robert@robur.slu.se> and Paul Mackerras.
*/
-/* $Id: ppp_tty.c,v 1.2 1994/11/28 01:38:59 paulus Exp $ */
+/* $Id: ppp_tty.c,v 1.9 2002/12/06 09:49:16 paulus Exp $ */
/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
+/* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
#include "ppp.h"
#if NPPP > 0
int pppclose __P((struct tty *tp, int flag));
int pppread __P((struct tty *tp, struct uio *uio, int flag));
int pppwrite __P((struct tty *tp, struct uio *uio, int flag));
-int ppptioctl __P((struct tty *tp, int cmd, caddr_t data, int flag,
- struct proc *));
+int ppptioctl __P((struct tty *tp, int cmd, caddr_t data, int flag));
int pppinput __P((int c, struct tty *tp));
int pppstart __P((struct tty *tp));
static u_short pppfcs __P((u_short fcs, u_char *cp, int len));
static void pppasyncstart __P((struct ppp_softc *));
static void pppasyncctlp __P((struct ppp_softc *));
+static void pppasyncrelinq __P((struct ppp_softc *));
+static void ppp_timeout __P((void *));
static void pppgetm __P((struct ppp_softc *sc));
static void pppdumpb __P((u_char *b, int l));
static void ppplogchar __P((struct ppp_softc *, int));
#define CCOUNT(q) ((q)->c_cc)
#define t_sc T_LINEP
+#define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */
#define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */
/*
* Line specific open routine for async tty devices.
* Attach the given tty to the first available ppp unit.
+ * Called from device open routine or ttioctl.
*/
/* ARGSUSED */
int
register struct tty *tp;
{
register struct ppp_softc *sc;
- int error, s, i;
+ int error, s;
struct proc *p = u.u_procp;
if (!suser())
return EPERM;
+ s = spltty();
+
if (tp->t_line == PPPDISC) {
sc = (struct ppp_softc *) tp->t_sc;
- if (sc != NULL && sc->sc_devp == (void *) tp)
+ if (sc != NULL && sc->sc_devp == (void *) tp) {
+ splx(s);
return (0);
+ }
}
- if ((sc = pppalloc(p->p_pid)) == NULL)
+ if ((sc = pppalloc(p->p_pid)) == NULL) {
+ splx(s);
return ENXIO;
-
- if (sc->sc_outm != NULL) {
- m_freem(sc->sc_outm);
- sc->sc_outm = NULL;
}
-
- pppgetm(sc);
+
+ if (sc->sc_relinq)
+ (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */
sc->sc_ilen = 0;
+ sc->sc_m = NULL;
bzero(sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
sc->sc_asyncmap[0] = 0xffffffff;
sc->sc_asyncmap[3] = 0x60000000;
sc->sc_devp = (void *) tp;
sc->sc_start = pppasyncstart;
sc->sc_ctlp = pppasyncctlp;
+ sc->sc_relinq = pppasyncrelinq;
+ sc->sc_outm = NULL;
+ pppgetm(sc);
+ sc->sc_if.if_flags |= IFF_RUNNING;
tp->t_sc = (caddr_t) sc;
ttyflush(tp, FREAD | FWRITE);
+ splx(s);
return (0);
}
/*
- * Line specific close routine.
+ * Line specific close routine, called from device close routine
+ * and from ttioctl.
* Detach the tty from the ppp unit.
* Mimics part of ttyclose().
*/
int flag;
{
register struct ppp_softc *sc;
- struct mbuf *m;
int s;
- ttywflush(tp);
- s = splimp(); /* paranoid; splnet probably ok */
+ s = spltty();
+ ttyflush(tp, FREAD|FWRITE);
tp->t_line = 0;
- sc = (struct ppp_softc *)tp->t_sc;
+ sc = (struct ppp_softc *) tp->t_sc;
if (sc != NULL) {
tp->t_sc = NULL;
if (tp == (struct tty *) sc->sc_devp) {
- m_freem(sc->sc_outm);
- sc->sc_outm = NULL;
- m_freem(sc->sc_m);
- sc->sc_m = NULL;
+ pppasyncrelinq(sc);
pppdealloc(sc);
}
}
return 0;
}
+/*
+ * Relinquish the interface unit to another device.
+ */
+static void
+pppasyncrelinq(sc)
+ struct ppp_softc *sc;
+{
+ int s;
+
+ s = spltty();
+ if (sc->sc_outm) {
+ m_freem(sc->sc_outm);
+ sc->sc_outm = NULL;
+ }
+ if (sc->sc_m) {
+ m_freem(sc->sc_m);
+ sc->sc_m = NULL;
+ }
+ if (sc->sc_flags & SC_TIMEOUT) {
+ untimeout(ppp_timeout, (void *) sc);
+ sc->sc_flags &= ~SC_TIMEOUT;
+ }
+ splx(s);
+}
+
/*
* Line specific (tty) read routine.
*/
register int s;
int error = 0;
- if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
- return 0; /* end of file */
- if (sc == NULL || tp != (struct tty *) sc->sc_devp)
+ if (sc == NULL)
return 0;
- s = splimp();
- while (sc->sc_inq.ifq_head == NULL && tp->t_line == PPPDISC) {
+ /*
+ * Loop waiting for input, checking that nothing disasterous
+ * happens in the meantime.
+ */
+ s = spltty();
+ for (;;) {
+ if (tp != (struct tty *) sc->sc_devp || tp->t_line != PPPDISC) {
+ splx(s);
+ return 0;
+ }
+ if (sc->sc_inq.ifq_head != NULL)
+ break;
+ if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0
+ && (tp->t_state & TS_ISOPEN)) {
+ splx(s);
+ return 0; /* end of file */
+ }
if (tp->t_state & (TS_ASYNC | TS_NBIO)) {
splx(s);
return (EWOULDBLOCK);
}
sleep((caddr_t) &tp->t_rawq, TTIPRI);
}
- if (tp->t_line != PPPDISC) {
- splx(s);
- return (-1);
- }
/* Pull place-holder byte out of canonical queue */
getc(&tp->t_canq);
int cmd, flag;
{
struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
- int error;
+ int error, s;
if (sc == NULL || tp != (struct tty *) sc->sc_devp)
return -1;
case PPPIOCSXASYNCMAP:
if (!suser())
return EPERM;
+ s = spltty();
bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */
sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */
sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */
+ splx(s);
break;
case PPPIOCGXASYNCMAP:
}
/*
- * This gets called from pppoutput when a new packet is
- * put on a queue.
+ * This gets called at splnet from if_ppp.c at various times
+ * when there is data ready to be sent.
*/
static void
pppasyncstart(sc)
register struct ppp_softc *sc;
{
register struct tty *tp = (struct tty *) sc->sc_devp;
-
- pppstart(tp);
-}
-
-/*
- * This gets called when a received packet is placed on
- * the inq.
- */
-static void
-pppasyncctlp(sc)
- struct ppp_softc *sc;
-{
- struct tty *tp;
-
- /* Put a placeholder byte in canq for ttselect()/ttnread(). */
- tp = (struct tty *) sc->sc_devp;
- putc(0, &tp->t_canq);
- ttwakeup(tp);
-}
-
-/*
- * Start output on async tty interface. Get another datagram
- * to send from the interface queue and start sending it.
- */
-int
-pppstart(tp)
- register struct tty *tp;
-{
- register struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
register struct mbuf *m;
register int len;
register u_char *start, *stop, *cp;
- int n, s, ndone, done;
+ int n, ndone, done, idle;
struct mbuf *m2;
+ int s;
- if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0) {
- /* sorry, I can't talk now */
- return;
- }
- if (sc == NULL || tp != (struct tty *) sc->sc_devp) {
- (*tp->t_oproc)(tp);
- return;
- }
-
- for (;;) {
- /*
- * If there is more in the output queue, just send it now.
- * We are being called in lieu of ttstart and must do what
- * it would.
- */
- if (CCOUNT(&tp->t_outq) != 0 && tp->t_oproc != NULL) {
- (*tp->t_oproc)(tp);
- if (CCOUNT(&tp->t_outq) > PPP_HIWAT)
- return;
- }
-
+ idle = 0;
+ while (CCOUNT(&tp->t_outq) < PPP_HIWAT) {
/*
* See if we have an existing packet partly sent.
* If not, get a new packet and start sending it.
* Get another packet to be sent.
*/
m = ppp_dequeue(sc);
- if (m == NULL)
- return;
+ if (m == NULL) {
+ idle = 1;
+ break;
+ }
/*
* The extra PPP_FLAG will start up a new packet, and thus
* the line may have been idle for some time.
*/
if (CCOUNT(&tp->t_outq) == 0) {
- ++sc->sc_bytessent;
+ ++sc->sc_stats.ppp_obytes;
(void) putc(PPP_FLAG, &tp->t_outq);
}
ndone = n - b_to_q(start, n, &tp->t_outq);
len -= ndone;
start += ndone;
- sc->sc_bytessent += ndone;
+ sc->sc_stats.ppp_obytes += ndone;
if (ndone < n)
break; /* packet doesn't fit */
}
/*
* If there are characters left in the mbuf,
- * the first one must be special..
+ * the first one must be special.
* Put it out in a different form.
*/
if (len) {
+ s = spltty();
if (putc(PPP_ESCAPE, &tp->t_outq))
break;
if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
(void) unputc(&tp->t_outq);
+ splx(s);
break;
}
- sc->sc_bytessent += 2;
+ splx(s);
+ sc->sc_stats.ppp_obytes += 2;
start++;
len--;
}
}
+
/*
* If we didn't empty this mbuf, remember where we're up to.
* If we emptied the last mbuf, try to add the FCS and closing
* Try to output the FCS and flag. If the bytes
* don't all fit, back out.
*/
+ s = spltty();
for (q = endseq; q < p; ++q)
if (putc(*q, &tp->t_outq)) {
done = 0;
unputc(&tp->t_outq);
break;
}
+ splx(s);
+ if (done)
+ sc->sc_stats.ppp_obytes += q - endseq;
}
if (!done) {
m->m_off += m->m_len - len;
m->m_len = len;
- sc->sc_outm = m;
- if (tp->t_oproc != NULL)
- (*tp->t_oproc)(tp);
- return; /* can't do any more at the moment */
+ break;
}
/* Finished with this mbuf; free it and move on. */
MFREE(m, m2);
- if (m2 == NULL)
- break;
-
m = m2;
+ if (m == NULL) {
+ /* Finished a packet */
+ break;
+ }
sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len);
}
- /* Finished a packet */
- sc->sc_outm = NULL;
- sc->sc_bytessent++; /* account for closing flag */
- sc->sc_if.if_opackets++;
+ /*
+ * If m == NULL, we have finished a packet.
+ * If m != NULL, we've either done as much work this time
+ * as we need to, or else we've filled up the output queue.
+ */
+ sc->sc_outm = m;
+ if (m)
+ break;
}
+
+ /* Call pppstart to start output again if necessary. */
+ s = spltty();
+ pppstart(tp);
+
+ /*
+ * This timeout is needed for operation on a pseudo-tty,
+ * because the pty code doesn't call pppstart after it has
+ * drained the t_outq.
+ */
+ if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
+ timeout(ppp_timeout, (void *) sc, 1);
+ sc->sc_flags |= SC_TIMEOUT;
+ }
+
+ splx(s);
+}
+
+/*
+ * This gets called when a received packet is placed on
+ * the inq, at splnet.
+ */
+static void
+pppasyncctlp(sc)
+ struct ppp_softc *sc;
+{
+ struct tty *tp;
+ int s;
+
+ /* Put a placeholder byte in canq for ttselect()/ttnread(). */
+ s = spltty();
+ tp = (struct tty *) sc->sc_devp;
+ putc(0, &tp->t_canq);
+ ttwakeup(tp);
+ splx(s);
+}
+
+/*
+ * Start output on async tty interface. If the transmit queue
+ * has drained sufficiently, arrange for pppasyncstart to be
+ * called later at splnet.
+ * Called at spltty or higher.
+ */
+int
+pppstart(tp)
+ register struct tty *tp;
+{
+ register struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
+
+ /*
+ * If there is stuff in the output queue, send it now.
+ * We are being called in lieu of ttstart and must do what it would.
+ */
+ if (tp->t_oproc != NULL)
+ (*tp->t_oproc)(tp);
+
+ /*
+ * If the transmit queue has drained and the tty has not hung up
+ * or been disconnected from the ppp unit, then tell if_ppp.c that
+ * we need more output.
+ */
+ if (CCOUNT(&tp->t_outq) < PPP_LOWAT
+ && !((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
+ && sc != NULL && tp == (struct tty *) sc->sc_devp) {
+ ppp_restart(sc);
+ }
+
+ return 0;
+}
+
+/*
+ * Timeout routine - try to start some more output.
+ */
+static void
+ppp_timeout(x)
+ void *x;
+{
+ struct ppp_softc *sc = (struct ppp_softc *) x;
+ struct tty *tp = (struct tty *) sc->sc_devp;
+ int s;
+
+ s = spltty();
+ sc->sc_flags &= ~SC_TIMEOUT;
+ pppstart(tp);
+ splx(s);
}
/*
{
struct mbuf *m, **mp, *p;
int len;
- int s;
- s = splimp();
mp = &sc->sc_m;
for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){
if ((m = *mp) == NULL) {
len -= M_DATASIZE(m);
mp = &m->m_next;
}
- splx(s);
}
/*
{
register struct ppp_softc *sc;
struct mbuf *m;
- int ilen;
+ int ilen, s;
+ extern int tk_nin;
- tk_nin++;
sc = (struct ppp_softc *) tp->t_sc;
if (sc == NULL || tp != (struct tty *) sc->sc_devp)
- return;
+ return 0;
- sc->sc_bytesrcvd++;
+ ++tk_nin;
+ ++sc->sc_stats.ppp_ibytes;
c &= 0xff;
+ /*
+ * Handle software flow control of output.
+ */
+ if (tp->t_iflag & IXON) {
+ if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != 0) {
+ if ((tp->t_state & TS_TTSTOP) == 0) {
+ tp->t_state |= TS_TTSTOP;
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
+ }
+ return 0;
+ }
+ if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != 0) {
+ tp->t_state &= ~TS_TTSTOP;
+ if (tp->t_oproc != NULL)
+ (*tp->t_oproc)(tp);
+ return 0;
+ }
+ }
+
+ s = spltty();
if (c & 0x80)
sc->sc_flags |= SC_RCV_B7_1;
else
sc->sc_flags |= SC_RCV_ODDP;
else
sc->sc_flags |= SC_RCV_EVNP;
+ splx(s);
if (sc->sc_flags & SC_LOG_RAWIN)
ppplogchar(sc, c);
* abort sequence "}~".
*/
if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
- || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) {
+ || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) {
+ s = spltty();
sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */
if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
if (sc->sc_flags & SC_DEBUG)
- printf("ppp%d: bad fcs %x\n", sc->sc_if.if_unit,
- sc->sc_fcs);
+ printf("ppp%d: bad fcs %x, pkt len %d\n",
+ sc->sc_if.if_unit, sc->sc_fcs, ilen);
sc->sc_if.if_ierrors++;
+ sc->sc_stats.ppp_ierrors++;
} else
sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
- return;
+ splx(s);
+ return 0;
}
if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
if (ilen) {
if (sc->sc_flags & SC_DEBUG)
printf("ppp%d: too short (%d)\n", sc->sc_if.if_unit, ilen);
+ s = spltty();
sc->sc_if.if_ierrors++;
+ sc->sc_stats.ppp_ierrors++;
sc->sc_flags |= SC_PKTLOST;
+ splx(s);
}
- return;
+ return 0;
}
/*
sc->sc_mc->m_next = NULL;
ppppktin(sc, m, sc->sc_flags & SC_PKTLOST);
- sc->sc_flags &= ~SC_PKTLOST;
+ if (sc->sc_flags & SC_PKTLOST) {
+ s = spltty();
+ sc->sc_flags &= ~SC_PKTLOST;
+ splx(s);
+ }
pppgetm(sc);
- return;
+ return 0;
}
if (sc->sc_flags & SC_FLUSH) {
if (sc->sc_flags & SC_LOG_FLUSH)
ppplogchar(sc, c);
- return;
+ return 0;
}
if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
- return;
+ return 0;
+ s = spltty();
if (sc->sc_flags & SC_ESCAPED) {
sc->sc_flags &= ~SC_ESCAPED;
c ^= PPP_TRANS;
} else if (c == PPP_ESCAPE) {
sc->sc_flags |= SC_ESCAPED;
- return;
+ splx(s);
+ return 0;
}
+ splx(s);
/*
* Initialize buffer on first octet received.
++m->m_len;
*sc->sc_mp++ = c;
sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
- return;
+ return 0;
flush:
if (!(sc->sc_flags & SC_FLUSH)) {
+ s = spltty();
sc->sc_if.if_ierrors++;
+ sc->sc_stats.ppp_ierrors++;
sc->sc_flags |= SC_FLUSH;
+ splx(s);
if (sc->sc_flags & SC_LOG_FLUSH)
ppplogchar(sc, c);
}
+ return 0;
}
#define MAX_DUMP_BYTES 128
if (c >= 0)
sc->sc_rawin[sc->sc_rawin_count++] = c;
if (sc->sc_rawin_count >= sizeof(sc->sc_rawin)
- || c < 0 && sc->sc_rawin_count > 0) {
+ || (c < 0 && sc->sc_rawin_count > 0)) {
printf("ppp%d input: ", sc->sc_if.if_unit);
pppdumpb(sc->sc_rawin, sc->sc_rawin_count);
sc->sc_rawin_count = 0;